diff --git a/.electron-vue/sass-loader-config.js b/.electron-vue/sass-loader-config.js new file mode 100644 index 0000000..796ea21 --- /dev/null +++ b/.electron-vue/sass-loader-config.js @@ -0,0 +1,40 @@ +'use strict' + +const silenceLegacySassApi = { + sassOptions: { + silenceDeprecations: ['legacy-js-api'] + } +} + +module.exports = { + scss: { + loader: 'sass-loader', + options: silenceLegacySassApi + }, + sass: { + loader: 'sass-loader', + options: { + indentedSyntax: true, + ...silenceLegacySassApi + } + }, + vueScssLoaders: [ + 'vue-style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: silenceLegacySassApi + } + ], + vueSassLoaders: [ + 'vue-style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: { + indentedSyntax: true, + ...silenceLegacySassApi + } + } + ] +} diff --git a/.electron-vue/webpack.renderer.config.js b/.electron-vue/webpack.renderer.config.js index e4b605b..4613752 100644 --- a/.electron-vue/webpack.renderer.config.js +++ b/.electron-vue/webpack.renderer.config.js @@ -12,6 +12,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { VueLoaderPlugin } = require('vue-loader') +const sassLoaders = require('./sass-loader-config') /** * List of node_modules to include in webpack bundle @@ -45,11 +46,11 @@ let rendererConfig = { }, { test: /\.scss$/, - use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] + use: ['vue-style-loader', 'css-loader', 'postcss-loader', sassLoaders.scss] }, { test: /\.sass$/, - use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader?indentedSyntax'] + use: ['vue-style-loader', 'css-loader', 'postcss-loader', sassLoaders.sass] }, { test: /\.less$/, @@ -79,8 +80,8 @@ let rendererConfig = { options: { extractCSS: process.env.NODE_ENV === 'production', loaders: { - sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', - scss: 'vue-style-loader!css-loader!sass-loader', + sass: sassLoaders.vueSassLoaders, + scss: sassLoaders.vueScssLoaders, less: 'vue-style-loader!css-loader!less-loader' } } diff --git a/.electron-vue/webpack.web.config.js b/.electron-vue/webpack.web.config.js index dc741be..95ffb55 100644 --- a/.electron-vue/webpack.web.config.js +++ b/.electron-vue/webpack.web.config.js @@ -12,6 +12,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const { VueLoaderPlugin } = require('vue-loader') +const sassLoaders = require('./sass-loader-config') let webConfig = { devtool: '#cheap-module-eval-source-map', @@ -33,11 +34,11 @@ let webConfig = { }, { test: /\.scss$/, - use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] + use: ['vue-style-loader', 'css-loader', 'postcss-loader', sassLoaders.scss] }, { test: /\.sass$/, - use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader?indentedSyntax'] + use: ['vue-style-loader', 'css-loader', 'postcss-loader', sassLoaders.sass] }, { test: /\.less$/, @@ -64,8 +65,8 @@ let webConfig = { options: { extractCSS: true, loaders: { - sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', - scss: 'vue-style-loader!css-loader!sass-loader', + sass: sassLoaders.vueSassLoaders, + scss: sassLoaders.vueScssLoaders, less: 'vue-style-loader!css-loader!less-loader' } } diff --git a/build/icons/128x128.png b/build/icons/128x128.png new file mode 100644 index 0000000..26426d7 Binary files /dev/null and b/build/icons/128x128.png differ diff --git a/build/icons/16x16.png b/build/icons/16x16.png new file mode 100644 index 0000000..d59f6b5 Binary files /dev/null and b/build/icons/16x16.png differ diff --git a/build/icons/256x256.png b/build/icons/256x256.png index 3d67e9d..69a138c 100644 Binary files a/build/icons/256x256.png and b/build/icons/256x256.png differ diff --git a/build/icons/32x32.png b/build/icons/32x32.png new file mode 100644 index 0000000..ad46cbc Binary files /dev/null and b/build/icons/32x32.png differ diff --git a/build/icons/48x48.png b/build/icons/48x48.png new file mode 100644 index 0000000..9ba0888 Binary files /dev/null and b/build/icons/48x48.png differ diff --git a/build/icons/512x512.png b/build/icons/512x512.png new file mode 100644 index 0000000..1aa14c0 Binary files /dev/null and b/build/icons/512x512.png differ diff --git a/build/icons/64x64.png b/build/icons/64x64.png new file mode 100644 index 0000000..ad5a7d3 Binary files /dev/null and b/build/icons/64x64.png differ diff --git a/build/icons/icon.icns b/build/icons/icon.icns index a65f91b..4b81e93 100644 Binary files a/build/icons/icon.icns and b/build/icons/icon.icns differ diff --git a/build/icons/icon.ico b/build/icons/icon.ico index 8702729..5916add 100644 Binary files a/build/icons/icon.ico and b/build/icons/icon.ico differ diff --git a/config/dev.env.js b/config/dev.env.js index fc7d31b..45d79bc 100644 --- a/config/dev.env.js +++ b/config/dev.env.js @@ -1,5 +1,7 @@ module.exports = { NODE_ENV: '"development"', BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"', - VUE_APP_SOCKET_API: '"ws://127.0.0.1:10010"' + VUE_APP_SOCKET_API: '"ws://127.0.0.1:10010"', + VUE_APP_CONSOLE_V2: '"true"', + VUE_APP_CONSOLE_DESIGN_ONLY: '"true"' } diff --git a/config/prod.env.js b/config/prod.env.js index 9f6ce5f..6d9b021 100644 --- a/config/prod.env.js +++ b/config/prod.env.js @@ -1,5 +1,7 @@ module.exports = { NODE_ENV: '"production"', BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"', - VUE_APP_SOCKET_API: '"ws://127.0.0.1:10010"' + VUE_APP_SOCKET_API: '"ws://127.0.0.1:10010"', + VUE_APP_CONSOLE_V2: '"false"', + VUE_APP_CONSOLE_DESIGN_ONLY: '"false"' } diff --git a/package.json b/package.json index 065f890..3d37243 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discworker", - "version": "0.0.1", + "version": "1.1.1.10", "author": "24kycj <1637269896@qq.com>", "description": "KaShu SoonWorkerDisk", "homepage": "https://www.cardsoon.com/index.php/cn", @@ -32,8 +32,8 @@ "to": "." }, { - "from": "./cardsoonServer", - "to": "." + "from": "./CardsoonServer", + "to": "CardsoonServer" }, { "from": "./help", @@ -73,6 +73,7 @@ }, "dependencies": { "@electron/remote": "^2.1.2", + "@fortawesome/fontawesome-free": "^6.7.2", "archiver": "^7.0.1", "archiver-zip-encrypted": "^2.0.0", "axios": "^0.18.0", @@ -139,4 +140,4 @@ "webpack-hot-middleware": "^2.22.2", "webpack-merge": "^4.1.3" } -} \ No newline at end of file +} diff --git a/src/index.ejs b/src/index.ejs index c2fa254..9fe4c25 100644 --- a/src/index.ejs +++ b/src/index.ejs @@ -3,6 +3,7 @@ DiscWorker V1.01 + <% if (typeof isWeb === 'undefined' ? htmlWebpackPlugin.options.nodeModules : !isWeb && htmlWebpackPlugin.options.nodeModules) { %> + + + + + + diff --git a/src/renderer/components/console/device/DiscBins.vue b/src/renderer/components/console/device/DiscBins.vue new file mode 100644 index 0000000..294de14 --- /dev/null +++ b/src/renderer/components/console/device/DiscBins.vue @@ -0,0 +1,185 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/device/InkLevels.vue b/src/renderer/components/console/device/InkLevels.vue new file mode 100644 index 0000000..4ca00bc --- /dev/null +++ b/src/renderer/components/console/device/InkLevels.vue @@ -0,0 +1,141 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/drive/DriveCard.vue b/src/renderer/components/console/drive/DriveCard.vue new file mode 100644 index 0000000..7903864 --- /dev/null +++ b/src/renderer/components/console/drive/DriveCard.vue @@ -0,0 +1,387 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/drive/DriveMonitor.vue b/src/renderer/components/console/drive/DriveMonitor.vue new file mode 100644 index 0000000..fe20d0e --- /dev/null +++ b/src/renderer/components/console/drive/DriveMonitor.vue @@ -0,0 +1,133 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/icons/ConsoleIcon.vue b/src/renderer/components/console/icons/ConsoleIcon.vue new file mode 100644 index 0000000..044f4c7 --- /dev/null +++ b/src/renderer/components/console/icons/ConsoleIcon.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/renderer/components/console/layout/ConsoleHeader.vue b/src/renderer/components/console/layout/ConsoleHeader.vue new file mode 100644 index 0000000..710c48b --- /dev/null +++ b/src/renderer/components/console/layout/ConsoleHeader.vue @@ -0,0 +1,372 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/layout/ConsoleShell.vue b/src/renderer/components/console/layout/ConsoleShell.vue new file mode 100644 index 0000000..def73f7 --- /dev/null +++ b/src/renderer/components/console/layout/ConsoleShell.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/src/renderer/components/console/layout/ConsoleStatusBar.vue b/src/renderer/components/console/layout/ConsoleStatusBar.vue new file mode 100644 index 0000000..64e1fcd --- /dev/null +++ b/src/renderer/components/console/layout/ConsoleStatusBar.vue @@ -0,0 +1,147 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/task/ActivityLog.vue b/src/renderer/components/console/task/ActivityLog.vue new file mode 100644 index 0000000..e935e87 --- /dev/null +++ b/src/renderer/components/console/task/ActivityLog.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/src/renderer/components/console/task/TaskTable.vue b/src/renderer/components/console/task/TaskTable.vue new file mode 100644 index 0000000..5be5d6d --- /dev/null +++ b/src/renderer/components/console/task/TaskTable.vue @@ -0,0 +1,331 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/task/TaskWorkspace.vue b/src/renderer/components/console/task/TaskWorkspace.vue new file mode 100644 index 0000000..98a94b6 --- /dev/null +++ b/src/renderer/components/console/task/TaskWorkspace.vue @@ -0,0 +1,197 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/ui/DwCard.vue b/src/renderer/components/console/ui/DwCard.vue new file mode 100644 index 0000000..91b3d9f --- /dev/null +++ b/src/renderer/components/console/ui/DwCard.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/src/renderer/components/console/ui/DwEmpty.vue b/src/renderer/components/console/ui/DwEmpty.vue new file mode 100644 index 0000000..d09a52f --- /dev/null +++ b/src/renderer/components/console/ui/DwEmpty.vue @@ -0,0 +1,215 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/ui/DwKpiChip.vue b/src/renderer/components/console/ui/DwKpiChip.vue new file mode 100644 index 0000000..8326771 --- /dev/null +++ b/src/renderer/components/console/ui/DwKpiChip.vue @@ -0,0 +1,127 @@ + + + + + + + + + + + diff --git a/src/renderer/components/console/ui/DwProgressBar.vue b/src/renderer/components/console/ui/DwProgressBar.vue new file mode 100644 index 0000000..d030ca7 --- /dev/null +++ b/src/renderer/components/console/ui/DwProgressBar.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/src/renderer/components/console/ui/DwStatusPill.vue b/src/renderer/components/console/ui/DwStatusPill.vue new file mode 100644 index 0000000..0f71edf --- /dev/null +++ b/src/renderer/components/console/ui/DwStatusPill.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/renderer/components/console/work/WorkAddModalV2.vue b/src/renderer/components/console/work/WorkAddModalV2.vue new file mode 100644 index 0000000..7d4a097 --- /dev/null +++ b/src/renderer/components/console/work/WorkAddModalV2.vue @@ -0,0 +1,230 @@ + + + + + + + diff --git a/src/renderer/components/console/work/WorkLabelPanel.vue b/src/renderer/components/console/work/WorkLabelPanel.vue new file mode 100644 index 0000000..ab3badd --- /dev/null +++ b/src/renderer/components/console/work/WorkLabelPanel.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/src/renderer/mock/console.mock.js b/src/renderer/mock/console.mock.js new file mode 100644 index 0000000..b0afbd7 --- /dev/null +++ b/src/renderer/mock/console.mock.js @@ -0,0 +1,178 @@ +export const MOCK_PRINTER_STATUS = { + I: '空闲', + B: '忙碌', + P: '正在打印' +} + +export function getConsoleMock() { + return { + isConnect: true, + serviceStatus: true, + taskNum: [12, 2, 1], + printer_info: { + blue_last: 78, + red_last: 65, + yellow_last: 72, + calibration_flag: 0, + lack_ink: 0, + cover_flag: false, + printer_name: '4200', + printer_path: '/dev/usb/lp0', + printer_status: 'I', + printer_tray: 'I', + system_ip: '192.168.1.100', + system_name: 'discworker-host', + serial_no: 'TF-2025-X88', + free_cache_space: 51587919872, + total_cache_space: 105152176128, + test_use: 255, + is_set_cddev: 0, + strongList: { + 1: { strong_pos: 1, cd_type: 1, cd_num: 38 }, + 2: { strong_pos: 2, cd_type: 2, cd_num: 25 } + }, + strong_list: [ + { strong_pos: 1, cd_type: 1, cd_num: 38 }, + { strong_pos: 2, cd_type: 2, cd_num: 25 } + ] + }, + cd_list: [ + { + cd_pos: 2, + cd_status: 1, + cd_taskid: '', + dev_path: '/dev/sr0', + dev_type: 1, + cd_type: 0, + cd_assid: '', + total_size: 4700000000, + use_size: 0, + copy_scheduler: 0, + burn_speed: 0, + is_sim: false + }, + { + cd_pos: 1, + cd_status: 4, + cd_taskid: '1763431953099', + dev_path: '/dev/sr1', + dev_type: 2, + cd_type: 2, + cd_assid: 'Archive_2025', + total_size: 4700000000, + use_size: 2115000000, + copy_scheduler: 45, + burn_speed: 4.2, + is_sim: false + } + ], + CDName: { 1: '下光驱', 2: '上光驱' }, + CDStatus: { + 0: '无法确定光盘状态', + 1: '没有光盘', + 2: '光驱托盘打开', + 3: '光驱未准备好', + 4: '光盘状态正常,光盘已插入', + 5: '校验中' + }, + CDTypes: { 1: 'CD', 2: 'DVD', 3: 'DVD_DL', 4: 'BD' }, + diskTypes: { 1: 'CD', 2: 'DVD', 3: 'BD' }, + task_list: [ + { + task_uuid: '1763452409668', + task_name: 'MU_FU_QIANG_Project_A', + finish_freq: 1, + total_freq: 1, + task_status: 100, + start_time: '2025-11-18 15:54:00', + finish_time: '2025-11-18 16:10:00', + task_size: 1771674009, + cdType: 'DVD', + id: '1763452409668', + ass_task: [ + { + id: '1763452409668_1', + cd_type: 2, + task_size: 1771674009, + task_status: 100, + start_time: '2025-11-18 15:54:00', + finish_time: '2025-11-18 16:10:00', + cdType: 'DVD_1' + } + ] + }, + { + task_uuid: '1763447668606', + task_name: 'System_Backup_Weekly', + finish_freq: 0, + total_freq: 1, + task_status: 5, + start_time: '2025-11-18 14:35:00', + finish_time: '', + task_size: 3435973836, + cdType: 'DVD', + id: '1763447668606', + ass_task: [ + { + id: '1763447668606_1', + cd_type: 2, + task_size: 3435973836, + task_status: 5, + start_time: '2025-11-18 14:35:00', + finish_time: '', + cdType: 'DVD_1' + } + ] + }, + { + task_uuid: '1763446831835', + task_name: 'MU_FU_QIANG_Docs', + finish_freq: 0, + total_freq: 1, + task_status: 7, + start_time: '2025-11-18 14:21:00', + finish_time: '', + task_size: 434865438, + cdType: 'CD', + id: '1763446831835', + ass_task: [] + }, + { + task_uuid: '1763431953073', + task_name: 'WuGuiSheng_Media', + finish_freq: 0, + total_freq: 1, + task_status: 7, + start_time: '2025-11-18 10:38:00', + finish_time: '', + task_size: 1771674009, + cdType: 'DVD', + id: '1763431953073', + ass_task: [] + }, + { + task_uuid: '1763431953099', + task_name: 'Auto_Archive_2025', + finish_freq: 0, + total_freq: 1, + task_status: 3, + start_time: '2025-11-18 16:01:00', + finish_time: '', + task_size: 1771674009, + cdType: 'DVD', + id: '1763431953099', + ass_task: [] + } + ], + logList: [ + { time: '16:01:02', text: '任务 Auto_Archive_2025 开始刻录', status: 'INFO' }, + { time: '15:54:10', text: '任务 MU_FU_QIANG_Project_A 已完成', status: 'INFO' }, + { time: '14:35:22', text: 'System_Backup_Weekly 刻录失败', status: 'ERROR' }, + { time: '14:21:05', text: '用户取消任务 MU_FU_QIANG_Docs', status: 'DEBUG' } + ], + allLogList: [ + { time: '10:00:00', text: '系统启动完成', status: 'INFO' }, + { time: '09:58:12', text: '设备 TF-2025-X88 已注册', status: 'INFO' } + ] + } +} diff --git a/src/renderer/permission.js b/src/renderer/permission.js index e7b3b79..dafd4e4 100644 --- a/src/renderer/permission.js +++ b/src/renderer/permission.js @@ -3,13 +3,14 @@ import store from './store' import NProgress from 'nprogress' // Progress 进度条 import 'nprogress/nprogress.css'// Progress 进度条样式 import { Message } from 'element-ui' +import { getDefaultHomeRoute } from '@/utils/console/homeRoute' -const whiteList = ['/login'] // 不重定向白名单 +const whiteList = ['/login', '/console/preview'] // 不重定向白名单 router.beforeEach((to, from, next) => { NProgress.start() if (store.getters.token) { if (to.path === '/login') { - next({ path: '/' }) + next({ path: getDefaultHomeRoute() }) NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it } else { next() diff --git a/src/renderer/platform/desktop.js b/src/renderer/platform/desktop.js index a1a074c..363d7ea 100644 --- a/src/renderer/platform/desktop.js +++ b/src/renderer/platform/desktop.js @@ -32,9 +32,9 @@ export function openHelp() { export function runCmd(cmd) { return new Promise((resolve, reject) => { exec(cmd, (err, stdout, stderr) => { + // 仅按退出码判定失败;Qt 等工具常向 stderr 打警告(如 QStandardPaths) if (err) reject(err.message || err) - else if (stderr) reject(stderr) - else resolve(stdout) + else resolve(stdout || '') }) }) } diff --git a/src/renderer/router/index.js b/src/renderer/router/index.js index 14850cf..fbd6b49 100644 --- a/src/renderer/router/index.js +++ b/src/renderer/router/index.js @@ -6,6 +6,9 @@ Vue.use(Router) export default new Router({ routes: [ { path: '/', component: () => import('@/views/dashboard/index'), name: '首页' }, + { path: '/legacy', component: () => import('@/views/dashboard/index'), name: '旧版首页' }, + { path: '/console', component: () => import('@/views/console/index'), name: '控制台' }, + { path: '/console/preview', component: () => import('@/views/console/preview'), name: '控制台预览' }, { path: '/login', component: () => import('@/views/login/index') }, { path: '/404', component: () => import('@/views/404') }, { path: '/manage', component: () => import('@/views/manage/index'), name: '系统管理' }, diff --git a/src/renderer/styles/console/components.scss b/src/renderer/styles/console/components.scss new file mode 100644 index 0000000..1be8b8b --- /dev/null +++ b/src/renderer/styles/console/components.scss @@ -0,0 +1,95 @@ +.console-app { + .dw-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 7px 14px; + border-radius: var(--dw-radius-sm); + font-size: 13px; + font-weight: 500; + cursor: pointer; + border: 1px solid transparent; + transition: all 0.15s ease; + background: transparent; + color: var(--dw-text); + + &--primary { + background: var(--dw-primary); + color: #fff; + box-shadow: 0 2px 4px rgba(30, 79, 217, 0.25); + } + + &--primary:hover { + background: var(--dw-primary-hover); + } + + &--outline { + background: var(--dw-surface); + border-color: var(--dw-border); + } + + &--outline:hover { + background: #f9fafb; + border-color: #d1d5db; + } + + &--ghost { + color: var(--dw-text-secondary); + } + + &--ghost:hover { + color: var(--dw-primary); + background: var(--dw-primary-soft); + } + } + + .dw-skeleton { + background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: dw-shimmer 1.2s infinite; + border-radius: var(--dw-radius-sm); + } + + @keyframes dw-shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } + } + + @keyframes dw-pulse { + 0%, 100% { box-shadow: 0 0 0 0 rgba(5, 150, 105, 0.4); } + 50% { box-shadow: 0 0 0 6px rgba(5, 150, 105, 0); } + } + + .dw-console-table { + border-radius: var(--dw-radius-sm); + overflow: hidden; + + &::before { display: none; } + + .el-table__header-wrapper th { + background: #f8fafc !important; + color: var(--dw-text-secondary); + font-size: 12px; + font-weight: 600; + padding: 10px 0; + } + + .el-table__body td { + font-size: 13px; + padding: 10px 0; + } + + .el-table__row:hover > td { + background: #f8fbff !important; + } + + .el-table__empty-block { + min-height: 200px; + } + + .el-table__empty-text { + width: 100%; + line-height: 1.4; + } + } +} diff --git a/src/renderer/styles/console/index.scss b/src/renderer/styles/console/index.scss new file mode 100644 index 0000000..34687ef --- /dev/null +++ b/src/renderer/styles/console/index.scss @@ -0,0 +1,5 @@ +@use './tokens.scss'; +@use './layout.scss'; +@use './components.scss'; + +@import '~@fortawesome/fontawesome-free/css/all.min.css'; diff --git a/src/renderer/styles/console/layout.scss b/src/renderer/styles/console/layout.scss new file mode 100644 index 0000000..124d623 --- /dev/null +++ b/src/renderer/styles/console/layout.scss @@ -0,0 +1,41 @@ +.console-app { + display: flex; + flex-direction: column; + min-height: 100vh; + + .console-main { + flex: 1; + display: grid; + grid-template-columns: var(--dw-sidebar-w) 1fr; + grid-template-rows: auto 1fr; + gap: 24px; + padding: 24px; + min-height: 0; + overflow: hidden; + } + + .console-main__device { + grid-row: 1 / 3; + min-height: 0; + display: flex; + flex-direction: column; + } + + .console-main__drive { + grid-column: 2; + min-height: 0; + } + + .console-main__task { + grid-column: 2; + min-height: 280px; + display: flex; + flex-direction: column; + } + + @media (max-width: 1279px) { + .console-main { + grid-template-columns: 280px 1fr; + } + } +} diff --git a/src/renderer/styles/console/tokens.scss b/src/renderer/styles/console/tokens.scss new file mode 100644 index 0000000..85dc723 --- /dev/null +++ b/src/renderer/styles/console/tokens.scss @@ -0,0 +1,48 @@ +.console-app { + --dw-primary: #2563eb; + --dw-primary-hover: #1d4ed8; + --dw-primary-soft: #eff6ff; + --dw-bg: #f0f2f5; + --dw-surface: #ffffff; + --dw-border: #e4e7ec; + --dw-text: #111827; + --dw-text-secondary: #6b7280; + --dw-text-muted: #9ca3af; + --dw-success: #10b981; + --dw-success-soft: #ecfdf5; + --dw-warning: #f59e0b; + --dw-warning-soft: #fffbeb; + --dw-danger: #ef4444; + --dw-danger-soft: #fef2f2; + --dw-info: #0284c7; + --dw-info-soft: #e0f2fe; + --dw-header-h: 64px; + --dw-statusbar-h: 28px; + --dw-sidebar-w: 350px; + --dw-radius-sm: 6px; + --dw-radius-md: 10px; + --dw-radius-lg: 14px; + --dw-shadow-sm: 0 1px 3px rgba(16, 24, 40, 0.08); + --dw-shadow-md: 0 8px 24px rgba(16, 24, 40, 0.08); + --dw-shadow-card: 0 1px 2px rgba(16, 24, 40, 0.06), 0 4px 16px rgba(16, 24, 40, 0.04); + --dw-gradient-device: radial-gradient(circle at 50% 35%, #ffffff 0%, #eef2ff 45%, #e2e8f0 100%); + --dw-gradient-disc: linear-gradient(180deg, #bfdbfe 0%, #3b82f6 100%); + --dw-font: 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif; + + font-family: var(--dw-font); + color: var(--dw-text); + background: var(--dw-bg); + min-height: 100vh; + font-variant-numeric: tabular-nums; + + .el-button--primary { + background-color: var(--dw-primary); + border-color: var(--dw-primary); + } + + .el-button--primary:hover, + .el-button--primary:focus { + background-color: var(--dw-primary-hover); + border-color: var(--dw-primary-hover); + } +} diff --git a/src/renderer/utils/console/consoleActions.js b/src/renderer/utils/console/consoleActions.js new file mode 100644 index 0000000..6d175c0 --- /dev/null +++ b/src/renderer/utils/console/consoleActions.js @@ -0,0 +1,280 @@ +let outTimer = null + +export function getShPath(platform) { + return platform.hasRunCmd && platform.hasRunCmd() + ? platform.pathJoin(platform.getAppRoot(), 'CardsoonServer', 'control.sh') + : '' +} + +export function getActivePath(platform) { + return platform.hasRunCmd && platform.hasRunCmd() + ? platform.pathJoin(platform.getAppRoot(), 'CardsoonServer', 'regist.sh') + : '' +} + +export function serviceChange(vm, enabled) { + if (enabled) startService(vm) + else stopService(vm) +} + +export function startService(vm) { + vm.$prompt('请输入管理员密码并点击确定以执行开启服务操作', '温馨提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + inputType: 'password' + }) + .then(({ value }) => { + vm.serviceLoading = true + vm.serviceText = '服务启动中' + const shPath = getShPath(vm.$platform || require('@/platform').default) + vm.$runCmd(`bash ${shPath} start ${value}`) + .then(() => { + vm.$store.dispatch('chat/setDatas', { name: 'workStatus', data: true }) + setTimeout(() => { + vm.$store.dispatch('chat/initWebSocket') + vm.serviceLoading = false + }, 500) + }) + .catch((err) => { + vm.$message({ message: err, type: 'error' }) + vm.serviceLoading = false + }) + }) + .catch(() => { + vm.serviceLoading = false + }) +} + +export function stopService(vm) { + vm.$prompt('请输入管理员密码并点击确定以执行关闭服务操作', '温馨提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + inputType: 'password' + }) + .then(({ value }) => { + vm.serviceLoading = true + vm.serviceText = '服务关闭中' + const shPath = getShPath(vm.$platform || require('@/platform').default) + vm.$runCmd(`bash ${shPath} stop ${value}`) + .then(() => { + vm.$store.dispatch('chat/setDatas', { name: 'workStatus', data: false }) + setTimeout(() => { + vm.$store.dispatch('chat/closeWebsocket') + vm.serviceLoading = false + vm.$message({ message: '服务已关闭', type: 'success' }) + }, 500) + }) + .catch((err) => { + vm.$message({ message: err, type: 'error' }) + vm.serviceLoading = false + }) + }) + .catch(() => { + vm.serviceLoading = false + }) +} + +export function handleActive(vm) { + vm.$prompt('请输入管理员密码并点击确定以前往激活设备', '温馨提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + inputType: 'password' + }) + .then(({ value }) => { + const activePath = getActivePath(vm.$platform || require('@/platform').default) + vm.$runCmd(`bash "${activePath}" '${String(value).replace(/'/g, `'\\''`)}'`).catch((err) => { + vm.$message({ message: err, type: 'error' }) + }) + }) + .catch(() => {}) +} + +export function openWork(vm, platform) { + if (!vm.workStatus) { + vm.$message({ message: '请先连接服务', type: 'warning' }) + return + } + if (vm.printer_info.is_set_cddev === 1) { + vm.$confirm('上下光驱未设置,请前往设置', '温馨提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }) + .then(() => goSet(vm, '/manage?type=cddev')) + .catch(() => {}) + return + } + vm.isNew = false + platform + .showOpenFileDialog({ + title: '打开作业文件', + filters: [{ name: 'Soon Work', extensions: ['dwk'] }] + }) + .then((res) => { + const pathOrName = (res.filePaths && res.filePaths[0]) || res.path + const contentP = res.content != null + ? Promise.resolve(res.content) + : platform.readFile(pathOrName, 'utf8') + contentP + .then((data) => { + const sep = pathOrName.lastIndexOf('\\') >= 0 ? '\\' : '/' + const fileName = pathOrName.trim().split(sep).pop() || pathOrName + vm.saveWorkList = JSON.parse(data) + vm.saveWorkList.task_name = fileName + vm.workShow = true + vm.$nextTick(() => { + vm.$refs.workAdd.show() + }) + }) + .catch((err) => { + vm.$message({ message: err && err.message ? err.message : '打开失败', type: 'error' }) + }) + }) + .catch(() => {}) +} + +export function showWork(vm) { + vm.isNew = true + vm.workShow = true + vm.$nextTick(() => { + vm.$refs.workAdd.show() + }) +} + +export function workRetry(vm, row) { + vm.$store + .dispatch('chat/websocketsend', { + req_name: 'retry_task', + req_type: 1, + req_info: { task_uuid: row.task_uuid } + }) + .then(() => { + vm.$message({ message: '重试成功', type: 'success' }) + row.task_status = 1 + if (row.ass_task && row.ass_task.length) { + row.ass_task.forEach((item) => { item.task_status = 1 }) + } + }) +} + +export function workCancel(vm, row) { + vm.$store.dispatch('chat/websocketsend', { + req_name: 'cancle_task', + req_type: 1, + req_info: { task_uuid: row.task_uuid } + }) +} + +export function handleRun(vm, runVal) { + const cd_list = vm.cd_list + if (!vm.workStatus) { + vm.$message({ message: '请先连接服务', type: 'warning' }) + return + } + if (!runVal) { + vm.$message({ message: '请先选择需要执行的操作', type: 'warning' }) + return + } + if ((cd_list[0] && cd_list[0].copy_scheduler) || (cd_list[1] && cd_list[1].copy_scheduler)) { + vm.$message({ message: '正在任务中,无法进行操作', type: 'warning' }) + return + } + clearTimeout(outTimer) + const send = (req_name, req_type, req_info, confirmMsg) => { + vm.$confirm(confirmMsg, '温馨提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + vm.$store.dispatch('chat/websocketsend', { req_name, req_type, req_info }).then(() => { + vm.$message({ message: '已发送指令', type: 'success' }) + }) + }).catch(() => {}) + } + switch (parseInt(runVal)) { + case 1: + send('resetPrinter', 2, {}, '确定执行重启设备操作吗?') + break + case 2: + send('cleanError', 2, {}, '确定执行清除错误操作吗?') + break + case 6: + outTimer = setTimeout(() => { + vm.currentRunVal = 0 + vm.changeCD.req_info.strong_list = JSON.parse(JSON.stringify(vm.default_cd)) + }, 120000) + break + case 7: + send('RefreshCdNum', 2, {}, '点击确定后将刷新所有光盘桶的光盘数量。') + break + default: + break + } + vm.currentRunVal = runVal +} + +export function handleCommandCD(vm, e, type) { + vm.changeCD.req_info.strong_list[type - 1].strong_type = parseInt(e, 10) +} + +export function sureCD(vm) { + vm.$store + .dispatch('chat/websocketsend', vm.changeCD) + .then(() => { + clearTimeout(outTimer) + vm.currentRunVal = 0 + const { getCurrentOSDiscTypes } = require('@/utils/fileSize') + let typeLists = getCurrentOSDiscTypes() + const isBD = vm.changeCD.req_info.strong_list.findIndex((item) => parseInt(item.strong_type) > 3) + if (isBD === -1) typeLists = typeLists.slice(0, 3) + vm.$store.dispatch('chat/setDatas', { name: 'cd_types', data: typeLists }) + }) + .catch(() => { + clearTimeout(outTimer) + vm.currentRunVal = 0 + }) +} + +export function cancleCD(vm) { + clearTimeout(outTimer) + vm.currentRunVal = 0 + vm.changeCD.req_info.strong_list = JSON.parse(JSON.stringify(vm.default_cd)) +} + +export function noticeChange(vm) { + if (!vm.workStatus) { + vm.$message({ message: '请先连接服务', type: 'warning' }) + return + } + vm.showAll = !vm.showAll +} + +export function goSet(vm, path) { + vm.$router.push({ path }) +} + +export function goOut(vm) { + vm.$store.dispatch('FedLogOut').then(() => { + location.reload() + }) +} + +export function help() { + const platform = require('@/platform').default + if (platform.openHelp) { + platform.openHelp() + } +} + +export function getChangeCDDefault() { + return { + req_name: 'setCDStrong', + req_type: 2, + req_info: { + strong_list: [ + { strong_pos: 1, strong_type: 1 }, + { strong_pos: 2, strong_type: 1 } + ] + } + } +} diff --git a/src/renderer/utils/console/constants.js b/src/renderer/utils/console/constants.js new file mode 100644 index 0000000..63bb3b9 --- /dev/null +++ b/src/renderer/utils/console/constants.js @@ -0,0 +1,59 @@ +export const TASK_STATUS_LABELS = { + 0: '新建中', + 1: '准备中', + 2: '等待中', + 3: '正在刻录', + 4: '正在打印', + 5: '已失败', + 6: '运行中', + 7: '已取消', + 100: '已完成' +} + +export function taskStatusVariant(status) { + if (status === 100) return 'online' + if (status === 5) return 'error' + if (status === 7) return 'neutral' + if ([3, 4, 6].includes(status)) return 'busy' + if ([0, 1, 2].includes(status)) return 'info' + return 'idle' +} + +export const CD_TYPE_LABELS = { + 1: 'CD', + 2: 'DVD', + 3: 'DVD_DL', + 4: 'BD', + 5: 'BD_DL', + 6: 'BD_TL', + 7: 'BD_QL' +} + +export function getDeviceImages() { + const dp4200 = { + img1: require('@/assets/images/DP-4200.png'), + img2: require('@/assets/images/DP-4200-open.png'), + num1: 5, + num2: 10 + } + const dpSe3 = { + img1: require('@/assets/images/DP-SE3.png'), + img2: require('@/assets/images/DP-SE3-open.png'), + num1: 3, + num2: 6 + } + return { + 4200: dp4200, + SE3: dpSe3, + 'DP-4200': dp4200, + 'DP-SE3': dpSe3 + } +} + +export function resolveDeviceImageKey(printerName) { + if (!printerName) return '4200' + const images = getDeviceImages() + if (images[printerName]) return printerName + if (String(printerName).indexOf('SE3') > -1) return 'SE3' + return '4200' +} diff --git a/src/renderer/utils/console/homeRoute.js b/src/renderer/utils/console/homeRoute.js new file mode 100644 index 0000000..4d5806e --- /dev/null +++ b/src/renderer/utils/console/homeRoute.js @@ -0,0 +1,7 @@ +export function isConsoleV2Enabled() { + return process.env.VUE_APP_CONSOLE_V2 === 'true' +} + +export function getDefaultHomeRoute() { + return isConsoleV2Enabled() ? '/console' : '/' +} diff --git a/src/renderer/utils/console/printerStatus.js b/src/renderer/utils/console/printerStatus.js new file mode 100644 index 0000000..d2bfc33 --- /dev/null +++ b/src/renderer/utils/console/printerStatus.js @@ -0,0 +1,21 @@ +export function getRunText({ printer_info, isConnect, serviceStatus, PrinterStatus }) { + let text = PrinterStatus[printer_info.printer_status] || '未知' + if (printer_info.lack_ink) text = '缺墨警告' + if (!isConnect) text = '服务已关闭' + if (!serviceStatus) text = '正在连接服务器,请检查服务器是否正常' + return text +} + +export function getDeviceTagVariant({ printer_info, isConnect, serviceStatus }) { + if (!isConnect || !serviceStatus) return 'error' + if (printer_info.lack_ink) return 'warning' + if (printer_info.printer_status === 'I') return 'online' + if (['B', 'P'].includes(printer_info.printer_status)) return 'busy' + return 'idle' +} + +export function getDeviceTagLabel({ printer_info, isConnect, serviceStatus, PrinterStatus }) { + if (!isConnect || !serviceStatus) return '离线' + if (printer_info.lack_ink) return '缺墨' + return PrinterStatus[printer_info.printer_status] || '空闲' +} diff --git a/src/renderer/utils/console/printerWatch.js b/src/renderer/utils/console/printerWatch.js new file mode 100644 index 0000000..f89f33a --- /dev/null +++ b/src/renderer/utils/console/printerWatch.js @@ -0,0 +1,45 @@ +export function buildRunList(printerName) { + const isSe3 = printerName && printerName.indexOf('SE3') > -1 + const base = [ + { value: 1, label: '重启设备' }, + { value: 2, label: '清除错误' } + ] + if (isSe3) { + return base.concat([{ value: 7, label: '刷新光盘桶' }]) + } + return base.concat([ + { value: 6, label: '设置光盘桶' }, + { value: 7, label: '刷新光盘桶' } + ]) +} + +export function buildChangeCDStrongList(printerName, strongList, currentRunVal) { + if (currentRunVal === 6) return null + const isSe3 = printerName && printerName.indexOf('SE3') > -1 + if (isSe3) { + return [ + { strong_pos: 1, strong_type: strongList[0] ? strongList[0].cd_type : 1 } + ] + } + return [ + { strong_pos: 1, strong_type: strongList[0] ? strongList[0].cd_type : 1 }, + { strong_pos: 2, strong_type: strongList[1] ? strongList[1].cd_type : 1 } + ] +} + +export function applyPrinterInfoSideEffects(printerInfo, ctx) { + const { currentRunVal, router, is_set_cddev_flag } = ctx + const result = { + runList: buildRunList(printerInfo.printer_name), + changeCDStrongList: buildChangeCDStrongList( + printerInfo.printer_name, + printerInfo.strong_list || [], + currentRunVal + ), + shouldRedirectCddev: false + } + if (printerInfo.is_set_cddev === 1 && !is_set_cddev_flag) { + result.shouldRedirectCddev = true + } + return result +} diff --git a/src/renderer/utils/console/taskListTransform.js b/src/renderer/utils/console/taskListTransform.js new file mode 100644 index 0000000..1d92462 --- /dev/null +++ b/src/renderer/utils/console/taskListTransform.js @@ -0,0 +1,33 @@ +export function transformTaskList(tasks, CDTypes, accAdd) { + if (!tasks || !tasks.length) { + return { list: [], taskNum: [0, 0, 0] } + } + const nums = [0, 0, 0] + const list = tasks.map((item) => { + const row = { ...item } + if (row.task_status === 100) nums[0]++ + else if (row.task_status === 7) nums[1]++ + else if (row.task_status === 5) nums[2]++ + + if (row.ass_task && row.ass_task.length) { + let task_size = 0 + row.cdType = CDTypes[row.ass_task[0].cd_type] + row.id = row.task_uuid + row.ass_task.forEach((sub, idx) => { + sub.id = row.task_uuid + '_' + (idx + 1) + sub.cdType = CDTypes[sub.cd_type] + '_' + (idx + 1) + task_size = accAdd(task_size, sub.task_size) + }) + row.task_size = task_size + } + return row + }) + + list.sort((a, b) => { + if (a.task_status === 100 && b.task_status !== 100) return 1 + if (b.task_status === 100 && a.task_status !== 100) return -1 + return 0 + }) + + return { list, taskNum: nums } +} diff --git a/src/renderer/views/console/consolePage.vue b/src/renderer/views/console/consolePage.vue new file mode 100644 index 0000000..b56e639 --- /dev/null +++ b/src/renderer/views/console/consolePage.vue @@ -0,0 +1,445 @@ + + + + + diff --git a/src/renderer/views/console/index.vue b/src/renderer/views/console/index.vue new file mode 100644 index 0000000..0e8fba7 --- /dev/null +++ b/src/renderer/views/console/index.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/renderer/views/console/preview.vue b/src/renderer/views/console/preview.vue new file mode 100644 index 0000000..33d4adc --- /dev/null +++ b/src/renderer/views/console/preview.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/renderer/views/dashboard/index.vue b/src/renderer/views/dashboard/index.vue index 22d8182..8aeb6ca 100644 --- a/src/renderer/views/dashboard/index.vue +++ b/src/renderer/views/dashboard/index.vue @@ -532,6 +532,8 @@ import { mapGetters } from 'vuex' import UserInfo from '@/components/userInfo/userInfo.vue' import WorkAdd from '@/components/workAdd/workAdd.vue' import platform from '@/platform' +import { transformTaskList } from '@/utils/console/taskListTransform' +import { applyPrinterInfoSideEffects } from '@/utils/console/printerWatch' export default { name: 'dashboard', components: { UserInfo, WorkAdd }, @@ -666,46 +668,8 @@ export default { '$store.state.chat.task_list': { handler(val) { if (val && val.length) { - let tasks = [...val] - let list = [] - let nums = [0, 0, 0] - tasks.forEach((item, index) => { - if (item.task_status == 100) { - nums[0]++ - } else if (item.task_status == 7) { - nums[1]++ - } else if (item.task_status == 5) { - nums[2]++ - } - if (item.ass_task && item.ass_task.length) { - let task_size = 0 - item.cdType = this.$store.state.chat.CDTypes[item.ass_task[0].cd_type] - item.id = item.task_uuid - item.ass_task.forEach((item2, idx) => { - item2.id = item.task_uuid + '_' + (idx + 1) - item2.cdType = this.$store.state.chat.CDTypes[item2.cd_type] + '_' + (idx + 1) - task_size = this.$accAdd(task_size, item2.task_size) - }) - item.task_size = task_size - } - list.push(item) - }) - - // 对任务列表进行排序:task_status为100的放到后面,其他保持原顺序 - list.sort((a, b) => { - // 如果a的状态是100,b不是100,则a排在后面 - if (a.task_status === 100 && b.task_status !== 100) { - return 1 - } - // 如果b的状态是100,a不是100,则b排在后面 - if (b.task_status === 100 && a.task_status !== 100) { - return -1 - } - // 其他情况保持原顺序(返回0表示相等,保持原顺序) - return 0 - }) - - this.taskNum = nums + const { list, taskNum } = transformTaskList(val, this.$store.state.chat.CDTypes, this.$accAdd) + this.taskNum = taskNum this.task_list = list } else { this.task_list = [] @@ -719,64 +683,16 @@ export default { that = this let data = { ...val } this.printer_info = { ...data } - if (val.printer_name && val.printer_name.indexOf('SE3') > -1) { - if (this.currentRunVal !== 6) { - this.changeCD.req_info.strong_list = [ - { - strong_pos: 1, - strong_type: data.strong_list[0].cd_type - } - ] - this.default_cd = this.changeCD.req_info.strong_list - } - this.runList = [ - { - value: 1, - label: '重启设备' - }, - { - value: 2, - label: '清除错误' - }, - { - value: 7, - label: '刷新光盘桶' - } - ] - } else { - if (this.currentRunVal !== 6) { - this.changeCD.req_info.strong_list = [ - { - strong_pos: 1, - strong_type: data.strong_list[0].cd_type - }, - { - strong_pos: 2, - strong_type: data.strong_list[1].cd_type - } - ] - this.default_cd = this.changeCD.req_info.strong_list - } - this.runList = [ - { - value: 1, - label: '重启设备' - }, - { - value: 2, - label: '清除错误' - }, - { - value: 6, - label: '设置光盘桶' - }, - { - value: 7, - label: '刷新光盘桶' - } - ] + const fx = applyPrinterInfoSideEffects(data, { + currentRunVal: this.currentRunVal, + is_set_cddev_flag: this.is_set_cddev + }) + this.runList = fx.runList + if (fx.changeCDStrongList) { + this.changeCD.req_info.strong_list = fx.changeCDStrongList + this.default_cd = fx.changeCDStrongList } - if (this.printer_info.is_set_cddev == 1) { + if (fx.shouldRedirectCddev) { if (this.is_set_cddev) return this.is_set_cddev = true this.goSet('/manage?type=cddev') @@ -989,7 +905,7 @@ export default { }) .then(({ value }) => { that - .$runCmd(`bash ${that.activePath} ${value}`) + .$runCmd(`bash "${that.activePath}" '${String(value).replace(/'/g, `'\\''`)}'`) .then((res) => { }) diff --git a/src/renderer/views/login/index.vue b/src/renderer/views/login/index.vue index 09b37df..29c9f55 100644 --- a/src/renderer/views/login/index.vue +++ b/src/renderer/views/login/index.vue @@ -66,6 +66,7 @@