新增控制台界面与相关资源,修复激活命令误判失败,并兼容 package 版本与 CardsoonServer 打包路径。
Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin')
|
|||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
const { VueLoaderPlugin } = require('vue-loader')
|
const { VueLoaderPlugin } = require('vue-loader')
|
||||||
|
const sassLoaders = require('./sass-loader-config')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of node_modules to include in webpack bundle
|
* List of node_modules to include in webpack bundle
|
||||||
@@ -45,11 +46,11 @@ let rendererConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
|
use: ['vue-style-loader', 'css-loader', 'postcss-loader', sassLoaders.scss]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.sass$/,
|
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$/,
|
test: /\.less$/,
|
||||||
@@ -79,8 +80,8 @@ let rendererConfig = {
|
|||||||
options: {
|
options: {
|
||||||
extractCSS: process.env.NODE_ENV === 'production',
|
extractCSS: process.env.NODE_ENV === 'production',
|
||||||
loaders: {
|
loaders: {
|
||||||
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
|
sass: sassLoaders.vueSassLoaders,
|
||||||
scss: 'vue-style-loader!css-loader!sass-loader',
|
scss: sassLoaders.vueScssLoaders,
|
||||||
less: 'vue-style-loader!css-loader!less-loader'
|
less: 'vue-style-loader!css-loader!less-loader'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin')
|
|||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
const { VueLoaderPlugin } = require('vue-loader')
|
const { VueLoaderPlugin } = require('vue-loader')
|
||||||
|
const sassLoaders = require('./sass-loader-config')
|
||||||
|
|
||||||
let webConfig = {
|
let webConfig = {
|
||||||
devtool: '#cheap-module-eval-source-map',
|
devtool: '#cheap-module-eval-source-map',
|
||||||
@@ -33,11 +34,11 @@ let webConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
|
use: ['vue-style-loader', 'css-loader', 'postcss-loader', sassLoaders.scss]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.sass$/,
|
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$/,
|
test: /\.less$/,
|
||||||
@@ -64,8 +65,8 @@ let webConfig = {
|
|||||||
options: {
|
options: {
|
||||||
extractCSS: true,
|
extractCSS: true,
|
||||||
loaders: {
|
loaders: {
|
||||||
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
|
sass: sassLoaders.vueSassLoaders,
|
||||||
scss: 'vue-style-loader!css-loader!sass-loader',
|
scss: sassLoaders.vueScssLoaders,
|
||||||
less: 'vue-style-loader!css-loader!less-loader'
|
less: 'vue-style-loader!css-loader!less-loader'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 648 B |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 257 KiB After Width: | Height: | Size: 350 KiB |
@@ -1,5 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
NODE_ENV: '"development"',
|
NODE_ENV: '"development"',
|
||||||
BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"',
|
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"'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
NODE_ENV: '"production"',
|
NODE_ENV: '"production"',
|
||||||
BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"',
|
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"'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "discworker",
|
"name": "discworker",
|
||||||
"version": "0.0.1",
|
"version": "1.1.1.10",
|
||||||
"author": "24kycj <1637269896@qq.com>",
|
"author": "24kycj <1637269896@qq.com>",
|
||||||
"description": "KaShu SoonWorkerDisk",
|
"description": "KaShu SoonWorkerDisk",
|
||||||
"homepage": "https://www.cardsoon.com/index.php/cn",
|
"homepage": "https://www.cardsoon.com/index.php/cn",
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
"to": "."
|
"to": "."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "./cardsoonServer",
|
"from": "./CardsoonServer",
|
||||||
"to": "."
|
"to": "CardsoonServer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "./help",
|
"from": "./help",
|
||||||
@@ -73,6 +73,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.1.2",
|
"@electron/remote": "^2.1.2",
|
||||||
|
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"archiver-zip-encrypted": "^2.0.0",
|
"archiver-zip-encrypted": "^2.0.0",
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>DiscWorker V1.01</title>
|
<title>DiscWorker V1.01</title>
|
||||||
|
<link rel="icon" href="<%= process.env.NODE_ENV === 'development' ? '/static/favicon.ico' : './static/favicon.ico' %>" type="image/x-icon">
|
||||||
<% if (typeof isWeb === 'undefined' ? htmlWebpackPlugin.options.nodeModules : !isWeb && htmlWebpackPlugin.options.nodeModules) { %>
|
<% if (typeof isWeb === 'undefined' ? htmlWebpackPlugin.options.nodeModules : !isWeb && htmlWebpackPlugin.options.nodeModules) { %>
|
||||||
<!-- Add `node_modules/` to global paths so `require` works properly in development (Electron only) -->
|
<!-- Add `node_modules/` to global paths so `require` works properly in development (Electron only) -->
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ const root = process.env.NODE_ENV === "development"
|
|||||||
? path.resolve(__dirname, '../../')
|
? path.resolve(__dirname, '../../')
|
||||||
: path.dirname(app.getPath("exe"))
|
: path.dirname(app.getPath("exe"))
|
||||||
|
|
||||||
|
function getAppIcon() {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
return path.join(root, 'src/renderer/assets/images/logo.png')
|
||||||
|
}
|
||||||
|
return path.join(__dirname, 'static/icon.png')
|
||||||
|
}
|
||||||
|
|
||||||
// 日志函数
|
// 日志函数
|
||||||
function writeLog(message) {
|
function writeLog(message) {
|
||||||
console.log(`[${new Date().toISOString()}] ${message}`)
|
console.log(`[${new Date().toISOString()}] ${message}`)
|
||||||
@@ -38,6 +45,7 @@ function createWindow () {
|
|||||||
useContentSize: true,
|
useContentSize: true,
|
||||||
show: false,
|
show: false,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
icon: getAppIcon(),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
webSecurity: false,
|
webSecurity: false,
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
@@ -56,7 +64,12 @@ function createWindow () {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', createWindow)
|
app.on('ready', () => {
|
||||||
|
if (process.platform === 'darwin' && app.dock) {
|
||||||
|
app.dock.setIcon(getAppIcon())
|
||||||
|
}
|
||||||
|
createWindow()
|
||||||
|
})
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
|
|||||||
|
After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1,781 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<dw-card class="device-panel" flex-body :title="''">
|
||||||
|
|
||||||
|
<template #header>
|
||||||
|
|
||||||
|
<div class="device-panel__head">
|
||||||
|
|
||||||
|
<console-icon name="device" :size="16" />
|
||||||
|
|
||||||
|
<span>设备状态</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dw-status-pill :variant="tagVariant" :label="tagLabel" />
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div v-if="loading" class="device-panel__skeleton">
|
||||||
|
|
||||||
|
<div class="dw-skeleton device-panel__sk-preview" />
|
||||||
|
|
||||||
|
<div class="dw-skeleton" style="height:88px;margin-bottom:12px" />
|
||||||
|
|
||||||
|
<div class="dw-skeleton" style="height:96px" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
|
||||||
|
<div class="device-panel__preview" :class="{ 'device-panel__preview--offline': !connected }">
|
||||||
|
|
||||||
|
<span class="device-panel__online-dot" :class="{ 'device-panel__online-dot--off': !online }" />
|
||||||
|
|
||||||
|
<img class="device-panel__img" :src="deviceImg" alt="device" />
|
||||||
|
|
||||||
|
<div class="device-panel__name">{{ deviceTitle }}</div>
|
||||||
|
|
||||||
|
<div class="device-panel__sn">序列号: {{ serialLabel }}</div>
|
||||||
|
|
||||||
|
<div v-if="!connected" class="device-panel__offline-tip">
|
||||||
|
|
||||||
|
<console-icon name="wifi" :size="14" />
|
||||||
|
|
||||||
|
请确认服务已启动并已联机
|
||||||
|
|
||||||
|
<button type="button" class="device-panel__refresh" @click="$emit('refresh')">刷新</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="device-panel__section">
|
||||||
|
|
||||||
|
<div class="device-panel__section-title">
|
||||||
|
|
||||||
|
<console-icon name="ink" :size="14" />
|
||||||
|
|
||||||
|
墨水余量
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ink-levels
|
||||||
|
|
||||||
|
v-if="connected && hasInkData"
|
||||||
|
|
||||||
|
:blue="printerInfo.blue_last"
|
||||||
|
|
||||||
|
:red="printerInfo.red_last"
|
||||||
|
|
||||||
|
:yellow="printerInfo.yellow_last"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-else class="device-panel__placeholder">联机后显示墨水余量</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="device-panel__section">
|
||||||
|
|
||||||
|
<div class="device-panel__section-title">
|
||||||
|
|
||||||
|
<console-icon name="bin" :size="14" />
|
||||||
|
|
||||||
|
盘仓监控
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<disc-bins v-if="discBins.length" :bins="discBins" />
|
||||||
|
|
||||||
|
<div v-else class="device-panel__placeholder">联机后显示盘仓容量</div>
|
||||||
|
|
||||||
|
<div v-if="showSureBar && changeCD.req_info" class="device-panel__cd-types">
|
||||||
|
|
||||||
|
<div
|
||||||
|
|
||||||
|
v-for="(item, index) in changeCD.req_info.strong_list"
|
||||||
|
|
||||||
|
:key="index"
|
||||||
|
|
||||||
|
class="device-panel__cd-type-row"
|
||||||
|
|
||||||
|
>
|
||||||
|
|
||||||
|
<span>盘仓 {{ item.strong_pos }}</span>
|
||||||
|
|
||||||
|
<el-select
|
||||||
|
|
||||||
|
size="mini"
|
||||||
|
|
||||||
|
:value="item.strong_type"
|
||||||
|
|
||||||
|
@change="(v) => $emit('command-cd', v, item.strong_pos)"
|
||||||
|
|
||||||
|
>
|
||||||
|
|
||||||
|
<el-option
|
||||||
|
|
||||||
|
v-for="opt in cdTypeOptions"
|
||||||
|
|
||||||
|
:key="opt.value"
|
||||||
|
|
||||||
|
:label="opt.label"
|
||||||
|
|
||||||
|
:value="opt.value"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div v-if="showSureBar" class="device-panel__sure">
|
||||||
|
|
||||||
|
<span>{{ sureText }}</span>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<button type="button" class="dw-btn dw-btn--ghost" @click="$emit('cancel-cd')">取消</button>
|
||||||
|
|
||||||
|
<button type="button" class="dw-btn dw-btn--primary" @click="$emit('confirm-cd')">确定</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<details v-if="connected" class="device-panel__details">
|
||||||
|
|
||||||
|
<summary>设备详情</summary>
|
||||||
|
|
||||||
|
<div class="device-panel__meta">
|
||||||
|
|
||||||
|
<div>主机名:{{ printerInfo.system_name || '—' }}</div>
|
||||||
|
|
||||||
|
<div>主机IP:{{ printerInfo.system_ip || '—' }}</div>
|
||||||
|
|
||||||
|
<div>缓存:{{ cacheText }}</div>
|
||||||
|
|
||||||
|
<div>状态:{{ runText }}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
|
||||||
|
<button type="button" class="dw-btn dw-btn--primary device-panel__cta" @click="$emit('new-work')">
|
||||||
|
|
||||||
|
<console-icon name="plus" :size="16" />
|
||||||
|
|
||||||
|
新建刻录作业
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<el-dropdown trigger="click" @command="(cmd) => $emit('maintain', cmd)">
|
||||||
|
|
||||||
|
<button type="button" class="dw-btn dw-btn--outline device-panel__more">
|
||||||
|
|
||||||
|
<console-icon name="settings" :size="14" />
|
||||||
|
|
||||||
|
更多维护
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
|
||||||
|
<el-dropdown-item v-for="item in runList" :key="item.value" :command="item.value">
|
||||||
|
|
||||||
|
{{ item.label }}
|
||||||
|
|
||||||
|
</el-dropdown-item>
|
||||||
|
|
||||||
|
</el-dropdown-menu>
|
||||||
|
|
||||||
|
</el-dropdown>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</dw-card>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import DwCard from '../ui/DwCard.vue'
|
||||||
|
|
||||||
|
import DwStatusPill from '../ui/DwStatusPill.vue'
|
||||||
|
|
||||||
|
import ConsoleIcon from '../icons/ConsoleIcon.vue'
|
||||||
|
|
||||||
|
import InkLevels from './InkLevels.vue'
|
||||||
|
|
||||||
|
import DiscBins from './DiscBins.vue'
|
||||||
|
|
||||||
|
import { getDeviceImages, resolveDeviceImageKey } from '@/utils/console/constants'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'DevicePanel',
|
||||||
|
|
||||||
|
components: { DwCard, DwStatusPill, ConsoleIcon, InkLevels, DiscBins },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
printerInfo: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
runText: { type: String, default: '' },
|
||||||
|
|
||||||
|
tagVariant: { type: String, default: 'idle' },
|
||||||
|
|
||||||
|
tagLabel: { type: String, default: '空闲' },
|
||||||
|
|
||||||
|
online: { type: Boolean, default: false },
|
||||||
|
|
||||||
|
loading: { type: Boolean, default: false },
|
||||||
|
|
||||||
|
runList: { type: Array, default: () => [] },
|
||||||
|
|
||||||
|
currentRunVal: { type: [Number, String], default: 0 },
|
||||||
|
|
||||||
|
sureText: { type: String, default: '' },
|
||||||
|
|
||||||
|
changeCD: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
cdTypeOptions: { type: Array, default: () => [] },
|
||||||
|
|
||||||
|
CDTypes: { type: Object, default: () => ({}) }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
connected() {
|
||||||
|
|
||||||
|
return !!(this.printerInfo && this.printerInfo.printer_name)
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
hasInkData() {
|
||||||
|
|
||||||
|
const p = this.printerInfo
|
||||||
|
|
||||||
|
return p.blue_last != null || p.red_last != null || p.yellow_last != null
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
deviceImages() {
|
||||||
|
|
||||||
|
return getDeviceImages()
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
deviceImg() {
|
||||||
|
|
||||||
|
const key = resolveDeviceImageKey(this.printerInfo.printer_name)
|
||||||
|
|
||||||
|
const imgs = this.deviceImages[key]
|
||||||
|
|
||||||
|
if (!imgs) return ''
|
||||||
|
|
||||||
|
return this.connected && this.printerInfo.cover_flag ? imgs.img2 : imgs.img1
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
deviceTitle() {
|
||||||
|
|
||||||
|
const p = this.printerInfo.printer_path
|
||||||
|
|
||||||
|
if (!this.connected) return 'TF-Publish 4200 系列'
|
||||||
|
|
||||||
|
const n = this.printerInfo.printer_name || ''
|
||||||
|
|
||||||
|
if (n === '4200' || n === 'DP-4200') {
|
||||||
|
|
||||||
|
return `TF-Publish 4200${p ? ` (${p})` : ''}`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return `国产光盘刻录一体机 ${n}${p ? ` (${p})` : ''}`
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
serialLabel() {
|
||||||
|
|
||||||
|
return (this.connected && this.printerInfo.serial_no) || '—'
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
cacheText() {
|
||||||
|
|
||||||
|
const free = this.printerInfo.free_cache_space
|
||||||
|
|
||||||
|
const total = this.printerInfo.total_cache_space
|
||||||
|
|
||||||
|
if (!total) return '—'
|
||||||
|
|
||||||
|
return `${this.$filterSize(free)} / ${this.$filterSize(total)}`
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
showSureBar() {
|
||||||
|
|
||||||
|
return parseInt(this.currentRunVal) === 6
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
discBins() {
|
||||||
|
|
||||||
|
if (!this.connected) return []
|
||||||
|
|
||||||
|
const info = this.printerInfo
|
||||||
|
|
||||||
|
const key = resolveDeviceImageKey(info.printer_name)
|
||||||
|
|
||||||
|
const limits = this.deviceImages[key] || this.deviceImages['4200']
|
||||||
|
|
||||||
|
const sl = info.strongList || this.buildStrongList(info.strong_list)
|
||||||
|
|
||||||
|
const build = (pos, labelKey) => {
|
||||||
|
|
||||||
|
const row = sl[pos]
|
||||||
|
|
||||||
|
if (!row) return null
|
||||||
|
|
||||||
|
const count = row.cd_num || 0
|
||||||
|
|
||||||
|
const percent = Math.min(100, (count / 50) * 100)
|
||||||
|
|
||||||
|
let levelClass = ''
|
||||||
|
|
||||||
|
if (count < limits.num2) levelClass = 'disc-bins__stack--warn'
|
||||||
|
|
||||||
|
if (count < limits.num1) levelClass = 'disc-bins__stack--danger'
|
||||||
|
|
||||||
|
const typeLabel = this.CDTypes[row.cd_type] || '光盘'
|
||||||
|
|
||||||
|
return { key: labelKey, label: typeLabel, count, percent, levelClass }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return [build(1, 'l'), build(2, 'r')].filter(Boolean)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
buildStrongList(list) {
|
||||||
|
|
||||||
|
if (!list || !list.length) return {}
|
||||||
|
|
||||||
|
const out = {}
|
||||||
|
|
||||||
|
list.forEach((item) => {
|
||||||
|
|
||||||
|
out[item.strong_pos] = item
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.device-panel {
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-card);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__head {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
font-size: 15px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
color: var(--dw-text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__sk-preview {
|
||||||
|
|
||||||
|
height: 160px;
|
||||||
|
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-md);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__preview {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
padding: 18px 16px 14px;
|
||||||
|
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
background: var(--dw-gradient-device);
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-md);
|
||||||
|
|
||||||
|
border: 1px solid rgba(30, 79, 217, 0.08);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--offline {
|
||||||
|
|
||||||
|
.device-panel__img {
|
||||||
|
|
||||||
|
opacity: 0.72;
|
||||||
|
|
||||||
|
filter: grayscale(0.25);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__online-dot {
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: 12px;
|
||||||
|
|
||||||
|
right: 12px;
|
||||||
|
|
||||||
|
width: 10px;
|
||||||
|
|
||||||
|
height: 10px;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
background: var(--dw-success);
|
||||||
|
|
||||||
|
box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.15);
|
||||||
|
|
||||||
|
animation: dw-pulse 2s infinite;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--off {
|
||||||
|
|
||||||
|
background: var(--dw-text-muted);
|
||||||
|
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
animation: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
|
||||||
|
max-width: 168px;
|
||||||
|
|
||||||
|
max-height: 120px;
|
||||||
|
|
||||||
|
object-fit: contain;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
line-height: 1.35;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__sn {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__offline-tip {
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
padding: 4px 10px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-warning);
|
||||||
|
|
||||||
|
background: var(--dw-warning-soft);
|
||||||
|
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__refresh {
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
padding: 0 2px;
|
||||||
|
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__section {
|
||||||
|
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__section-title {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
color: var(--dw-text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__placeholder {
|
||||||
|
|
||||||
|
padding: 14px 12px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-muted);
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
background: #f8fafc;
|
||||||
|
|
||||||
|
border: 1px dashed var(--dw-border);
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__cd-types {
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__cd-type-row {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__sure {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
background: var(--dw-primary-soft);
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__details {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
summary {
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__meta div {
|
||||||
|
|
||||||
|
line-height: 1.8;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__cta {
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
padding: 10px 14px;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__more {
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="disc-bins">
|
||||||
|
|
||||||
|
<div v-for="bin in bins" :key="bin.key" class="disc-bins__item">
|
||||||
|
|
||||||
|
<div class="disc-bins__box">
|
||||||
|
|
||||||
|
<div
|
||||||
|
|
||||||
|
class="disc-bins__stack"
|
||||||
|
|
||||||
|
:style="{ height: bin.percent + '%' }"
|
||||||
|
|
||||||
|
:class="bin.levelClass"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="disc-bins__label">{{ bin.label }}</div>
|
||||||
|
|
||||||
|
<div class="disc-bins__count">{{ bin.count }} 张</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'DiscBins',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
bins: { type: Array, default: () => [] }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.disc-bins {
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: 92px;
|
||||||
|
|
||||||
|
background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
|
||||||
|
|
||||||
|
border: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-md);
|
||||||
|
|
||||||
|
padding: 10px 14px 8px;
|
||||||
|
|
||||||
|
box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__stack {
|
||||||
|
|
||||||
|
width: 56%;
|
||||||
|
|
||||||
|
background: var(--dw-gradient-disc);
|
||||||
|
|
||||||
|
border-radius: 6px 6px 4px 4px;
|
||||||
|
|
||||||
|
min-height: 6px;
|
||||||
|
|
||||||
|
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.25);
|
||||||
|
|
||||||
|
transition: height 0.35s ease;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
|
||||||
|
content: '';
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: -3px;
|
||||||
|
|
||||||
|
left: 8%;
|
||||||
|
|
||||||
|
right: 8%;
|
||||||
|
|
||||||
|
height: 5px;
|
||||||
|
|
||||||
|
background: #94a3b8;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--warn {
|
||||||
|
|
||||||
|
background: linear-gradient(180deg, #fde68a 0%, #f59e0b 100%);
|
||||||
|
|
||||||
|
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.25);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
|
||||||
|
background: linear-gradient(180deg, #fecaca 0%, #ef4444 100%);
|
||||||
|
|
||||||
|
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.25);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
color: var(--dw-text);
|
||||||
|
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__count {
|
||||||
|
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
margin-top: 2px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="ink-levels">
|
||||||
|
|
||||||
|
<div v-for="item in items" :key="item.key" class="ink-levels__row">
|
||||||
|
|
||||||
|
<div class="ink-levels__label">
|
||||||
|
|
||||||
|
<span class="ink-levels__swatch" :style="{ background: item.color }" />
|
||||||
|
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
|
||||||
|
<span class="ink-levels__pct">{{ item.value }}%</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dw-progress-bar :value="item.value" :color="item.color" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import DwProgressBar from '../ui/DwProgressBar.vue'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'InkLevels',
|
||||||
|
|
||||||
|
components: { DwProgressBar },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
blue: { type: Number, default: 0 },
|
||||||
|
|
||||||
|
red: { type: Number, default: 0 },
|
||||||
|
|
||||||
|
yellow: { type: Number, default: 0 }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
items() {
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
{ key: 'c', label: 'Cyan (青)', value: parseInt(this.blue) || 0, color: '#06b6d4' },
|
||||||
|
|
||||||
|
{ key: 'm', label: 'Magenta (品红)', value: parseInt(this.red) || 0, color: '#ec4899' },
|
||||||
|
|
||||||
|
{ key: 'y', label: 'Yellow (黄)', value: parseInt(this.yellow) || 0, color: '#eab308' }
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.ink-levels {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__swatch {
|
||||||
|
|
||||||
|
width: 10px;
|
||||||
|
|
||||||
|
height: 10px;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.8), 0 1px 2px rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__pct {
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
color: var(--dw-text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="drive-card" :class="{ 'drive-card--burning': burning }">
|
||||||
|
|
||||||
|
<div class="drive-card__icon" :class="{ 'drive-card__icon--spin': burning }">
|
||||||
|
|
||||||
|
<console-icon v-if="burning" name="spinner" :size="22" spin />
|
||||||
|
|
||||||
|
<console-icon v-else name="disc" :size="22" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drive-card__body">
|
||||||
|
|
||||||
|
<div class="drive-card__top">
|
||||||
|
|
||||||
|
<div class="drive-card__title">{{ title }}</div>
|
||||||
|
|
||||||
|
<dw-status-pill :variant="statusVariant" :label="statusLabel" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="discTypeLabel" class="drive-card__type">{{ discTypeLabel }}</div>
|
||||||
|
|
||||||
|
<div v-if="burning" class="drive-card__progress">
|
||||||
|
|
||||||
|
<dw-progress-bar :value="progress" color="#059669" />
|
||||||
|
|
||||||
|
<div class="drive-card__meta">
|
||||||
|
|
||||||
|
已刻录 {{ usedSize }} / {{ totalSize }}
|
||||||
|
|
||||||
|
<span v-if="speed"> · 速度 {{ speed }}X</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="taskId" class="drive-card__task">
|
||||||
|
|
||||||
|
<span class="drive-card__task-label">任务</span>
|
||||||
|
|
||||||
|
{{ taskId }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="sim" class="drive-card__sim">模拟刻录</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ConsoleIcon from '../icons/ConsoleIcon.vue'
|
||||||
|
|
||||||
|
import DwStatusPill from '../ui/DwStatusPill.vue'
|
||||||
|
|
||||||
|
import DwProgressBar from '../ui/DwProgressBar.vue'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'DriveCard',
|
||||||
|
|
||||||
|
components: { ConsoleIcon, DwStatusPill, DwProgressBar },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
drive: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
CDStatus: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
CDName: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
CDTypes: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
diskTypes: { type: Object, default: () => ({}) }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
title() {
|
||||||
|
|
||||||
|
const path = this.drive.dev_path || '光驱'
|
||||||
|
|
||||||
|
const pos = this.CDName[this.drive.cd_pos] || this.fallbackPosLabel
|
||||||
|
|
||||||
|
return `${path} (${pos})`
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
fallbackPosLabel() {
|
||||||
|
|
||||||
|
return this.drive.cd_pos === 1 ? '下光驱' : this.drive.cd_pos === 2 ? '上光驱' : '光驱'
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
burning() {
|
||||||
|
|
||||||
|
return this.drive.copy_scheduler > 0
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
progress() {
|
||||||
|
|
||||||
|
return parseFloat(this.drive.copy_scheduler) || 0
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
statusLabel() {
|
||||||
|
|
||||||
|
if (this.burning) return `刻录中 ${this.progress}%`
|
||||||
|
|
||||||
|
return this.CDStatus[this.drive.cd_status] || '未知'
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
statusVariant() {
|
||||||
|
|
||||||
|
if (this.burning) return 'busy'
|
||||||
|
|
||||||
|
if (this.drive.cd_status === 1) return 'neutral'
|
||||||
|
|
||||||
|
if (this.drive.cd_status === 4) return 'online'
|
||||||
|
|
||||||
|
return 'idle'
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
discTypeLabel() {
|
||||||
|
|
||||||
|
if (!this.drive.cd_type) return ''
|
||||||
|
|
||||||
|
return this.CDTypes[this.drive.cd_type] || this.diskTypes[this.drive.cd_type] || ''
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
usedSize() {
|
||||||
|
|
||||||
|
return this.$filterSize(this.drive.use_size || 0)
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
totalSize() {
|
||||||
|
|
||||||
|
return this.$filterSize(this.drive.total_size || 0)
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
speed() {
|
||||||
|
|
||||||
|
return this.drive.burn_speed ? this.drive.burn_speed.toFixed(2) : ''
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
taskId() {
|
||||||
|
|
||||||
|
return this.drive.cd_taskid && this.burning ? this.drive.cd_taskid : ''
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
sim() {
|
||||||
|
|
||||||
|
return this.drive.copy_scheduler && this.drive.is_sim
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.drive-card {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
border: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-md);
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-sm);
|
||||||
|
|
||||||
|
transition: box-shadow 0.2s ease, border-color 0.2s ease;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-md);
|
||||||
|
|
||||||
|
border-color: rgba(30, 79, 217, 0.15);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--burning {
|
||||||
|
|
||||||
|
border-color: rgba(5, 150, 105, 0.25);
|
||||||
|
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #f0fdf4 100%);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
|
||||||
|
width: 52px;
|
||||||
|
|
||||||
|
height: 52px;
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-md);
|
||||||
|
|
||||||
|
background: linear-gradient(145deg, #eff6ff 0%, #dbeafe 100%);
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
border: 1px solid rgba(30, 79, 217, 0.12);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--spin {
|
||||||
|
|
||||||
|
color: var(--dw-success);
|
||||||
|
|
||||||
|
background: linear-gradient(145deg, #ecfdf5 0%, #d1fae5 100%);
|
||||||
|
|
||||||
|
border-color: rgba(5, 150, 105, 0.2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__top {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
line-height: 1.35;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__type {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__progress {
|
||||||
|
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__meta {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
margin-top: 6px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__task {
|
||||||
|
|
||||||
|
margin-top: 6px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__task-label {
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
padding: 1px 6px;
|
||||||
|
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
background: #f3f4f6;
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__sim {
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
padding: 2px 8px;
|
||||||
|
|
||||||
|
background: var(--dw-warning-soft);
|
||||||
|
|
||||||
|
color: var(--dw-warning);
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<dw-card class="drive-monitor" :title="''">
|
||||||
|
|
||||||
|
<template #header>
|
||||||
|
|
||||||
|
<div class="drive-monitor__head">
|
||||||
|
|
||||||
|
<console-icon name="drive" :size="16" />
|
||||||
|
|
||||||
|
<span>光驱监控</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-if="loading" class="drive-monitor__grid">
|
||||||
|
|
||||||
|
<div class="dw-skeleton" style="height:120px" />
|
||||||
|
|
||||||
|
<div class="dw-skeleton" style="height:120px" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dw-empty v-else-if="!cdList.length" scene="drives" title="无光驱数据" description="联机后将显示上下光驱状态" />
|
||||||
|
|
||||||
|
<div v-else class="drive-monitor__grid">
|
||||||
|
|
||||||
|
<drive-card
|
||||||
|
|
||||||
|
v-for="(item, index) in cdList"
|
||||||
|
|
||||||
|
:key="index"
|
||||||
|
|
||||||
|
:drive="item"
|
||||||
|
|
||||||
|
:CDStatus="CDStatus"
|
||||||
|
|
||||||
|
:CDName="CDName"
|
||||||
|
|
||||||
|
:CDTypes="CDTypes"
|
||||||
|
|
||||||
|
:diskTypes="diskTypes"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</dw-card>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import DwCard from '../ui/DwCard.vue'
|
||||||
|
|
||||||
|
import DwEmpty from '../ui/DwEmpty.vue'
|
||||||
|
|
||||||
|
import ConsoleIcon from '../icons/ConsoleIcon.vue'
|
||||||
|
|
||||||
|
import DriveCard from './DriveCard.vue'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'DriveMonitor',
|
||||||
|
|
||||||
|
components: { DwCard, DwEmpty, ConsoleIcon, DriveCard },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
cdList: { type: Array, default: () => [] },
|
||||||
|
|
||||||
|
CDStatus: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
CDName: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
CDTypes: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
diskTypes: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
loading: { type: Boolean, default: false }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.drive-monitor {
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-card);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__head {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
font-size: 15px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__grid {
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<i
|
||||||
|
class="dw-icon"
|
||||||
|
:class="iconClasses"
|
||||||
|
:style="iconStyle"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const FA_MAP = {
|
||||||
|
disc: 'fa-compact-disc',
|
||||||
|
logo: 'fa-compact-disc',
|
||||||
|
drive: 'fa-hdd',
|
||||||
|
device: 'fa-print',
|
||||||
|
server: 'fa-server',
|
||||||
|
ink: 'fa-tint',
|
||||||
|
bin: 'fa-box',
|
||||||
|
folder: 'fa-folder-open',
|
||||||
|
settings: 'fa-cog',
|
||||||
|
plus: 'fa-plus',
|
||||||
|
tasks: 'fa-list',
|
||||||
|
logs: 'fa-stream',
|
||||||
|
spinner: 'fa-spinner',
|
||||||
|
offline: 'fa-circle-exclamation',
|
||||||
|
wifi: 'fa-wifi',
|
||||||
|
help: 'fa-circle-question',
|
||||||
|
user: 'fa-user',
|
||||||
|
refresh: 'fa-rotate-right'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConsoleIcon',
|
||||||
|
props: {
|
||||||
|
name: { type: String, required: true },
|
||||||
|
size: { type: [Number, String], default: 16 },
|
||||||
|
spin: { type: Boolean, default: false },
|
||||||
|
fixed: { type: Boolean, default: true }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
faClass() {
|
||||||
|
return FA_MAP[this.name] || 'fa-circle'
|
||||||
|
},
|
||||||
|
iconClasses() {
|
||||||
|
return [
|
||||||
|
this.fixed ? 'fa-solid' : 'fas',
|
||||||
|
this.faClass,
|
||||||
|
{ 'fa-spin': this.spin }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
iconStyle() {
|
||||||
|
const n = parseInt(this.size, 10) || 16
|
||||||
|
return { fontSize: `${n}px`, width: `${n}px`, lineHeight: `${n}px` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dw-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="console-header">
|
||||||
|
|
||||||
|
<div class="console-header__brand">
|
||||||
|
|
||||||
|
<console-icon name="logo" :size="22" class="console-header__logo-icon" />
|
||||||
|
<img class="console-header__logo" src="@/assets/images/logo.png" alt="logo" />
|
||||||
|
<div>
|
||||||
|
<div class="console-header__title">DiscWorker 控制台</div>
|
||||||
|
<div class="console-header__sub">国产光盘打印刻录系统 v2.0</div>
|
||||||
|
<div v-if="previewMode" class="console-header__preview-tag">预览模式</div>
|
||||||
|
<div v-else-if="demoMode" class="console-header__preview-tag console-header__preview-tag--demo">设计模式 · Mock 数据</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="console-header__center">
|
||||||
|
|
||||||
|
<dw-status-pill :variant="connectionVariant" :label="connectionLabel" dot />
|
||||||
|
|
||||||
|
<dw-kpi-chip label="完成" :value="taskNum[0]" variant="success" icon="check" />
|
||||||
|
|
||||||
|
<dw-kpi-chip label="废弃" :value="taskNum[1]" variant="muted" />
|
||||||
|
|
||||||
|
<dw-kpi-chip label="错误" :value="taskNum[2]" variant="danger" icon="alert" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="console-header__actions">
|
||||||
|
|
||||||
|
<template v-if="printerInfo.test_use > -1 && printerInfo.test_use !== 255">
|
||||||
|
|
||||||
|
<span class="console-header__trial">试用版 {{ printerInfo.test_use }} 天</span>
|
||||||
|
|
||||||
|
<button v-if="hasRunCmd" type="button" class="dw-btn dw-btn--ghost" @click="$emit('activate')">激活</button>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<button type="button" class="dw-btn dw-btn--outline" @click="$emit('open-work')">
|
||||||
|
|
||||||
|
<console-icon name="folder" :size="15" />
|
||||||
|
|
||||||
|
打开文件
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="dw-btn dw-btn--outline" @click="$emit('go-settings')">
|
||||||
|
|
||||||
|
<console-icon name="settings" :size="15" />
|
||||||
|
|
||||||
|
设置
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<el-dropdown trigger="click" @command="handleUserCommand">
|
||||||
|
|
||||||
|
<button type="button" class="console-header__user">
|
||||||
|
|
||||||
|
<span class="console-header__avatar">{{ userInitial }}</span>
|
||||||
|
|
||||||
|
<span>{{ userName }}</span>
|
||||||
|
|
||||||
|
<i class="el-icon-arrow-down" />
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
|
||||||
|
<el-dropdown-item v-if="hasRunCmd" command="toggle-service">
|
||||||
|
|
||||||
|
{{ workStatus ? '关闭服务' : '开启服务' }}
|
||||||
|
|
||||||
|
</el-dropdown-item>
|
||||||
|
|
||||||
|
<el-dropdown-item v-else disabled>网页端(无服务控制)</el-dropdown-item>
|
||||||
|
|
||||||
|
<el-dropdown-item command="legacy">切换旧版界面</el-dropdown-item>
|
||||||
|
|
||||||
|
<el-dropdown-item divided command="logout">登出</el-dropdown-item>
|
||||||
|
|
||||||
|
</el-dropdown-menu>
|
||||||
|
|
||||||
|
</el-dropdown>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ConsoleIcon from '../icons/ConsoleIcon.vue'
|
||||||
|
|
||||||
|
import DwStatusPill from '../ui/DwStatusPill.vue'
|
||||||
|
|
||||||
|
import DwKpiChip from '../ui/DwKpiChip.vue'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'ConsoleHeader',
|
||||||
|
|
||||||
|
components: { ConsoleIcon, DwStatusPill, DwKpiChip },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
userName: { type: String, default: 'Admin' },
|
||||||
|
|
||||||
|
taskNum: { type: Array, default: () => [0, 0, 0] },
|
||||||
|
|
||||||
|
workStatus: { type: Boolean, default: false },
|
||||||
|
|
||||||
|
hasRunCmd: { type: Boolean, default: false },
|
||||||
|
|
||||||
|
previewMode: { type: Boolean, default: false },
|
||||||
|
demoMode: { type: Boolean, default: false },
|
||||||
|
printerInfo: { type: Object, default: () => ({}) },
|
||||||
|
|
||||||
|
isConnect: { type: Boolean, default: false },
|
||||||
|
|
||||||
|
serviceStatus: { type: Boolean, default: false }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
userInitial() {
|
||||||
|
|
||||||
|
return (this.userName || 'A').charAt(0).toUpperCase()
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
connectionVariant() {
|
||||||
|
|
||||||
|
if (!this.isConnect || !this.serviceStatus) return 'error'
|
||||||
|
|
||||||
|
return 'online'
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
connectionLabel() {
|
||||||
|
if (this.demoMode && this.isConnect && this.serviceStatus) return '系统联机'
|
||||||
|
if (!this.hasRunCmd) return '网页端'
|
||||||
|
if (!this.isConnect || !this.serviceStatus) return '未联机'
|
||||||
|
return '系统联机'
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
handleUserCommand(cmd) {
|
||||||
|
|
||||||
|
this.$emit('user-command', cmd)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.console-header {
|
||||||
|
|
||||||
|
height: var(--dw-header-h);
|
||||||
|
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
background: var(--dw-surface);
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-sm);
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__brand {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__logo-icon {
|
||||||
|
color: var(--dw-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__logo {
|
||||||
|
|
||||||
|
width: 36px;
|
||||||
|
|
||||||
|
height: 36px;
|
||||||
|
|
||||||
|
object-fit: contain;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
line-height: 1.2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__sub {
|
||||||
|
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
color: var(--dw-text-muted);
|
||||||
|
|
||||||
|
margin-top: 1px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__preview-tag {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--dw-warning);
|
||||||
|
background: var(--dw-warning-soft);
|
||||||
|
|
||||||
|
&--demo {
|
||||||
|
color: var(--dw-info);
|
||||||
|
background: var(--dw-info-soft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__center {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__trial {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
padding: 4px 8px;
|
||||||
|
|
||||||
|
background: #f9fafb;
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
|
||||||
|
margin-right: 2px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__user {
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
border: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
|
||||||
|
padding: 4px 10px 4px 4px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
transition: border-color 0.15s ease;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
|
||||||
|
border-color: rgba(30, 79, 217, 0.35);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__avatar {
|
||||||
|
|
||||||
|
width: 28px;
|
||||||
|
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
background: linear-gradient(135deg, #dbeafe 0%, #eff6ff 100%);
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
border: 1px solid rgba(30, 79, 217, 0.15);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<div class="console-shell">
|
||||||
|
<console-header
|
||||||
|
:user-name="userName"
|
||||||
|
:task-num="taskNum"
|
||||||
|
:work-status="workStatus"
|
||||||
|
:has-run-cmd="hasRunCmd"
|
||||||
|
:preview-mode="previewMode"
|
||||||
|
:demo-mode="demoMode"
|
||||||
|
:printer-info="printerInfo"
|
||||||
|
:is-connect="isConnect"
|
||||||
|
:service-status="serviceStatus"
|
||||||
|
@open-work="$emit('open-work')"
|
||||||
|
@go-settings="$emit('go-settings')"
|
||||||
|
@activate="$emit('activate')"
|
||||||
|
@user-command="$emit('user-command', $event)"
|
||||||
|
/>
|
||||||
|
<main class="console-main">
|
||||||
|
<div class="console-main__device">
|
||||||
|
<device-panel
|
||||||
|
:printer-info="printerInfo"
|
||||||
|
:run-text="runText"
|
||||||
|
:tag-variant="tagVariant"
|
||||||
|
:tag-label="tagLabel"
|
||||||
|
:online="workStatus"
|
||||||
|
:loading="loading"
|
||||||
|
:run-list="runList"
|
||||||
|
:current-run-val="currentRunVal"
|
||||||
|
:sure-text="sureText"
|
||||||
|
:change-c-d="changeCD"
|
||||||
|
:cd-type-options="cdTypeOptions"
|
||||||
|
:CDTypes="CDTypes"
|
||||||
|
@new-work="$emit('new-work')"
|
||||||
|
@maintain="$emit('maintain', $event)"
|
||||||
|
@command-cd="(e, type) => $emit('command-cd', e, type)"
|
||||||
|
@confirm-cd="$emit('confirm-cd')"
|
||||||
|
@cancel-cd="$emit('cancel-cd')"
|
||||||
|
@refresh="$emit('refresh')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="console-main__drive">
|
||||||
|
<drive-monitor
|
||||||
|
:cd-list="cdList"
|
||||||
|
:CDStatus="CDStatus"
|
||||||
|
:CDName="CDName"
|
||||||
|
:CDTypes="CDTypes"
|
||||||
|
:diskTypes="diskTypes"
|
||||||
|
:loading="loading"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="console-main__task">
|
||||||
|
<task-workspace
|
||||||
|
:task-list="taskList"
|
||||||
|
:log-list="logList"
|
||||||
|
:show-all-logs="showAllLogs"
|
||||||
|
@retry="$emit('retry', $event)"
|
||||||
|
@cancel="$emit('cancel-task', $event)"
|
||||||
|
@toggle-logs="$emit('toggle-logs')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<console-status-bar
|
||||||
|
:serial-no="printerInfo.serial_no"
|
||||||
|
:cache-text="cacheText"
|
||||||
|
@help="$emit('help')"
|
||||||
|
@about="$emit('about')"
|
||||||
|
/>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConsoleHeader from './ConsoleHeader.vue'
|
||||||
|
import ConsoleStatusBar from './ConsoleStatusBar.vue'
|
||||||
|
import DevicePanel from '../device/DevicePanel.vue'
|
||||||
|
import DriveMonitor from '../drive/DriveMonitor.vue'
|
||||||
|
import TaskWorkspace from '../task/TaskWorkspace.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConsoleShell',
|
||||||
|
components: {
|
||||||
|
ConsoleHeader,
|
||||||
|
ConsoleStatusBar,
|
||||||
|
DevicePanel,
|
||||||
|
DriveMonitor,
|
||||||
|
TaskWorkspace
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
previewMode: { type: Boolean, default: false },
|
||||||
|
demoMode: { type: Boolean, default: false },
|
||||||
|
userName: { type: String, default: '' },
|
||||||
|
taskNum: { type: Array, default: () => [0, 0, 0] },
|
||||||
|
workStatus: { type: Boolean, default: false },
|
||||||
|
hasRunCmd: { type: Boolean, default: false },
|
||||||
|
isConnect: { type: Boolean, default: false },
|
||||||
|
serviceStatus: { type: Boolean, default: false },
|
||||||
|
printerInfo: { type: Object, default: () => ({}) },
|
||||||
|
runText: { type: String, default: '' },
|
||||||
|
tagVariant: { type: String, default: 'idle' },
|
||||||
|
tagLabel: { type: String, default: '' },
|
||||||
|
loading: { type: Boolean, default: false },
|
||||||
|
runList: { type: Array, default: () => [] },
|
||||||
|
currentRunVal: { type: [Number, String], default: 0 },
|
||||||
|
sureText: { type: String, default: '' },
|
||||||
|
changeCD: { type: Object, default: () => ({}) },
|
||||||
|
cdTypeOptions: { type: Array, default: () => [] },
|
||||||
|
cdList: { type: Array, default: () => [] },
|
||||||
|
CDStatus: { type: Object, default: () => ({}) },
|
||||||
|
CDName: { type: Object, default: () => ({}) },
|
||||||
|
CDTypes: { type: Object, default: () => ({}) },
|
||||||
|
diskTypes: { type: Object, default: () => ({}) },
|
||||||
|
taskList: { type: Array, default: () => [] },
|
||||||
|
logList: { type: Array, default: () => [] },
|
||||||
|
showAllLogs: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
cacheText() {
|
||||||
|
const p = this.printerInfo
|
||||||
|
if (!p.total_cache_space) return ''
|
||||||
|
return `缓存 ${this.$filterSize(p.free_cache_space)} / ${this.$filterSize(p.total_cache_space)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.console-shell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="console-statusbar">
|
||||||
|
|
||||||
|
<div class="console-statusbar__left">
|
||||||
|
|
||||||
|
<span v-if="serialNo" class="console-statusbar__item">SN: {{ serialNo }}</span>
|
||||||
|
|
||||||
|
<span v-if="cacheText" class="console-statusbar__item">{{ cacheText }}</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="console-statusbar__links">
|
||||||
|
|
||||||
|
<a href="#" @click.prevent="$emit('help')">
|
||||||
|
|
||||||
|
<console-icon name="help" :size="13" />
|
||||||
|
|
||||||
|
帮助
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<span class="console-statusbar__sep">|</span>
|
||||||
|
|
||||||
|
<a href="#" @click.prevent="$emit('about')">关于</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ConsoleIcon from '../icons/ConsoleIcon.vue'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'ConsoleStatusBar',
|
||||||
|
|
||||||
|
components: { ConsoleIcon },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
serialNo: { type: String, default: '' },
|
||||||
|
|
||||||
|
cacheText: { type: String, default: '' }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.console-statusbar {
|
||||||
|
|
||||||
|
height: var(--dw-statusbar-h);
|
||||||
|
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
background: var(--dw-surface);
|
||||||
|
|
||||||
|
border-top: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-muted);
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__links {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
a {
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__sep {
|
||||||
|
|
||||||
|
margin: 0 6px;
|
||||||
|
|
||||||
|
opacity: 0.45;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div class="activity-log">
|
||||||
|
<div class="activity-log__toolbar">
|
||||||
|
<button type="button" class="dw-btn dw-btn--ghost" @click="$emit('toggle-all')">
|
||||||
|
{{ showAll ? '最新日志' : '全部日志' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="activity-log__list">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
class="activity-log__item"
|
||||||
|
:class="`activity-log__item--${(item.status || 'INFO').toLowerCase()}`"
|
||||||
|
>
|
||||||
|
<span class="activity-log__badge">{{ item.status || 'INFO' }}</span>
|
||||||
|
<span class="activity-log__text">{{ item.text }}</span>
|
||||||
|
<span class="activity-log__time">{{ item.time }}</span>
|
||||||
|
</div>
|
||||||
|
<dw-empty v-if="!list.length" scene="logs" title="暂无日志" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DwEmpty from '../ui/DwEmpty.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ActivityLog',
|
||||||
|
components: { DwEmpty },
|
||||||
|
props: {
|
||||||
|
list: { type: Array, default: () => [] },
|
||||||
|
showAll: { type: Boolean, default: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.activity-log {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
&__toolbar {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
background: var(--dw-danger-soft);
|
||||||
|
border-color: rgba(220, 38, 38, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--debug {
|
||||||
|
background: var(--dw-warning-soft);
|
||||||
|
border-color: rgba(217, 119, 6, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__badge {
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-width: 44px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--dw-info-soft);
|
||||||
|
color: var(--dw-info);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item--error &__badge {
|
||||||
|
background: rgba(220, 38, 38, 0.12);
|
||||||
|
color: var(--dw-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item--debug &__badge {
|
||||||
|
background: rgba(217, 119, 6, 0.12);
|
||||||
|
color: var(--dw-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__time {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="task-table-wrap">
|
||||||
|
|
||||||
|
<div class="task-table-wrap__toolbar">
|
||||||
|
|
||||||
|
<el-input
|
||||||
|
|
||||||
|
v-model="keyword"
|
||||||
|
|
||||||
|
size="small"
|
||||||
|
|
||||||
|
clearable
|
||||||
|
|
||||||
|
placeholder="搜索任务名称或 ID"
|
||||||
|
|
||||||
|
prefix-icon="el-icon-search"
|
||||||
|
|
||||||
|
class="task-table-wrap__search"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
|
||||||
|
row-key="id"
|
||||||
|
|
||||||
|
default-expand-all
|
||||||
|
|
||||||
|
class="task-table dw-console-table"
|
||||||
|
|
||||||
|
:data="filteredList"
|
||||||
|
|
||||||
|
height="100%"
|
||||||
|
|
||||||
|
stripe
|
||||||
|
|
||||||
|
:tree-props="{ children: 'ass_task', hasChildren: 'hasChildren' }"
|
||||||
|
|
||||||
|
>
|
||||||
|
|
||||||
|
<template slot="empty">
|
||||||
|
|
||||||
|
<dw-empty scene="tasks" title="暂无作业列表" description="点击左侧「新建刻录作业」开始第一个任务" />
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table-column prop="task_uuid" label="作业ID" min-width="130" show-overflow-tooltip />
|
||||||
|
|
||||||
|
<el-table-column prop="task_name" label="任务名称" min-width="150" show-overflow-tooltip />
|
||||||
|
|
||||||
|
<el-table-column prop="cdType" label="类型" width="88">
|
||||||
|
|
||||||
|
<template slot-scope="scope">
|
||||||
|
|
||||||
|
<span v-if="scope.row.cdType" class="task-table__type">{{ scope.row.cdType }}</span>
|
||||||
|
|
||||||
|
<span v-else>—</span>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="大小" width="96">
|
||||||
|
|
||||||
|
<template slot-scope="scope">{{ $filterSize(scope.row.task_size) }}</template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="start_time" label="开始时间" width="150" show-overflow-tooltip />
|
||||||
|
|
||||||
|
<el-table-column prop="finish_time" label="结束时间" width="150" show-overflow-tooltip />
|
||||||
|
|
||||||
|
<el-table-column label="完成度" width="120">
|
||||||
|
|
||||||
|
<template slot-scope="scope">
|
||||||
|
|
||||||
|
<template v-if="scope.row.task_uuid && isRunning(scope.row.task_status)">
|
||||||
|
|
||||||
|
<dw-progress-bar :value="taskProgress(scope.row)" color="#0284c7" />
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<span v-else-if="scope.row.task_uuid">{{ scope.row.finish_freq }}/{{ scope.row.total_freq }}</span>
|
||||||
|
|
||||||
|
<span v-else>—</span>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="状态" width="148" fixed="right">
|
||||||
|
|
||||||
|
<template slot-scope="scope">
|
||||||
|
|
||||||
|
<div v-if="scope.row.task_uuid && scope.row.task_status == 5" class="task-table__actions">
|
||||||
|
|
||||||
|
<dw-status-pill variant="error" :label="statusLabel(scope.row.task_status)" />
|
||||||
|
|
||||||
|
<el-button type="text" size="mini" icon="el-icon-refresh-right" @click="$emit('retry', scope.row)" />
|
||||||
|
|
||||||
|
<el-button type="text" size="mini" icon="el-icon-circle-close" @click="$emit('cancel', scope.row)" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dw-status-pill
|
||||||
|
|
||||||
|
v-else-if="scope.row.task_uuid"
|
||||||
|
|
||||||
|
:variant="statusVariant(scope.row.task_status)"
|
||||||
|
|
||||||
|
:label="statusLabel(scope.row.task_status)"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span v-else class="task-table__sub">{{ statusLabel(scope.row.task_status) }}</span>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="task-table-wrap__footer">
|
||||||
|
|
||||||
|
共 <strong>{{ filteredList.length }}</strong> 条作业
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import DwEmpty from '../ui/DwEmpty.vue'
|
||||||
|
|
||||||
|
import DwStatusPill from '../ui/DwStatusPill.vue'
|
||||||
|
|
||||||
|
import DwProgressBar from '../ui/DwProgressBar.vue'
|
||||||
|
|
||||||
|
import { TASK_STATUS_LABELS, taskStatusVariant } from '@/utils/console/constants'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'TaskTable',
|
||||||
|
|
||||||
|
components: { DwEmpty, DwStatusPill, DwProgressBar },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
taskList: { type: Array, default: () => [] }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
|
||||||
|
return { keyword: '' }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
filteredList() {
|
||||||
|
|
||||||
|
const kw = (this.keyword || '').trim().toLowerCase()
|
||||||
|
|
||||||
|
if (!kw) return this.taskList
|
||||||
|
|
||||||
|
return this.taskList.filter((row) => {
|
||||||
|
|
||||||
|
const name = (row.task_name || '').toLowerCase()
|
||||||
|
|
||||||
|
const id = (row.task_uuid || '').toLowerCase()
|
||||||
|
|
||||||
|
return name.includes(kw) || id.includes(kw)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
statusLabel(status) {
|
||||||
|
|
||||||
|
return TASK_STATUS_LABELS[status] || status
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
statusVariant(status) {
|
||||||
|
|
||||||
|
return taskStatusVariant(status)
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
isRunning(status) {
|
||||||
|
|
||||||
|
return [3, 4, 6].includes(parseInt(status, 10))
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
taskProgress(row) {
|
||||||
|
|
||||||
|
if (!row.total_freq) return 0
|
||||||
|
|
||||||
|
return Math.min(100, Math.round((row.finish_freq / row.total_freq) * 100))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.task-table-wrap {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__toolbar {
|
||||||
|
|
||||||
|
padding: 0 0 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__search {
|
||||||
|
|
||||||
|
max-width: 280px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-muted);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
strong {
|
||||||
|
|
||||||
|
color: var(--dw-text);
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.task-table {
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.task-table__type {
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
padding: 2px 8px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
background: var(--dw-primary-soft);
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.task-table__sub {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.task-table__actions {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<dw-card class="task-workspace" flex-body flush>
|
||||||
|
|
||||||
|
<template #header>
|
||||||
|
|
||||||
|
<el-tabs v-model="activeTab" class="task-workspace__tabs">
|
||||||
|
|
||||||
|
<el-tab-pane name="tasks">
|
||||||
|
|
||||||
|
<span slot="label" class="task-workspace__tab-label">
|
||||||
|
|
||||||
|
<console-icon name="tasks" :size="14" />
|
||||||
|
|
||||||
|
作业列表
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane name="logs">
|
||||||
|
|
||||||
|
<span slot="label" class="task-workspace__tab-label">
|
||||||
|
|
||||||
|
<console-icon name="logs" :size="14" />
|
||||||
|
|
||||||
|
动态日志
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="task-workspace__body">
|
||||||
|
|
||||||
|
<task-table
|
||||||
|
|
||||||
|
v-show="activeTab === 'tasks'"
|
||||||
|
|
||||||
|
:task-list="taskList"
|
||||||
|
|
||||||
|
@retry="$emit('retry', $event)"
|
||||||
|
|
||||||
|
@cancel="$emit('cancel', $event)"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<activity-log
|
||||||
|
|
||||||
|
v-show="activeTab === 'logs'"
|
||||||
|
|
||||||
|
:list="logList"
|
||||||
|
|
||||||
|
:show-all="showAllLogs"
|
||||||
|
|
||||||
|
@toggle-all="$emit('toggle-logs')"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</dw-card>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import DwCard from '../ui/DwCard.vue'
|
||||||
|
|
||||||
|
import ConsoleIcon from '../icons/ConsoleIcon.vue'
|
||||||
|
|
||||||
|
import TaskTable from './TaskTable.vue'
|
||||||
|
|
||||||
|
import ActivityLog from './ActivityLog.vue'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'TaskWorkspace',
|
||||||
|
|
||||||
|
components: { DwCard, ConsoleIcon, TaskTable, ActivityLog },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
taskList: { type: Array, default: () => [] },
|
||||||
|
|
||||||
|
logList: { type: Array, default: () => [] },
|
||||||
|
|
||||||
|
showAllLogs: { type: Boolean, default: false }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
|
||||||
|
return { activeTab: 'tasks' }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.task-workspace {
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-card);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__tabs {
|
||||||
|
|
||||||
|
::v-deep .el-tabs__header {
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
::v-deep .el-tabs__nav-wrap::after {
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
::v-deep .el-tabs__item {
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
height: 44px;
|
||||||
|
|
||||||
|
line-height: 44px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
::v-deep .el-tabs__item.is-active {
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__tab-label {
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
|
||||||
|
padding: 12px 16px 16px;
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
height: calc(100% - 0px);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dw-card" :class="{ 'dw-card--flush': flush }">
|
||||||
|
<div v-if="$slots.header || title" class="dw-card__header">
|
||||||
|
<slot name="header">
|
||||||
|
<span class="dw-card__title">{{ title }}</span>
|
||||||
|
</slot>
|
||||||
|
<div v-if="$slots.extra" class="dw-card__extra">
|
||||||
|
<slot name="extra" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dw-card__body" :class="{ 'dw-card__body--flex': flexBody }">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<div v-if="$slots.footer" class="dw-card__footer">
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DwCard',
|
||||||
|
props: {
|
||||||
|
title: { type: String, default: '' },
|
||||||
|
flush: { type: Boolean, default: false },
|
||||||
|
flexBody: { type: Boolean, default: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dw-card {
|
||||||
|
background: var(--dw-surface);
|
||||||
|
border-radius: var(--dw-radius-lg);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||||
|
box-shadow: var(--dw-shadow-card);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-bottom: 1px solid var(--dw-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--dw-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 16px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
&--flex {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--flush .dw-card__body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-top: 1px solid var(--dw-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="dw-empty" :class="`dw-empty--${scene}`">
|
||||||
|
|
||||||
|
<div class="dw-empty__visual">
|
||||||
|
|
||||||
|
<img
|
||||||
|
|
||||||
|
v-if="scene === 'offline'"
|
||||||
|
|
||||||
|
class="dw-empty__device-img"
|
||||||
|
|
||||||
|
:src="placeholderDeviceImg"
|
||||||
|
|
||||||
|
alt=""
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<console-icon v-else :name="iconName" :size="iconSize" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dw-empty__title">{{ title }}</div>
|
||||||
|
|
||||||
|
<div v-if="description" class="dw-empty__desc">{{ description }}</div>
|
||||||
|
|
||||||
|
<button v-if="actionText" type="button" class="dw-btn dw-btn--outline" @click="$emit('action')">
|
||||||
|
|
||||||
|
<console-icon v-if="scene === 'offline'" name="wifi" :size="14" />
|
||||||
|
|
||||||
|
{{ actionText }}
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ConsoleIcon from '../icons/ConsoleIcon.vue'
|
||||||
|
|
||||||
|
import { getDeviceImages } from '@/utils/console/constants'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'DwEmpty',
|
||||||
|
|
||||||
|
components: { ConsoleIcon },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
title: { type: String, default: '暂无数据' },
|
||||||
|
|
||||||
|
description: { type: String, default: '' },
|
||||||
|
|
||||||
|
actionText: { type: String, default: '' },
|
||||||
|
|
||||||
|
scene: {
|
||||||
|
|
||||||
|
type: String,
|
||||||
|
|
||||||
|
default: 'default',
|
||||||
|
|
||||||
|
validator: (v) => ['default', 'offline', 'tasks', 'drives', 'logs'].includes(v)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
iconName() {
|
||||||
|
|
||||||
|
const map = { tasks: 'tasks', drives: 'drive', logs: 'tasks', default: 'device', offline: 'offline' }
|
||||||
|
|
||||||
|
return map[this.scene] || 'device'
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
iconSize() {
|
||||||
|
|
||||||
|
return this.scene === 'offline' ? 32 : 40
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
placeholderDeviceImg() {
|
||||||
|
|
||||||
|
return getDeviceImages()['4200'].img1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.dw-empty {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
padding: 28px 16px;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__visual {
|
||||||
|
|
||||||
|
width: 88px;
|
||||||
|
|
||||||
|
height: 88px;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
background: linear-gradient(145deg, #f8fafc 0%, #eef2ff 100%);
|
||||||
|
|
||||||
|
border: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
color: var(--dw-primary);
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-sm);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__device-img {
|
||||||
|
|
||||||
|
max-width: 72px;
|
||||||
|
|
||||||
|
max-height: 56px;
|
||||||
|
|
||||||
|
object-fit: contain;
|
||||||
|
|
||||||
|
opacity: 0.55;
|
||||||
|
|
||||||
|
filter: grayscale(0.35);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--offline &__visual {
|
||||||
|
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
padding: 12px 16px;
|
||||||
|
|
||||||
|
border-radius: var(--dw-radius-md);
|
||||||
|
|
||||||
|
background: var(--dw-gradient-device);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
color: var(--dw-text);
|
||||||
|
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__desc {
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
max-width: 280px;
|
||||||
|
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="dw-kpi" :class="`dw-kpi--${variant}`">
|
||||||
|
|
||||||
|
<span v-if="icon === 'check'" class="dw-kpi__dot dw-kpi__dot--success" />
|
||||||
|
|
||||||
|
<span v-else-if="icon === 'alert'" class="dw-kpi__dot dw-kpi__dot--danger" />
|
||||||
|
|
||||||
|
<span class="dw-kpi__label">{{ label }}</span>
|
||||||
|
|
||||||
|
<span class="dw-kpi__value">{{ value }}</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: 'DwKpiChip',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
label: { type: String, required: true },
|
||||||
|
|
||||||
|
value: { type: [Number, String], default: 0 },
|
||||||
|
|
||||||
|
variant: {
|
||||||
|
|
||||||
|
type: String,
|
||||||
|
|
||||||
|
default: 'default',
|
||||||
|
|
||||||
|
validator: (v) => ['default', 'success', 'muted', 'danger'].includes(v)
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
icon: { type: String, default: '' }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.dw-kpi {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
padding: 5px 12px;
|
||||||
|
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
border: 1px solid var(--dw-border);
|
||||||
|
|
||||||
|
box-shadow: var(--dw-shadow-sm);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__dot {
|
||||||
|
|
||||||
|
width: 8px;
|
||||||
|
|
||||||
|
height: 8px;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--success { background: var(--dw-success); }
|
||||||
|
|
||||||
|
&--danger { background: var(--dw-danger); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
min-width: 18px;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&--success .dw-kpi__value { color: var(--dw-success); }
|
||||||
|
|
||||||
|
&--muted .dw-kpi__value { color: var(--dw-text-muted); }
|
||||||
|
|
||||||
|
&--danger .dw-kpi__value { color: var(--dw-danger); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dw-progress">
|
||||||
|
<div v-if="showLabel" class="dw-progress__label">
|
||||||
|
<span>{{ labelLeft }}</span>
|
||||||
|
<span>{{ labelRight }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="dw-progress__track">
|
||||||
|
<div
|
||||||
|
class="dw-progress__fill"
|
||||||
|
:class="{ 'dw-progress__fill--warning': value >= 90 && value < 100, 'dw-progress__fill--danger': value >= 100 }"
|
||||||
|
:style="{ width: clamped + '%', background: color || undefined }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DwProgressBar',
|
||||||
|
props: {
|
||||||
|
value: { type: Number, default: 0 },
|
||||||
|
color: { type: String, default: '' },
|
||||||
|
showLabel: { type: Boolean, default: false },
|
||||||
|
labelLeft: { type: String, default: '' },
|
||||||
|
labelRight: { type: String, default: '' }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
clamped() {
|
||||||
|
return Math.min(100, Math.max(0, this.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dw-progress {
|
||||||
|
&__label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__track {
|
||||||
|
height: 8px;
|
||||||
|
background: #e5e7eb;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fill {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--dw-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
|
||||||
|
&--warning { background: var(--dw-warning) !important; }
|
||||||
|
&--danger { background: var(--dw-danger) !important; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<span class="dw-pill" :class="`dw-pill--${variant}`">
|
||||||
|
<span v-if="dot" class="dw-pill__dot" />
|
||||||
|
<slot>{{ label }}</slot>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DwStatusPill',
|
||||||
|
props: {
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
default: 'idle',
|
||||||
|
validator: (v) => ['online', 'idle', 'busy', 'error', 'warning', 'info', 'neutral'].includes(v)
|
||||||
|
},
|
||||||
|
label: { type: String, default: '' },
|
||||||
|
dot: { type: Boolean, default: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dw-pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
|
||||||
|
&__dot {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--online {
|
||||||
|
background: var(--dw-success-soft);
|
||||||
|
color: var(--dw-success);
|
||||||
|
.dw-pill__dot { animation: dw-pulse 2s infinite; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&--idle { background: #f3f4f6; color: var(--dw-text-secondary); }
|
||||||
|
&--busy { background: var(--dw-info-soft); color: var(--dw-info); }
|
||||||
|
&--error { background: var(--dw-danger-soft); color: var(--dw-danger); }
|
||||||
|
&--warning { background: var(--dw-warning-soft); color: var(--dw-warning); }
|
||||||
|
&--info { background: var(--dw-info-soft); color: var(--dw-info); }
|
||||||
|
&--neutral { background: #f3f4f6; color: #4b5563; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="previewMode" class="work-add-modal-v2">
|
||||||
|
<div v-if="visible" class="work-add-modal-v2__mask">
|
||||||
|
<div class="work-add-modal-v2__dialog">
|
||||||
|
<div class="work-add-modal-v2__header">
|
||||||
|
<span>新建刻录作业</span>
|
||||||
|
<button type="button" class="work-add-modal-v2__close" @click="close">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="work-add-modal-v2__body">
|
||||||
|
<div class="work-add-modal-v2__left">
|
||||||
|
<h3>1. 数据内容</h3>
|
||||||
|
<div class="work-add-modal-v2__row">
|
||||||
|
<el-select v-model="previewForm.cd_type" size="small" style="width:130px">
|
||||||
|
<el-option v-for="item in previewCdTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
<el-input v-model="previewForm.label" size="small" placeholder="卷标" />
|
||||||
|
</div>
|
||||||
|
<div class="work-add-modal-v2__drop">
|
||||||
|
<p>点击或拖放文件到此处</p>
|
||||||
|
<span>支持 ISO, IMG, 文件夹</span>
|
||||||
|
</div>
|
||||||
|
<dw-progress-bar show-label :value="previewPercent" label-left="已选: 0 B" label-right="上限: 4.7 GB" />
|
||||||
|
</div>
|
||||||
|
<work-label-panel
|
||||||
|
:templates="previewTemplates"
|
||||||
|
:preview-title="previewForm.label"
|
||||||
|
preview-date="2025-11-23"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="work-add-modal-v2__footer">
|
||||||
|
<div class="work-add-modal-v2__freq">
|
||||||
|
<label>份数</label>
|
||||||
|
<el-input-number v-model="previewForm.task_freq" size="small" :min="1" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="button" class="dw-btn dw-btn--outline" @click="stub">保存草稿</button>
|
||||||
|
<button type="button" class="dw-btn dw-btn--primary" @click="stub">开始刻录</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<work-add
|
||||||
|
v-else
|
||||||
|
ref="legacy"
|
||||||
|
class="console-work-add-bridge"
|
||||||
|
:is-new="isNew"
|
||||||
|
:save-work-list="saveWorkList"
|
||||||
|
@close="$emit('close')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import WorkAdd from '@/components/workAdd/workAdd.vue'
|
||||||
|
import WorkLabelPanel from './WorkLabelPanel.vue'
|
||||||
|
import DwProgressBar from '../ui/DwProgressBar.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkAddModalV2',
|
||||||
|
components: { WorkAdd, WorkLabelPanel, DwProgressBar },
|
||||||
|
props: {
|
||||||
|
previewMode: { type: Boolean, default: false },
|
||||||
|
isNew: { type: Boolean, default: true },
|
||||||
|
saveWorkList: { type: Object, default: undefined }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
previewForm: { cd_type: 2, copy_type: 1, label: '2025-11-23_Archive', task_freq: 1 },
|
||||||
|
previewPercent: 0,
|
||||||
|
previewCdTypes: [
|
||||||
|
{ value: 1, label: 'CD (700MB)' },
|
||||||
|
{ value: 2, label: 'DVD (4.7GB)' }
|
||||||
|
],
|
||||||
|
previewTemplates: [{ value: 1, label: '标准日期模板' }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show() {
|
||||||
|
if (this.previewMode) {
|
||||||
|
this.visible = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$refs.legacy.show()
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
if (this.previewMode) {
|
||||||
|
this.visible = false
|
||||||
|
this.$emit('close')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$refs.legacy.close()
|
||||||
|
},
|
||||||
|
stub() {
|
||||||
|
this.$message.info('预览模式')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.work-add-modal-v2 {
|
||||||
|
&__mask {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(15, 23, 42, 0.45);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
z-index: 3000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dialog {
|
||||||
|
width: 960px;
|
||||||
|
max-width: calc(100vw - 32px);
|
||||||
|
height: 600px;
|
||||||
|
max-height: calc(100vh - 32px);
|
||||||
|
background: #fff;
|
||||||
|
border-radius: var(--dw-radius-lg);
|
||||||
|
box-shadow: var(--dw-shadow-md);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-bottom: 1px solid var(--dw-border);
|
||||||
|
background: #f8fafc;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
font-size: 22px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
flex: 1;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.2fr 0.8fr;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
padding: 16px 20px;
|
||||||
|
overflow: auto;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> *:first-child {
|
||||||
|
border-right: 1px solid var(--dw-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__drop {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 140px;
|
||||||
|
border: 2px dashed #bfdbfe;
|
||||||
|
background: #f0f9ff;
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
color: var(--dw-text);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-top: 1px solid var(--dw-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__freq {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.console-app .console-work-add-bridge.add_box .el-dialog {
|
||||||
|
width: 960px !important;
|
||||||
|
max-width: calc(100vw - 40px);
|
||||||
|
border-radius: var(--dw-radius-lg, 14px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-app .console-work-add-bridge .work_top_text,
|
||||||
|
.console-app .console-work-add-bridge .work_left_top_label,
|
||||||
|
.console-app .console-work-add-bridge .work_right_top_label {
|
||||||
|
color: var(--dw-text, #111827);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<div class="work-label-panel">
|
||||||
|
<div class="work-label-panel__head">
|
||||||
|
<h3 class="work-label-panel__title">2. 盘面标签</h3>
|
||||||
|
<button v-if="hasRunCmd" type="button" class="dw-btn dw-btn--ghost" @click="$emit('new-label')">新建标签</button>
|
||||||
|
<span v-else class="work-label-panel__hint">仅桌面端支持新建标签</span>
|
||||||
|
</div>
|
||||||
|
<div class="work-label-panel__field">
|
||||||
|
<label>选择模板</label>
|
||||||
|
<el-select v-model="currentTemplate" size="small" placeholder="选择模板" @change="$emit('template-change', $event)">
|
||||||
|
<el-option v-for="item in templates" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="work-label-panel__preview">
|
||||||
|
<img v-if="soonImg" class="work-label-panel__img" :src="soonImg" alt="label preview" />
|
||||||
|
<div v-else class="work-label-panel__disc">
|
||||||
|
<div class="work-label-panel__disc-title">{{ previewTitle }}</div>
|
||||||
|
<div class="work-label-panel__disc-date">{{ previewDate }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="(item, index) in soonList" :key="index" class="work-label-panel__field">
|
||||||
|
<label>{{ item.name }}</label>
|
||||||
|
<el-input v-if="item.type == 3 || item.type == 4 || item.type == 5" v-model="item.val" size="small" clearable :disabled="csvIsExist" />
|
||||||
|
</div>
|
||||||
|
<div class="work-label-panel__warn">
|
||||||
|
请确保打印机已装载光盘托架,且墨盒余量充足。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'WorkLabelPanel',
|
||||||
|
props: {
|
||||||
|
templates: { type: Array, default: () => [] },
|
||||||
|
currentTemplate: { type: [String, Number], default: '' },
|
||||||
|
soonImg: { type: String, default: '' },
|
||||||
|
soonList: { type: Array, default: () => [] },
|
||||||
|
csvIsExist: { type: Boolean, default: false },
|
||||||
|
hasRunCmd: { type: Boolean, default: false },
|
||||||
|
previewTitle: { type: String, default: '同方档案' },
|
||||||
|
previewDate: { type: String, default: '' }
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'currentTemplate',
|
||||||
|
event: 'update:currentTemplate'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.work-label-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&__head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--dw-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__preview {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 140px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid var(--dw-border);
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 180px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__disc {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: conic-gradient(from 45deg, #e2e8f0, #fff, #e2e8f0, #cbd5e1, #e2e8f0);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
box-shadow: var(--dw-shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__disc-title {
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__disc-date {
|
||||||
|
margin-top: 36px;
|
||||||
|
color: var(--dw-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warn {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9a3412;
|
||||||
|
background: #fff7ed;
|
||||||
|
border: 1px solid #ffedd5;
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -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' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@ import store from './store'
|
|||||||
import NProgress from 'nprogress' // Progress 进度条
|
import NProgress from 'nprogress' // Progress 进度条
|
||||||
import 'nprogress/nprogress.css'// Progress 进度条样式
|
import 'nprogress/nprogress.css'// Progress 进度条样式
|
||||||
import { Message } from 'element-ui'
|
import { Message } from 'element-ui'
|
||||||
|
import { getDefaultHomeRoute } from '@/utils/console/homeRoute'
|
||||||
|
|
||||||
const whiteList = ['/login'] // 不重定向白名单
|
const whiteList = ['/login', '/console/preview'] // 不重定向白名单
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
if (store.getters.token) {
|
if (store.getters.token) {
|
||||||
if (to.path === '/login') {
|
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
|
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ export function openHelp() {
|
|||||||
export function runCmd(cmd) {
|
export function runCmd(cmd) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
exec(cmd, (err, stdout, stderr) => {
|
exec(cmd, (err, stdout, stderr) => {
|
||||||
|
// 仅按退出码判定失败;Qt 等工具常向 stderr 打警告(如 QStandardPaths)
|
||||||
if (err) reject(err.message || err)
|
if (err) reject(err.message || err)
|
||||||
else if (stderr) reject(stderr)
|
else resolve(stdout || '')
|
||||||
else resolve(stdout)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ Vue.use(Router)
|
|||||||
export default new Router({
|
export default new Router({
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/', component: () => import('@/views/dashboard/index'), name: '首页' },
|
{ 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: '/login', component: () => import('@/views/login/index') },
|
||||||
{ path: '/404', component: () => import('@/views/404') },
|
{ path: '/404', component: () => import('@/views/404') },
|
||||||
{ path: '/manage', component: () => import('@/views/manage/index'), name: '系统管理' },
|
{ path: '/manage', component: () => import('@/views/manage/index'), name: '系统管理' },
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@use './tokens.scss';
|
||||||
|
@use './layout.scss';
|
||||||
|
@use './components.scss';
|
||||||
|
|
||||||
|
@import '~@fortawesome/fontawesome-free/css/all.min.css';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export function isConsoleV2Enabled() {
|
||||||
|
return process.env.VUE_APP_CONSOLE_V2 === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDefaultHomeRoute() {
|
||||||
|
return isConsoleV2Enabled() ? '/console' : '/'
|
||||||
|
}
|
||||||
@@ -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] || '空闲'
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,445 @@
|
|||||||
|
<template>
|
||||||
|
<div class="console-app">
|
||||||
|
<console-shell
|
||||||
|
:preview-mode="previewMode"
|
||||||
|
:demo-mode="isStaticView"
|
||||||
|
:user-name="displayName"
|
||||||
|
:task-num="taskNum"
|
||||||
|
:work-status="workStatus"
|
||||||
|
:has-run-cmd="hasRunCmd"
|
||||||
|
:is-connect="isConnect"
|
||||||
|
:service-status="serviceStatus"
|
||||||
|
:printer-info="printer_info"
|
||||||
|
:run-text="runText"
|
||||||
|
:tag-variant="tagVariant"
|
||||||
|
:tag-label="tagLabel"
|
||||||
|
:loading="pageLoading"
|
||||||
|
:run-list="runList"
|
||||||
|
:current-run-val="currentRunVal"
|
||||||
|
:sure-text="sureText"
|
||||||
|
:change-c-d="changeCD"
|
||||||
|
:cd-type-options="cdTypeOptions"
|
||||||
|
:cd-list="cd_list"
|
||||||
|
:CDStatus="CDStatus"
|
||||||
|
:CDName="CDName"
|
||||||
|
:CDTypes="CDTypes"
|
||||||
|
:diskTypes="diskTypes"
|
||||||
|
:task-list="task_list"
|
||||||
|
:log-list="displayLogs"
|
||||||
|
:show-all-logs="showAll"
|
||||||
|
@open-work="onOpenWork"
|
||||||
|
@go-settings="onGoSettings"
|
||||||
|
@activate="onActivate"
|
||||||
|
@user-command="onUserCommand"
|
||||||
|
@new-work="onNewWork"
|
||||||
|
@maintain="onMaintain"
|
||||||
|
@command-cd="onCommandCd"
|
||||||
|
@confirm-cd="onConfirmCd"
|
||||||
|
@cancel-cd="onCancelCd"
|
||||||
|
@refresh="onRefresh"
|
||||||
|
@retry="onRetry"
|
||||||
|
@cancel-task="onCancel"
|
||||||
|
@toggle-logs="onToggleLogs"
|
||||||
|
@help="onHelp"
|
||||||
|
@about="aboutVisible = true"
|
||||||
|
/>
|
||||||
|
<work-add-modal-v2
|
||||||
|
v-if="workShow"
|
||||||
|
ref="workAdd"
|
||||||
|
:preview-mode="isStaticView"
|
||||||
|
:is-new="isNew"
|
||||||
|
:save-work-list="saveWorkList"
|
||||||
|
@close="onWorkClose"
|
||||||
|
/>
|
||||||
|
<el-dialog title="关于 DiscWorker" :visible.sync="aboutVisible" width="400px">
|
||||||
|
<div class="about-box">
|
||||||
|
<div>国产光盘打印刻录系统 DiscWorker</div>
|
||||||
|
<div>版本 1.0</div>
|
||||||
|
<div>© 2023 cardsoon Co., Ltd.</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
<div v-if="serviceLoading" class="console-service-loading">
|
||||||
|
<i class="el-icon-loading" /> {{ serviceText }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import '@/styles/console/index.scss'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
import platform from '@/platform'
|
||||||
|
import ConsoleShell from '@/components/console/layout/ConsoleShell.vue'
|
||||||
|
import WorkAddModalV2 from '@/components/console/work/WorkAddModalV2.vue'
|
||||||
|
import { getConsoleMock, MOCK_PRINTER_STATUS } from '@/mock/console.mock'
|
||||||
|
import { transformTaskList } from '@/utils/console/taskListTransform'
|
||||||
|
import { getRunText, getDeviceTagVariant, getDeviceTagLabel } from '@/utils/console/printerStatus'
|
||||||
|
import { applyPrinterInfoSideEffects } from '@/utils/console/printerWatch'
|
||||||
|
import {
|
||||||
|
serviceChange,
|
||||||
|
startService,
|
||||||
|
handleActive as activateDevice,
|
||||||
|
openWork,
|
||||||
|
showWork,
|
||||||
|
workRetry,
|
||||||
|
workCancel,
|
||||||
|
handleRun,
|
||||||
|
handleCommandCD,
|
||||||
|
sureCD,
|
||||||
|
cancleCD,
|
||||||
|
noticeChange,
|
||||||
|
goSet,
|
||||||
|
goOut,
|
||||||
|
help,
|
||||||
|
getChangeCDDefault
|
||||||
|
} from '@/utils/console/consoleActions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConsolePage',
|
||||||
|
components: { ConsoleShell, WorkAddModalV2 },
|
||||||
|
props: {
|
||||||
|
previewMode: { type: Boolean, default: false },
|
||||||
|
designMode: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
aboutVisible: false,
|
||||||
|
taskNum: [0, 0, 0],
|
||||||
|
task_list: [],
|
||||||
|
printer_info: {},
|
||||||
|
cd_list: [],
|
||||||
|
logList: [],
|
||||||
|
previewAllLogs: [],
|
||||||
|
showAll: false,
|
||||||
|
workShow: false,
|
||||||
|
isNew: true,
|
||||||
|
saveWorkList: {},
|
||||||
|
is_set_cddev: false,
|
||||||
|
runList: [],
|
||||||
|
runVal: '',
|
||||||
|
currentRunVal: 0,
|
||||||
|
sureText: '请设置左右光盘桶类型并按确定键',
|
||||||
|
default_cd: [],
|
||||||
|
changeCD: getChangeCDDefault(),
|
||||||
|
CDTypes: {},
|
||||||
|
CDStatus: {},
|
||||||
|
CDName: {},
|
||||||
|
diskTypes: {},
|
||||||
|
isConnect: false,
|
||||||
|
serviceStatus: false,
|
||||||
|
serviceLoading: false,
|
||||||
|
serviceText: '服务关闭中',
|
||||||
|
pageLoading: true,
|
||||||
|
mockUserName: 'Admin'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['name']),
|
||||||
|
isStaticView() {
|
||||||
|
return this.previewMode || this.designMode
|
||||||
|
},
|
||||||
|
displayName() {
|
||||||
|
return this.isStaticView ? this.mockUserName : this.name
|
||||||
|
},
|
||||||
|
workStatus() {
|
||||||
|
return this.isConnect && this.serviceStatus
|
||||||
|
},
|
||||||
|
hasRunCmd() {
|
||||||
|
return platform.hasRunCmd && platform.hasRunCmd()
|
||||||
|
},
|
||||||
|
runText() {
|
||||||
|
return getRunText({
|
||||||
|
printer_info: this.printer_info,
|
||||||
|
isConnect: this.isConnect,
|
||||||
|
serviceStatus: this.serviceStatus,
|
||||||
|
PrinterStatus: this.isStaticView ? MOCK_PRINTER_STATUS : this.$store.state.chat.PrinterStatus
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tagVariant() {
|
||||||
|
return getDeviceTagVariant({
|
||||||
|
printer_info: this.printer_info,
|
||||||
|
isConnect: this.isConnect,
|
||||||
|
serviceStatus: this.serviceStatus
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tagLabel() {
|
||||||
|
return getDeviceTagLabel({
|
||||||
|
printer_info: this.printer_info,
|
||||||
|
isConnect: this.isConnect,
|
||||||
|
serviceStatus: this.serviceStatus,
|
||||||
|
PrinterStatus: this.isStaticView ? MOCK_PRINTER_STATUS : this.$store.state.chat.PrinterStatus
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cdTypeOptions() {
|
||||||
|
return Object.keys(this.CDTypes).map((key) => ({
|
||||||
|
value: parseInt(key, 10),
|
||||||
|
label: this.CDTypes[key]
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
displayLogs() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
return this.showAll ? this.previewAllLogs : this.logList
|
||||||
|
}
|
||||||
|
return this.showAll ? this.$store.state.chat.allLogList : this.logList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$store.state.chat.firstConnect': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
if (val && this.$store.state.user.oneLogin) {
|
||||||
|
this.$store.dispatch('setDatas', { name: 'oneLogin', data: false })
|
||||||
|
this.$store.dispatch('chat/setDatas', { name: 'firstConnect', data: false })
|
||||||
|
startService(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.task_list': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
const { list, taskNum } = transformTaskList(val, this.$store.state.chat.CDTypes, this.$accAdd)
|
||||||
|
this.task_list = list
|
||||||
|
this.taskNum = taskNum
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.printer_info': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.printer_info = { ...val }
|
||||||
|
const fx = applyPrinterInfoSideEffects(val, {
|
||||||
|
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 = JSON.parse(JSON.stringify(fx.changeCDStrongList))
|
||||||
|
}
|
||||||
|
if (fx.shouldRedirectCddev) {
|
||||||
|
this.is_set_cddev = true
|
||||||
|
goSet(this, '/manage?type=cddev')
|
||||||
|
}
|
||||||
|
this.pageLoading = false
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.cd_list': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.cd_list = [...val]
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.logList': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.logList = [...val]
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.CDTypes': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.CDTypes = { ...val }
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.CDStatus': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.CDStatus = { ...val }
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.CDName': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.CDName = { ...val }
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.diskTypes': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.diskTypes = { ...val }
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.isConnect': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.isConnect = val
|
||||||
|
if (val) this.pageLoading = false
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
'$store.state.chat.serviceStatus': {
|
||||||
|
handler(val) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
this.serviceStatus = val
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.applyMock()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (!this.isStaticView) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.pageLoading = false
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
applyMock() {
|
||||||
|
const mock = getConsoleMock()
|
||||||
|
this.taskNum = mock.taskNum
|
||||||
|
this.task_list = mock.task_list
|
||||||
|
this.printer_info = mock.printer_info
|
||||||
|
this.cd_list = mock.cd_list
|
||||||
|
this.logList = mock.logList
|
||||||
|
this.previewAllLogs = [...mock.logList, ...(mock.allLogList || [])]
|
||||||
|
this.isConnect = mock.isConnect
|
||||||
|
this.serviceStatus = mock.serviceStatus
|
||||||
|
this.CDTypes = { ...(mock.CDTypes || {}) }
|
||||||
|
this.CDStatus = { ...(mock.CDStatus || {}) }
|
||||||
|
this.CDName = { ...(mock.CDName || {}) }
|
||||||
|
this.diskTypes = { ...(mock.diskTypes || {}) }
|
||||||
|
this.changeCD = getChangeCDDefault()
|
||||||
|
this.default_cd = JSON.parse(JSON.stringify(this.changeCD.req_info.strong_list))
|
||||||
|
this.runList = [
|
||||||
|
{ value: 1, label: '重启设备' },
|
||||||
|
{ value: 2, label: '清除错误' },
|
||||||
|
{ value: 6, label: '设置光盘桶' },
|
||||||
|
{ value: 7, label: '刷新光盘桶' }
|
||||||
|
]
|
||||||
|
this.pageLoading = false
|
||||||
|
},
|
||||||
|
onGoSettings() {
|
||||||
|
goSet(this, '/manage')
|
||||||
|
},
|
||||||
|
onOpenWork() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.$message.info('设计模式:打开文件 UI 待接入')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
openWork(this, platform)
|
||||||
|
},
|
||||||
|
onNewWork() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.workShow = true
|
||||||
|
this.$nextTick(() => this.$refs.workAdd.show())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
showWork(this)
|
||||||
|
},
|
||||||
|
onActivate() {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
activateDevice(this)
|
||||||
|
},
|
||||||
|
onUserCommand(cmd) {
|
||||||
|
if (cmd === 'toggle-service') {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.$message.info('设计模式:暂不连接服务')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serviceChange(this, !this.workStatus)
|
||||||
|
} else if (cmd === 'legacy') {
|
||||||
|
this.$router.push('/')
|
||||||
|
} else if (cmd === 'logout') {
|
||||||
|
goOut(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMaintain(runVal) {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
if (parseInt(runVal, 10) === 6) {
|
||||||
|
this.currentRunVal = 6
|
||||||
|
} else {
|
||||||
|
this.$message.info('设计模式')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.runVal = runVal
|
||||||
|
handleRun(this, runVal)
|
||||||
|
},
|
||||||
|
onCommandCd(e, type) {
|
||||||
|
handleCommandCD(this, e, type)
|
||||||
|
},
|
||||||
|
onConfirmCd() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.currentRunVal = 0
|
||||||
|
this.$message.info('设计模式')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sureCD(this)
|
||||||
|
},
|
||||||
|
onCancelCd() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.currentRunVal = 0
|
||||||
|
this.changeCD.req_info.strong_list = JSON.parse(JSON.stringify(this.default_cd))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cancleCD(this)
|
||||||
|
},
|
||||||
|
onRefresh() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.applyMock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$store.dispatch('chat/initWebSocket')
|
||||||
|
},
|
||||||
|
onRetry(row) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
workRetry(this, row)
|
||||||
|
},
|
||||||
|
onCancel(row) {
|
||||||
|
if (this.isStaticView) return
|
||||||
|
workCancel(this, row)
|
||||||
|
},
|
||||||
|
onToggleLogs() {
|
||||||
|
if (this.isStaticView) {
|
||||||
|
this.showAll = !this.showAll
|
||||||
|
return
|
||||||
|
}
|
||||||
|
noticeChange(this)
|
||||||
|
},
|
||||||
|
onHelp() {
|
||||||
|
help()
|
||||||
|
},
|
||||||
|
onWorkClose() {
|
||||||
|
this.workShow = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.about-box {
|
||||||
|
line-height: 1.8;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-service-loading {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 40px;
|
||||||
|
right: 20px;
|
||||||
|
background: rgba(17, 24, 39, 0.88);
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: var(--dw-radius-sm);
|
||||||
|
font-size: 13px;
|
||||||
|
z-index: 2500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<console-page design-mode />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConsolePage from './consolePage.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConsoleIndex',
|
||||||
|
components: { ConsolePage }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<console-page :preview-mode="true" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConsolePage from './consolePage.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConsolePreview',
|
||||||
|
components: { ConsolePage }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -532,6 +532,8 @@ import { mapGetters } from 'vuex'
|
|||||||
import UserInfo from '@/components/userInfo/userInfo.vue'
|
import UserInfo from '@/components/userInfo/userInfo.vue'
|
||||||
import WorkAdd from '@/components/workAdd/workAdd.vue'
|
import WorkAdd from '@/components/workAdd/workAdd.vue'
|
||||||
import platform from '@/platform'
|
import platform from '@/platform'
|
||||||
|
import { transformTaskList } from '@/utils/console/taskListTransform'
|
||||||
|
import { applyPrinterInfoSideEffects } from '@/utils/console/printerWatch'
|
||||||
export default {
|
export default {
|
||||||
name: 'dashboard',
|
name: 'dashboard',
|
||||||
components: { UserInfo, WorkAdd },
|
components: { UserInfo, WorkAdd },
|
||||||
@@ -666,46 +668,8 @@ export default {
|
|||||||
'$store.state.chat.task_list': {
|
'$store.state.chat.task_list': {
|
||||||
handler(val) {
|
handler(val) {
|
||||||
if (val && val.length) {
|
if (val && val.length) {
|
||||||
let tasks = [...val]
|
const { list, taskNum } = transformTaskList(val, this.$store.state.chat.CDTypes, this.$accAdd)
|
||||||
let list = []
|
this.taskNum = taskNum
|
||||||
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
|
|
||||||
this.task_list = list
|
this.task_list = list
|
||||||
} else {
|
} else {
|
||||||
this.task_list = []
|
this.task_list = []
|
||||||
@@ -719,64 +683,16 @@ export default {
|
|||||||
that = this
|
that = this
|
||||||
let data = { ...val }
|
let data = { ...val }
|
||||||
this.printer_info = { ...data }
|
this.printer_info = { ...data }
|
||||||
if (val.printer_name && val.printer_name.indexOf('SE3') > -1) {
|
const fx = applyPrinterInfoSideEffects(data, {
|
||||||
if (this.currentRunVal !== 6) {
|
currentRunVal: this.currentRunVal,
|
||||||
this.changeCD.req_info.strong_list = [
|
is_set_cddev_flag: this.is_set_cddev
|
||||||
{
|
})
|
||||||
strong_pos: 1,
|
this.runList = fx.runList
|
||||||
strong_type: data.strong_list[0].cd_type
|
if (fx.changeCDStrongList) {
|
||||||
}
|
this.changeCD.req_info.strong_list = fx.changeCDStrongList
|
||||||
]
|
this.default_cd = fx.changeCDStrongList
|
||||||
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: '刷新光盘桶'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
if (this.printer_info.is_set_cddev == 1) {
|
if (fx.shouldRedirectCddev) {
|
||||||
if (this.is_set_cddev) return
|
if (this.is_set_cddev) return
|
||||||
this.is_set_cddev = true
|
this.is_set_cddev = true
|
||||||
this.goSet('/manage?type=cddev')
|
this.goSet('/manage?type=cddev')
|
||||||
@@ -989,7 +905,7 @@ export default {
|
|||||||
})
|
})
|
||||||
.then(({ value }) => {
|
.then(({ value }) => {
|
||||||
that
|
that
|
||||||
.$runCmd(`bash ${that.activePath} ${value}`)
|
.$runCmd(`bash "${that.activePath}" '${String(value).replace(/'/g, `'\\''`)}'`)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { validUsername } from '@/utils/validate'
|
import { validUsername } from '@/utils/validate'
|
||||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||||
|
import { getDefaultHomeRoute } from '@/utils/console/homeRoute'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'login',
|
name: 'login',
|
||||||
@@ -133,7 +134,7 @@ export default {
|
|||||||
this.$store.dispatch('setDatas', { name: 'oneLogin', data: true })
|
this.$store.dispatch('setDatas', { name: 'oneLogin', data: true })
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.$router.push({ path: '/' })
|
this.$router.push({ path: getDefaultHomeRoute() })
|
||||||
}, 2000);
|
}, 2000);
|
||||||
// this.$store.dispatch('Login', this.loginForm).then(() => {
|
// this.$store.dispatch('Login', this.loginForm).then(() => {
|
||||||
// this.loading = false
|
// this.loading = false
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container_box">
|
<div class="container_box">
|
||||||
|
<div class="console-back-bar">
|
||||||
|
<router-link to="/console" class="console-back-bar__link">← 返回控制台</router-link>
|
||||||
|
</div>
|
||||||
<!-- 头部 -->
|
<!-- 头部 -->
|
||||||
<tab-bar :active="1"></tab-bar>
|
<tab-bar :active="1"></tab-bar>
|
||||||
<!-- 内容 -->
|
<!-- 内容 -->
|
||||||
@@ -1102,6 +1105,16 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.console-back-bar {
|
||||||
|
padding: 8px 32px 0;
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1e4fd9;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.container_box {
|
.container_box {
|
||||||
.form_box {
|
.form_box {
|
||||||
padding: 0 32px;
|
padding: 0 32px;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container_box">
|
<div class="container_box">
|
||||||
|
<div class="console-back-bar">
|
||||||
|
<router-link to="/console" class="console-back-bar__link">← 返回控制台</router-link>
|
||||||
|
</div>
|
||||||
<!-- 头部 -->
|
<!-- 头部 -->
|
||||||
<tab-bar :active="3"></tab-bar>
|
<tab-bar :active="3"></tab-bar>
|
||||||
<!-- 内容 -->
|
<!-- 内容 -->
|
||||||
@@ -262,6 +265,16 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.console-back-bar {
|
||||||
|
padding: 8px 32px 0;
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1e4fd9;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.container_box {
|
.container_box {
|
||||||
.form_box {
|
.form_box {
|
||||||
padding: 0 32px;
|
padding: 0 32px;
|
||||||
|
|||||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 13 KiB |