Compare commits
10 Commits
1c503a69c9
...
c727492da2
| Author | SHA1 | Date | |
|---|---|---|---|
| c727492da2 | |||
| dee6029642 | |||
| 68d1968376 | |||
| 31c9d64ba9 | |||
| b277ef38b3 | |||
| 9c471a494c | |||
| 03a5a18998 | |||
| f0a3b92dd3 | |||
| ba25ad48f0 | |||
| 4a412ea668 |
@@ -65,6 +65,7 @@ function startRenderer () {
|
||||
contentBase: path.join(__dirname, '../'),
|
||||
quiet: true,
|
||||
hot: true,
|
||||
disableHostCheck: true,
|
||||
before (app, ctx) {
|
||||
app.use(hotMiddleware)
|
||||
ctx.middleware.waitUntilValid(() => {
|
||||
|
||||
@@ -139,6 +139,7 @@ let rendererConfig = {
|
||||
options: options,
|
||||
},
|
||||
process,
|
||||
isWeb: false,
|
||||
};
|
||||
},
|
||||
minify: {
|
||||
|
||||
@@ -4,6 +4,8 @@ process.env.BABEL_ENV = 'web'
|
||||
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const env = process.env.NODE_ENV === 'production' ? config.build.env : config.dev.env
|
||||
|
||||
const MinifyPlugin = require("babel-minify-webpack-plugin")
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
@@ -107,6 +109,7 @@ let webConfig = {
|
||||
options: options,
|
||||
},
|
||||
process,
|
||||
isWeb: true,
|
||||
};
|
||||
},
|
||||
minify: {
|
||||
@@ -117,7 +120,9 @@ let webConfig = {
|
||||
nodeModules: false
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.IS_WEB': 'true'
|
||||
'process.env.IS_WEB': '"true"',
|
||||
'process.env.BASE_API': env.BASE_API || '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"',
|
||||
'process.env.VUE_APP_SOCKET_API': env.VUE_APP_SOCKET_API || '"ws://127.0.0.1:10010"'
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin()
|
||||
@@ -129,11 +134,24 @@ let webConfig = {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.join(__dirname, '../src/renderer'),
|
||||
'@/platform': path.join(__dirname, '../src/renderer/platform/web.js'),
|
||||
'vue$': 'vue/dist/vue.esm.js'
|
||||
},
|
||||
extensions: ['.js', '.vue', '.json', '.css']
|
||||
},
|
||||
target: 'web'
|
||||
node: {
|
||||
fs: 'empty',
|
||||
path: 'empty',
|
||||
child_process: 'empty'
|
||||
},
|
||||
target: 'web',
|
||||
devServer: process.env.NODE_ENV !== 'production' ? {
|
||||
port: 9081,
|
||||
hot: true,
|
||||
open: true,
|
||||
contentBase: path.join(__dirname, '../'),
|
||||
publicPath: '/'
|
||||
} : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-1
@@ -1 +1,5 @@
|
||||
|
||||
功能:统一相对路径导入 fileSize 工具,修复打包别名解析问题
|
||||
|
||||
- 将 '@/utils/fileSize' 全部改为相对路径导入,兼容打包环境
|
||||
- 修复 store 与页面中别名解析失败导致的模块找不到问题
|
||||
- 保持逻辑不变,仅调整导入路径,降低打包风险
|
||||
@@ -0,0 +1,226 @@
|
||||
# 宝塔部署 Web 端说明
|
||||
|
||||
## 一、构建前配置
|
||||
|
||||
### 1. 配置后端 API 地址
|
||||
|
||||
编辑 `config/prod.env.js`,将 `BASE_API` 改为实际的后端接口地址:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"',
|
||||
BASE_API: '"https://your-api-domain.com/api"', // 改为实际后端地址
|
||||
VUE_APP_SOCKET_API: '"ws://127.0.0.1:10010"' // WebSocket 默认地址(用户可在登录页修改)
|
||||
}
|
||||
```
|
||||
|
||||
**注意:**
|
||||
- `BASE_API` 是 HTTP 接口地址(登录、数据请求等)
|
||||
- `VUE_APP_SOCKET_API` 是 WebSocket 默认地址,用户可在登录页修改,所以这里可以保持默认或设为公网地址
|
||||
|
||||
### 2. 执行构建
|
||||
|
||||
在项目根目录执行:
|
||||
|
||||
```bash
|
||||
npm run build:web
|
||||
```
|
||||
|
||||
构建完成后,产物在 `dist/web/` 目录下,包含:
|
||||
- `index.html`(入口文件)
|
||||
- `web.js`(主 JS 文件,包含所有代码和样式)
|
||||
- `1.js` ~ `7.js`(代码分割的 chunk 文件)
|
||||
- `fonts/`(字体文件)
|
||||
- `imgs/`(图片资源)
|
||||
|
||||
**注意**:CSS 样式被打包进 `web.js` 中,运行时动态注入,无需单独引用 CSS 文件。
|
||||
|
||||
---
|
||||
|
||||
## 二、宝塔部署步骤
|
||||
|
||||
### 1. 创建网站
|
||||
|
||||
1. 登录宝塔面板
|
||||
2. 点击「网站」→「添加站点」
|
||||
3. 填写:
|
||||
- **域名**:例如 `your-domain.com` 或 `192.168.1.100`(内网 IP)
|
||||
- **根目录**:例如 `/www/wwwroot/your-domain.com`
|
||||
- **PHP 版本**:选择「纯静态」或任意版本(前端不需要 PHP)
|
||||
|
||||
### 2. 上传文件
|
||||
|
||||
将 `dist/web/` 目录下的**所有文件**上传到网站根目录:
|
||||
|
||||
```
|
||||
/www/wwwroot/your-domain.com/
|
||||
├── index.html
|
||||
├── web.js
|
||||
├── styles.css
|
||||
└── static/
|
||||
├── imgs/
|
||||
└── fonts/
|
||||
```
|
||||
|
||||
**上传方式:**
|
||||
- 方式一:在宝塔「文件」中直接上传压缩包并解压
|
||||
- 方式二:使用 FTP/SFTP 工具上传
|
||||
- 方式三:使用宝塔「终端」执行 `scp` 或 `rsync` 命令
|
||||
|
||||
### 3. 配置 Nginx(重要)
|
||||
|
||||
由于是 Vue Router 的 hash 模式(`#/login`、`#/manage`),通常不需要特殊配置。但建议添加以下配置以确保正常访问:
|
||||
|
||||
在宝塔「网站」→ 选择站点 →「设置」→「配置文件」中,在 `server` 块内添加:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
root /www/wwwroot/your-domain.com;
|
||||
index index.html;
|
||||
|
||||
# 前端路由支持(hash 模式通常不需要,但加上更稳妥)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 禁止访问隐藏文件
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 配置 HTTPS(可选但推荐)
|
||||
|
||||
如果使用域名且需要 HTTPS:
|
||||
|
||||
1. 在宝塔「网站」→ 选择站点 →「SSL」→「Let's Encrypt」申请免费证书
|
||||
2. 开启「强制 HTTPS」
|
||||
3. **注意**:如果后端 API 或 WebSocket 使用 HTTPS/WSS,确保:
|
||||
- `BASE_API` 使用 `https://`
|
||||
- WebSocket 地址使用 `wss://`(或在登录页填写 `wss://` 地址)
|
||||
|
||||
### 5. 配置跨域(如需要)
|
||||
|
||||
如果后端 API 与前端不在同一域名,后端需要配置 CORS:
|
||||
|
||||
- 允许的源:`https://your-domain.com`(或 `*` 用于开发)
|
||||
- 允许的请求头:`Content-Type, X-Token`(根据实际后端要求)
|
||||
- 允许的方法:`GET, POST, PUT, DELETE` 等
|
||||
|
||||
---
|
||||
|
||||
## 三、WebSocket 配置
|
||||
|
||||
### 情况 1:WebSocket 服务在服务器本机
|
||||
|
||||
如果 WebSocket 服务运行在**宝塔服务器本机**(例如 `ws://127.0.0.1:10010`):
|
||||
|
||||
- 用户访问网页时,浏览器会尝试连接**用户电脑**的 `127.0.0.1`,**无法连接**
|
||||
- **解决方案**:
|
||||
1. 在登录页填写 `ws://服务器公网IP:10010` 或 `ws://服务器域名:10010`
|
||||
2. 或配置 Nginx 反向代理 WebSocket(见下方)
|
||||
|
||||
### 情况 2:使用 Nginx 反向代理 WebSocket(推荐)
|
||||
|
||||
在宝塔「网站」→ 选择站点 →「设置」→「配置文件」中添加:
|
||||
|
||||
```nginx
|
||||
# WebSocket 代理(假设后端 WebSocket 在 127.0.0.1:10010)
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:10010;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
```
|
||||
|
||||
然后在登录页填写:`wss://your-domain.com/ws`(HTTPS 用 `wss://`,HTTP 用 `ws://`)
|
||||
|
||||
### 情况 3:WebSocket 服务在公网服务器
|
||||
|
||||
如果 WebSocket 服务部署在**公网服务器**(例如 `wss://ws-api.xxx.com`):
|
||||
|
||||
- 在登录页填写该地址即可
|
||||
- 或在构建时设置 `VUE_APP_SOCKET_API` 为 `wss://ws-api.xxx.com`
|
||||
|
||||
---
|
||||
|
||||
## 四、访问测试
|
||||
|
||||
1. 打开浏览器访问:`http://your-domain.com` 或 `https://your-domain.com`
|
||||
2. 应能看到登录页
|
||||
3. 点击「设置 WebSocket 服务地址」,填写实际可访问的 WebSocket 地址
|
||||
4. 输入账号密码登录
|
||||
5. 检查:
|
||||
- 登录是否成功
|
||||
- WebSocket 是否连接成功(查看浏览器控制台 Network → WS)
|
||||
- 各功能页面是否正常加载
|
||||
|
||||
---
|
||||
|
||||
## 五、常见问题
|
||||
|
||||
### 1. 页面空白
|
||||
|
||||
- **检查**:浏览器控制台是否有 JS 错误
|
||||
- **解决**:确认 `index.html` 中引用的 `web.js`、`styles.css` 路径正确;检查 Nginx 配置是否正确
|
||||
|
||||
### 2. 接口请求失败(404 或 CORS)
|
||||
|
||||
- **检查**:`BASE_API` 是否正确;后端是否允许跨域
|
||||
- **解决**:修改 `config/prod.env.js` 中的 `BASE_API` 后重新构建;后端配置 CORS
|
||||
|
||||
### 3. WebSocket 连接失败
|
||||
|
||||
- **检查**:浏览器控制台 Network → WS,查看连接地址和错误信息
|
||||
- **解决**:
|
||||
- 确认 WebSocket 服务已启动
|
||||
- 在登录页填写正确的地址(本机用 `ws://127.0.0.1:10010`,服务器用 `ws://服务器IP:10010`,HTTPS 用 `wss://`)
|
||||
- 如使用 Nginx 代理,确认代理配置正确
|
||||
|
||||
### 4. 静态资源 404
|
||||
|
||||
- **检查**:`static/` 目录是否上传完整
|
||||
- **解决**:重新上传 `dist/web/static/` 目录
|
||||
|
||||
### 5. 路由跳转异常
|
||||
|
||||
- **检查**:Nginx 配置中是否有 `try_files $uri $uri/ /index.html;`
|
||||
- **解决**:添加该配置后重启 Nginx
|
||||
|
||||
---
|
||||
|
||||
## 六、更新部署
|
||||
|
||||
当代码更新后:
|
||||
|
||||
1. 修改 `config/prod.env.js`(如需要)
|
||||
2. 执行 `npm run build:web`
|
||||
3. 将 `dist/web/` 下的文件**覆盖上传**到宝塔网站根目录
|
||||
4. 清除浏览器缓存或强制刷新(Ctrl+F5)
|
||||
|
||||
---
|
||||
|
||||
## 七、总结
|
||||
|
||||
- **构建产物**:`dist/web/` 目录
|
||||
- **上传位置**:宝塔网站根目录
|
||||
- **配置要点**:
|
||||
- `BASE_API`:后端 HTTP 接口地址
|
||||
- WebSocket:用户可在登录页配置,或通过 Nginx 反向代理
|
||||
- **访问方式**:`http(s)://your-domain.com`
|
||||
|
||||
部署完成后,用户访问网站即可使用,首次登录时记得在登录页配置 WebSocket 服务地址。
|
||||
@@ -0,0 +1,82 @@
|
||||
# WebSocket 与网页端部署说明
|
||||
|
||||
## 问题说明
|
||||
|
||||
程序通过 **WebSocket** 与后端服务通信(设备状态、作业列表、系统配置等)。该服务在**桌面端**场景下是 Windows 本机运行的服务(默认 `ws://127.0.0.1:10010`)。
|
||||
|
||||
**网页端打包部署后能否连接,取决于“浏览器连的是谁”:**
|
||||
|
||||
- 网页运行在**用户浏览器**里,`new WebSocket("ws://127.0.0.1:10010")` 会连接的是**当前用户所在设备**的 127.0.0.1,而不是你放网页的那台服务器。
|
||||
- 因此:
|
||||
- **用户和 Windows 服务在同一台电脑**:例如在本机浏览器打开部署在本机的页面,用 `ws://127.0.0.1:10010` 可以连到本机服务,**可以连接**。
|
||||
- **用户和 Windows 服务不在同一台电脑**:例如网页部署在服务器 A,用户在手机或另一台电脑 B 打开,此时 127.0.0.1 指向的是 B,连不到 A 上的服务,**无法连接**。
|
||||
|
||||
所以:**把程序打包放到网页端后,只有在“能访问到 WebSocket 服务”的情况下才可以连接**;默认写死 127.0.0.1 时,只适合“网页和服务在同一台机器”或“用户本机也跑了该服务”的场景。
|
||||
|
||||
---
|
||||
|
||||
## 已做改造:可配置 WebSocket 地址
|
||||
|
||||
为支持不同部署方式,已做两点改动:
|
||||
|
||||
### 1. 连接时优先读“用户配置”
|
||||
|
||||
- 连接 WebSocket 时**优先使用**本地存储中的 **`WS_SOCKET_API`**(localStorage)。
|
||||
- 若未配置,再使用构建时注入的 **`process.env.VUE_APP_SOCKET_API`**(默认 `ws://127.0.0.1:10010`)。
|
||||
|
||||
因此同一套网页包可以:
|
||||
|
||||
- 部署在服务器上,由用户在登录页填写“实际要连的 WebSocket 地址”;
|
||||
- 或由部署方在构建时通过环境变量写死一个公网/内网地址。
|
||||
|
||||
### 2. 登录页可填写并保存地址
|
||||
|
||||
- 在**登录页**增加 **“设置 WebSocket 服务地址”**(可展开/收起)。
|
||||
- 用户可输入例如:
|
||||
- `ws://127.0.0.1:10010`(本机服务)
|
||||
- `ws://192.168.1.100:10010`(内网某台机器上的服务)
|
||||
- `wss://your-domain.com/ws`(通过域名 + HTTPS 的 WSS 服务)
|
||||
- 失焦或保存后写入 **localStorage**,下次进入或刷新后,连接会使用该地址。
|
||||
|
||||
这样**网页端打包部署后**,只要用户(或管理员)在登录页把地址改成“实际可访问的 WebSocket 服务地址”,就可以正常连接。
|
||||
|
||||
---
|
||||
|
||||
## 常见部署方式与是否可连接
|
||||
|
||||
| 部署方式 | 网页访问方式 | WebSocket 地址建议 | 能否连接 |
|
||||
|----------|--------------|--------------------|----------|
|
||||
| 网页和 Windows 服务都在**同一台电脑** | 本机浏览器打开(如 http://localhost:9081 或 file://) | `ws://127.0.0.1:10010`(默认即可) | 可以 |
|
||||
| 网页在**服务器**,服务仍在**用户本机** | 用户在本机浏览器打开网站 | 在登录页设为 `ws://127.0.0.1:10010`(本机须已运行该服务) | 可以(仅限本机也跑服务时) |
|
||||
| 网页在**服务器**,服务在**内网另一台机器** | 用户在内网任意电脑打开网站 | 在登录页设为 `ws://内网机器IP:10010`(或该机域名) | 可以(需网络可达、无策略拦截) |
|
||||
| 网页在**公网**,服务在**公网服务器** | 任意设备打开网站 | 构建时设 `VUE_APP_SOCKET_API=wss://api.xxx.com/ws`,或在登录页填写该地址 | 可以(需后端提供 wss 与跨域策略) |
|
||||
|
||||
---
|
||||
|
||||
## 使用步骤(网页端部署后)
|
||||
|
||||
1. 打开登录页,点击 **“设置 WebSocket 服务地址”**。
|
||||
2. 填入实际可访问的地址,例如:
|
||||
- 本机:`ws://127.0.0.1:10010`
|
||||
- 内网机器:`ws://192.168.1.100:10010`
|
||||
- 公网:`wss://your-api.com/ws`
|
||||
3. 失焦后会自动保存到当前浏览器;之后登录或刷新页面,会使用该地址连接。
|
||||
4. 若更换环境(换电脑/换网络),在同一浏览器重新打开登录页,修改并保存新地址即可。
|
||||
|
||||
---
|
||||
|
||||
## 构建时写死地址(可选)
|
||||
|
||||
若部署环境固定(例如始终连同一台服务器),可在构建网页时注入默认地址,减少用户手动填写:
|
||||
|
||||
- 开发/测试:在 `config/dev.env.js` 中设置 `VUE_APP_SOCKET_API`。
|
||||
- 生产:在 `config/prod.env.js` 或 CI 中设置 `VUE_APP_SOCKET_API`(如 `wss://api.xxx.com/ws`)。
|
||||
|
||||
未在登录页保存过地址时,会使用该默认值;保存过后以登录页配置为准。
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
- **默认 `ws://127.0.0.1:10010` 只适合“浏览器所在机器 = 运行 WebSocket 服务的机器”的情况。**
|
||||
- **网页端打包部署后**:通过**登录页可配置 WebSocket 地址**并写入 localStorage,连接时**优先使用该配置**,因此只要用户填写的地址在浏览器侧可达(本机/内网/公网),就可以正常连接。
|
||||
@@ -0,0 +1,154 @@
|
||||
# DiscWorker 网页端功能清单与实现情况
|
||||
|
||||
本文档列出全部功能,并标明:**无改动**(桌面/网页一致)、**有改动**(为兼容网页做了适配)、**网页端无法实现**(仅桌面端或需后端扩展)。
|
||||
|
||||
---
|
||||
|
||||
## 一、全局与基础设施
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 路由(/、/login、/manage、/set、/user、/404) | 无改动 | 使用 Vue Router,hash 模式,网页端可直接部署 |
|
||||
| 登录鉴权(permission.js) | 无改动 | 基于 token 与 store,与运行环境无关 |
|
||||
| 路由守卫(白名单、未登录跳转登录) | 无改动 | 纯前端逻辑 |
|
||||
| HTTP 请求(request.js、BASE_API) | 有改动 | 网页构建通过 DefinePlugin 注入 BASE_API,需配置后端地址 |
|
||||
| 多语言(vue-i18n) | 无改动 | 纯前端 |
|
||||
| Element UI、主题与尺寸 | 无改动 | 纯前端 |
|
||||
| Vuex Store(user、chat、app 等) | 有改动 | store 仅桌面端条件引入 vuex-electron,网页端不引入 |
|
||||
|
||||
---
|
||||
|
||||
## 二、登录页(/login)
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 用户名/密码输入、记住账号 | 无改动 | 纯表单 + localStorage |
|
||||
| 登录请求(/user/login) | 无改动 | 走 axios + BASE_API,网页端需后端可用 |
|
||||
| 新手引导(第一步) | 无改动 | 纯前端弹窗与步骤 |
|
||||
| **WebSocket 服务地址设置** | **有改动** | 登录页可展开「设置 WebSocket 服务地址」,填写后写入 localStorage(key: `WS_SOCKET_API`),连接时优先使用该地址,便于网页端部署后连接本机/内网/公网服务。详见 [WEBSOCKET_DEPLOY.md](./WEBSOCKET_DEPLOY.md) |
|
||||
|
||||
---
|
||||
|
||||
## 三、首页 / 看板(/)
|
||||
|
||||
### 3.1 顶部与状态
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 回到主界面、系统设置入口 | 无改动 | 路由跳转 |
|
||||
| 服务启停开关(打开/关闭服务) | **网页端无法实现** | 依赖桌面端执行 `control.sh`,网页端显示「网页端」占位与 tooltip「仅桌面端支持启停服务」 |
|
||||
| 完成刻录数/废弃刻录数/错误数 | 无改动 | 来自 WebSocket 的 task_list,与端无关 |
|
||||
| 试用版天数、前往激活 | 有改动 | 「前往激活」仅桌面端显示(依赖 runCmd 执行 regist.sh);网页端不显示该按钮 |
|
||||
| 用户信息组件 | 无改动 | 展示 store 中的用户信息 |
|
||||
|
||||
### 3.2 设备与作业
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 设备状态展示(图示、状态文案) | 无改动 | 数据来自 WebSocket printer_info |
|
||||
| 运行选项(重启设备、清除错误、设置光盘桶、刷新光盘桶等) | 无改动 | 通过 WebSocket 下发,网页端在已连接服务时可用 |
|
||||
| 作业列表(表格、重试/取消) | 无改动 | WebSocket task_list + websocketsend |
|
||||
| 新建作业、打开作业 | 有改动 | **打开作业**:桌面端用系统对话框选 .dwk 后读文件;网页端用 `<input type="file">` 选文件并 FileReader 读内容,再解析填入 workAdd,**可正常使用**。新建作业为打开弹窗,无改动 |
|
||||
| 保存作业(workAdd 内) | 有改动 | 桌面端:系统保存对话框 + 写入本地路径;网页端:无保存对话框,直接触发浏览器下载 .dwk 文件,**可正常使用** |
|
||||
| 动态/日志(最新日志、全部日志) | 无改动 | 数据来自 WebSocket log,与端无关 |
|
||||
|
||||
### 3.3 帮助与其它
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 帮助(打开帮助文档) | 有改动 | 桌面端:IPC 打开本机 PDF;网页端:`window.open(HELP_PDF_URL)`,需部署或配置帮助 PDF 地址 |
|
||||
| 关于我们弹窗 | 无改动 | 纯前端 |
|
||||
|
||||
---
|
||||
|
||||
## 四、作业弹窗(WorkAdd 组件)
|
||||
|
||||
### 4.1 作业配置
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 任务名称、CD 类型、刻录类型、标签、UDF、关联任务等表单 | 无改动 | 纯表单与校验 |
|
||||
| 模板下拉(从 User Templates 加载) | 有改动 | 桌面端:读本地「User Templates」目录;网页端:readdir 不可用,模板列表为空,**网页端无法使用本地模板列表**;若需网页端模板,需后端提供列表接口 |
|
||||
| 选择模板(加载 .soon 文件) | 有改动 | 仅桌面端可用(依赖本地路径 + readFile);网页端无模板列表,该入口不适用 |
|
||||
| 「···」选择作业文件(.soon) | 有改动 | 桌面端:系统对话框选文件;网页端:可用平台 showOpenFileDialog(input file),**单文件选择可用**;多选仍仅桌面端 |
|
||||
| 新建标签(打开 SoonDesign) | **网页端无法实现** | 依赖桌面端 exec('soondesign'),网页端按钮改为灰色 + tooltip「仅桌面端支持」 |
|
||||
|
||||
### 4.2 文件与路径
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 添加文件(单/多选) | 有改动 | 桌面端:系统对话框 + 本地路径 + stat;网页端:input file 单文件,以「web://」虚拟路径加入列表,**可添加单文件** |
|
||||
| 添加文件夹 | **网页端无法实现** | 依赖系统选择目录,网页端 showOpenDirectoryDialog 拒绝,提示「仅桌面端支持」 |
|
||||
| 拖放添加文件/文件夹 | 有改动 | 桌面端:支持路径与 calcSize;网页端:仅支持 File 对象(无 path),用 name 与 size,不计算目录大小,**可拖放文件** |
|
||||
| 选择路径(归档路径) | **网页端无法实现** | 依赖系统选择目录,网页端不支持 |
|
||||
| 保存作业为 .dwk | 见「三、首页」保存作业 | 网页端为下载 |
|
||||
|
||||
### 4.3 提交与打包
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 提交任务(刻录/打印) | 有改动 | 桌面端:提交本地路径 + mtime 等,由本机服务访问本地文件;网页端:当前提交的为「web://」虚拟路径,**后端无法按路径读文件,故网页端提交任务需后端提供文件上传接口**,否则仅桌面端可完整使用 |
|
||||
| 复制文件到某路径(file-back 复制) | **网页端无法实现** | 依赖 fs.cp,仅桌面端;网页端点击会提示「仅桌面端支持」 |
|
||||
| 压缩为 zip/加密 zip(file-back) | **网页端无法实现** | 依赖 fs stream + archiver,仅桌面端;网页端会提示「仅桌面端支持」 |
|
||||
|
||||
---
|
||||
|
||||
## 五、系统管理(/manage)
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 设置光驱(手动设置光驱、设置光盘桶类型) | 无改动 | 全部通过 WebSocket 与后端/设备通信,网页端在连接服务后**可正常使用** |
|
||||
| 校准设备、维护设备 | 无改动 | WebSocket 下发指令,网页端可用 |
|
||||
| 刷新时间、执行 | 无改动 | WebSocket,网页端可用 |
|
||||
| 清除任务(服务重启时是否删除已完成/已取消任务) | 无改动 | WebSocket/配置,网页端可用 |
|
||||
| 新手引导开关 | 无改动 | 本地存储,网页端可用 |
|
||||
| 光盘类型列表(getCurrentOSDiscTypes) | 有改动 | fileSize 在网页端返回固定 1024 与 web 类型,manage 仅读配置列表,**网页端可正常使用** |
|
||||
|
||||
---
|
||||
|
||||
## 六、系统配置(/set)
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| LogLevel、AutoRetryTimes、PrintQuality、CacheDir、SkipSr0Check 等 | 无改动 | 通过 WebSocket 获取/提交系统配置,网页端**可正常使用** |
|
||||
| 保存 | 无改动 | websocketsend,网页端可用 |
|
||||
|
||||
---
|
||||
|
||||
## 七、用户管理(/user)
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 用户列表展示(当前为静态示例数据) | 无改动 | 纯前端表格,若后续接真实接口则与端无关 |
|
||||
|
||||
---
|
||||
|
||||
## 八、其它
|
||||
|
||||
| 功能 | 实现情况 | 说明 |
|
||||
|------|----------|------|
|
||||
| 404 页 | 无改动 | 纯静态 |
|
||||
| TabBar 导航 | 无改动 | 路由跳转 |
|
||||
| WebSocket 连接(chat 模块) | 有改动 | 网页端通过 VUE_APP_SOCKET_API 连接,需后端或本机服务提供 WebSocket;**网页端需能访问该地址**(同源或 CORS/代理) |
|
||||
| 文件大小显示(filterSize、fileSize) | 有改动 | fileSize 在网页端 getOS 返回 'web'、getBaseSize 固定 1024,**显示正常** |
|
||||
| 右键菜单(show-context-menu) | 有改动 | 桌面端 IPC;网页端 no-op,无影响 |
|
||||
|
||||
---
|
||||
|
||||
## 九、总结表
|
||||
|
||||
| 类型 | 数量 | 说明 |
|
||||
|------|------|------|
|
||||
| **无改动** | 绝大多数 | 登录、配置、设备指令、作业列表、重试/取消、日志、系统管理、系统配置等均与运行环境无关或仅依赖 WebSocket/HTTP |
|
||||
| **有改动且网页端可用** | 若干 | 打开作业(选文件 + 读内容)、保存作业(下载)、帮助(新标签打开 PDF)、添加单文件、拖放文件、服务开关/激活 UI 占位与提示 |
|
||||
| **网页端无法实现** | 少量 | 服务启停(执行 control.sh)、前往激活(执行 regist.sh)、新建标签(打开 SoonDesign)、选择目录、复制到本地路径、本地 zip 打包;提交任务在无上传接口时仅桌面端完整可用 |
|
||||
|
||||
---
|
||||
|
||||
## 十、网页端使用前提
|
||||
|
||||
1. **后端与 WebSocket**:配置 `BASE_API`、`VUE_APP_SOCKET_API`,确保登录接口与 WebSocket 可访问(同源或代理/CORS)。
|
||||
2. **帮助文档**:如需帮助入口,部署帮助 PDF 并配置 `HELP_PDF_URL`(或使用默认 `/help/User Manual.pdf`)。
|
||||
3. **提交任务**:若要在网页端完整使用「提交刻录/打印任务」,需后端提供文件上传接口,前端改为先上传再提交任务参数;当前实现仍为提交路径,仅桌面端可被本机服务访问。
|
||||
|
||||
按当前实现,网页端可正常完成:登录、看板查看、设备状态与指令、作业列表与重试/取消、打开/保存作业(选文件与下载)、系统管理、系统配置、日志查看等;仅上述「网页端无法实现」项在网页端为禁用或提示。
|
||||
Binary file not shown.
@@ -12,6 +12,7 @@
|
||||
"build:dir": "node .electron-vue/build.js && electron-builder --dir",
|
||||
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
|
||||
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
|
||||
"dev:web": "cross-env NODE_ENV=development node node_modules/webpack-dev-server/bin/webpack-dev-server.js --config .electron-vue/webpack.web.config.js --open",
|
||||
"electron:linux": "vue-cli-service electron:build -l --x64",
|
||||
"electron:arm": "vue-cli-service electron:build -l --arm64",
|
||||
"dev": "node .electron-vue/dev-runner.js",
|
||||
@@ -33,6 +34,10 @@
|
||||
{
|
||||
"from": "./cardsoonServer",
|
||||
"to": "."
|
||||
},
|
||||
{
|
||||
"from": "./help",
|
||||
"to": "."
|
||||
}
|
||||
],
|
||||
"directories": {
|
||||
|
||||
+4
-4
@@ -3,8 +3,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DiscWorker V1.01</title>
|
||||
<% if (htmlWebpackPlugin.options.nodeModules) { %>
|
||||
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
|
||||
<% 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) -->
|
||||
<script>
|
||||
require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
|
||||
</script>
|
||||
@@ -12,8 +12,8 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- Set `__static` path to static files in production -->
|
||||
<% if (!process.browser) { %>
|
||||
<!-- Set `__static` path to static files in production (Electron only; do not inject in web build) -->
|
||||
<% if (typeof isWeb === 'undefined' ? !process.browser : !isWeb) { %>
|
||||
<script>
|
||||
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
|
||||
</script>
|
||||
|
||||
+38
-1
@@ -1,4 +1,6 @@
|
||||
import { app, BrowserWindow, Menu } from 'electron'
|
||||
import { app, BrowserWindow, Menu, ipcMain, shell } from 'electron'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import '../renderer/store'
|
||||
|
||||
/**
|
||||
@@ -14,6 +16,18 @@ const winURL = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9080`
|
||||
: `file://${__dirname}/index.html`
|
||||
|
||||
// 获取应用根目录
|
||||
// 开发环境:项目根目录(__dirname 指向 dist/electron)
|
||||
// 生产环境:可执行文件所在目录
|
||||
const root = process.env.NODE_ENV === "development"
|
||||
? path.resolve(__dirname, '../../')
|
||||
: path.dirname(app.getPath("exe"))
|
||||
|
||||
// 日志函数
|
||||
function writeLog(message) {
|
||||
console.log(`[${new Date().toISOString()}] ${message}`)
|
||||
}
|
||||
|
||||
function createWindow () {
|
||||
/**
|
||||
* Initial window options
|
||||
@@ -75,3 +89,26 @@ app.on('ready', () => {
|
||||
if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates()
|
||||
})
|
||||
*/
|
||||
|
||||
// IPC 处理:打开帮助文件
|
||||
ipcMain.on('open-help-file', async event => {
|
||||
const helpFilePath = path.join(root, "help", "User Manual.pdf");
|
||||
|
||||
if (!fs.existsSync(helpFilePath)) {
|
||||
const errorMsg = `帮助文件不存在: ${helpFilePath}`;
|
||||
writeLog(errorMsg);
|
||||
if (event.reply) {
|
||||
event.reply('open-help-file-error', errorMsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await shell.openPath(helpFilePath);
|
||||
} catch (error) {
|
||||
writeLog("打开帮助文件错误: " + error.message);
|
||||
if (event.reply) {
|
||||
event.reply('open-help-file-error', error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -228,4 +228,54 @@ export default {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
// 引导样式
|
||||
.guide_body {
|
||||
position: relative;
|
||||
z-index: 200;
|
||||
background-color: #fff;
|
||||
padding: 0 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px dashed #2e9bfb;
|
||||
height: 100%;
|
||||
}
|
||||
.guide_box {
|
||||
border: 1px dashed #2e9bfb;
|
||||
padding: 12px;
|
||||
.guide_title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
color: #000000;
|
||||
line-height: 30px;
|
||||
span {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
.guide_desc {
|
||||
font-size: 13px;
|
||||
color: #000000;
|
||||
line-height: 24px;
|
||||
a {
|
||||
color: #2e9bfb;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.guide_btns {
|
||||
padding: 10px 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
.guide_btn1 {
|
||||
color: #999;
|
||||
}
|
||||
.guide_btn2 {
|
||||
background-color: #2e9bfb;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-popover {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 307 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 205 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
@@ -8,8 +8,9 @@
|
||||
</div>
|
||||
<div class="top_left_line"></div>
|
||||
<div class="flex_box flex_col_top tab_box">
|
||||
<div @click="goPath(item)" v-for="item in tabs" :key="item.id" class="flex_box flex_row_center flex_col tab_item" :class="{'tab_item1': active===item.id}">
|
||||
<div class="tab_name">{{item.name}}</div>
|
||||
<div @click="goPath(item)" v-for="item in tabs" :key="item.id"
|
||||
class="flex_box flex_row_center flex_col tab_item" :class="{ 'tab_item1': active === item.id }">
|
||||
<div class="tab_name">{{ item.name }}</div>
|
||||
<div class="tab_line"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -20,112 +21,120 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import UserInfo from '../userInfo/userInfo.vue';
|
||||
export default {
|
||||
name: 'TabBar',
|
||||
components: { UserInfo },
|
||||
props: {
|
||||
active: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tabs: [
|
||||
{
|
||||
id: 1,
|
||||
name: '系统管理',
|
||||
path: '/manage'
|
||||
},
|
||||
// {
|
||||
// id: 2,
|
||||
// name: '用户管理',
|
||||
// path: '/user'
|
||||
// },
|
||||
{
|
||||
id: 3,
|
||||
name: '系统配置',
|
||||
path: '/set'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['name', 'roles'])
|
||||
},
|
||||
methods: {
|
||||
goHome() {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
goPath(e) {
|
||||
console.log(e)
|
||||
if (e.id === this.active) {
|
||||
return
|
||||
import { mapGetters } from 'vuex'
|
||||
import UserInfo from '../userInfo/userInfo.vue';
|
||||
export default {
|
||||
name: 'TabBar',
|
||||
components: { UserInfo },
|
||||
props: {
|
||||
active: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabs: [
|
||||
{
|
||||
id: 1,
|
||||
name: '系统管理',
|
||||
path: '/manage'
|
||||
},
|
||||
// {
|
||||
// id: 2,
|
||||
// name: '用户管理',
|
||||
// path: '/user'
|
||||
// },
|
||||
{
|
||||
id: 3,
|
||||
name: '系统配置',
|
||||
path: '/set'
|
||||
}
|
||||
this.$router.replace(e.path)
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['name', 'roles'])
|
||||
},
|
||||
methods: {
|
||||
goHome() {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
goPath(e) {
|
||||
console.log(e)
|
||||
if (e.id === this.active) {
|
||||
return
|
||||
}
|
||||
this.$router.replace(e.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top_box {
|
||||
height: 86px;
|
||||
padding: 0 32px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 8px solid #F8F8F8;
|
||||
.top_left {
|
||||
.top_btn1 {
|
||||
cursor: pointer;
|
||||
padding: 0 16px;
|
||||
height: 30px;
|
||||
background: #00C325;
|
||||
border-radius: 4px;
|
||||
margin-right: 16px;
|
||||
.top_box {
|
||||
height: 86px;
|
||||
padding: 0 32px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 8px solid #F8F8F8;
|
||||
|
||||
.top_left {
|
||||
.top_btn1 {
|
||||
cursor: pointer;
|
||||
padding: 0 16px;
|
||||
height: 30px;
|
||||
background: #009688;
|
||||
border-radius: 4px;
|
||||
margin-right: 16px;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
|
||||
img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.top_left_line {
|
||||
width: 2px;
|
||||
height: 30px;
|
||||
background: #E0E0E0;
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.tab_box {
|
||||
.tab_item {
|
||||
padding: 20px 16px;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 8px;
|
||||
color: #000000;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
.tab_name {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.tab_line {
|
||||
display: none;
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
background: #009688;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.top_left_line {
|
||||
width: 2px;
|
||||
height: 30px;
|
||||
background: #E0E0E0;
|
||||
margin: 0 16px;
|
||||
}
|
||||
.tab_box {
|
||||
.tab_item {
|
||||
padding: 20px 16px;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
color: #000000;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
.tab_name {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.tab_line {
|
||||
display: none;
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
background: #00C325;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.tab_item1 {
|
||||
color: #00C325;
|
||||
font-weight: bold;
|
||||
.tab_line {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tab_item1 {
|
||||
color: #009688;
|
||||
font-weight: bold;
|
||||
|
||||
.tab_line {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex_box top_right">
|
||||
<div class="top_right_name">你好,{{name}}</div>
|
||||
<div class="top_right_name">你好,{{ name }}</div>
|
||||
<!-- <div @click="goOut" class="top_right_btn">登出</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'UserInfo',
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['name', 'roles'])
|
||||
},
|
||||
methods: {
|
||||
goOut() {
|
||||
this.$store.dispatch('FedLogOut').then(() => {
|
||||
location.reload()// In order to re-instantiate the vue-router object to avoid bugs
|
||||
})
|
||||
}
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'UserInfo',
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['name', 'roles'])
|
||||
},
|
||||
methods: {
|
||||
goOut() {
|
||||
this.$store.dispatch('FedLogOut').then(() => {
|
||||
location.reload()// In order to re-instantiate the vue-router object to avoid bugs
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top_right {
|
||||
.top_right_name {
|
||||
font-weight: 500;
|
||||
font-size: 21px;
|
||||
color: #000000;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.top_right_btn {
|
||||
font-weight: 500;
|
||||
font-size: 21px;
|
||||
color: #00C325;
|
||||
line-height: 50px;
|
||||
padding-left: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top_right {
|
||||
.top_right_name {
|
||||
font-weight: 500;
|
||||
font-size: 21px;
|
||||
color: #000000;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.top_right_btn {
|
||||
font-weight: 500;
|
||||
font-size: 21px;
|
||||
color: #009688;
|
||||
line-height: 50px;
|
||||
padding-left: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
// 使用promisify方法来promise化指定方法
|
||||
const { promisify } = require("util");
|
||||
const stat = promisify(fs.stat);
|
||||
const readdir = promisify(fs.readdir);
|
||||
const IS_WEB = process.env.IS_WEB === 'true';
|
||||
let fs, path, stat, readdir;
|
||||
if (!IS_WEB) {
|
||||
fs = require("fs");
|
||||
path = require("path");
|
||||
const { promisify } = require("util");
|
||||
stat = promisify(fs.stat);
|
||||
readdir = promisify(fs.readdir);
|
||||
}
|
||||
|
||||
// 异步
|
||||
// 异步(网页端不计算目录大小,直接 callback 0)
|
||||
export async function calcSize(dirPath, callback) {
|
||||
if (IS_WEB || !stat) {
|
||||
callback(null, 0, dirPath);
|
||||
return;
|
||||
}
|
||||
let fileSize = 0;
|
||||
let error = null;
|
||||
async function calc(dirPath) {
|
||||
@@ -14,9 +21,7 @@ export async function calcSize(dirPath, callback) {
|
||||
const statObj = await stat(dirPath);
|
||||
if (statObj.isDirectory()) {
|
||||
const files = await readdir(dirPath);
|
||||
let dirs = files.map((item) => {
|
||||
return path.join(dirPath, item);
|
||||
});
|
||||
let dirs = files.map((item) => path.join(dirPath, item));
|
||||
let index = 0;
|
||||
async function next() {
|
||||
if (index < dirs.length) {
|
||||
@@ -38,7 +43,9 @@ export async function calcSize(dirPath, callback) {
|
||||
}
|
||||
|
||||
export function getFileName(name) {
|
||||
return name.substring(name.lastIndexOf("\\") + 1);
|
||||
if (!name) return '';
|
||||
const i = Math.max(name.lastIndexOf('\\'), name.lastIndexOf('/'));
|
||||
return i < 0 ? name : name.substring(i + 1);
|
||||
}
|
||||
export function getExtension(name) {
|
||||
return name.substring(name.lastIndexOf(".") + 1);
|
||||
@@ -52,7 +59,8 @@ export function bytesToSize(bytes) {
|
||||
return (bytes / Math.pow(k, i)).toPrecision(3) + " " + sizes[i];
|
||||
}
|
||||
|
||||
export function isFolder(path) {
|
||||
let _stat = fs.lstatSync(path);
|
||||
export function isFolder(filePath) {
|
||||
if (IS_WEB || !fs) return false;
|
||||
let _stat = fs.lstatSync(filePath);
|
||||
return _stat.isDirectory();
|
||||
}
|
||||
|
||||
@@ -52,15 +52,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const { dialog } = require("@electron/remote");
|
||||
const fs = require("fs");
|
||||
import platform from "@/platform";
|
||||
import fileEmpty from "./fileEmpty";
|
||||
import fileList from "./fileList";
|
||||
import progressdialog from "./progressdialog";
|
||||
import archiverdialog from "./archiverdialog";
|
||||
import { calcSize, getFileName, isFolder } from "./calc";
|
||||
import { copy } from "./copy";
|
||||
import { zip } from "./archiver";
|
||||
const copyFn = process.env.IS_WEB !== 'true' ? require("./copy").copy : null;
|
||||
const zipFn = process.env.IS_WEB !== 'true' ? require("./archiver").zip : null;
|
||||
export default {
|
||||
name: "Files",
|
||||
props: {
|
||||
@@ -105,15 +104,15 @@ export default {
|
||||
e.preventDefault();
|
||||
// e.stopPropagation();
|
||||
for (const f of e.dataTransfer.files) {
|
||||
const pathKey = f.path || ('web://' + (f.name || 'file') + '_' + Date.now() + Math.random());
|
||||
const isFolder = _this.dropFolderCheck(f);
|
||||
_this.insertList({
|
||||
name: getFileName(f.path),
|
||||
path: f.path,
|
||||
name: getFileName(f.path || f.name),
|
||||
path: pathKey,
|
||||
size: isFolder ? -1 : f.size,
|
||||
folder: isFolder,
|
||||
});
|
||||
|
||||
if (isFolder) {
|
||||
if (isFolder && platform.hasNativeFs && platform.hasNativeFs()) {
|
||||
calcSize(f.path, _this.folderCalcCallback);
|
||||
}
|
||||
}
|
||||
@@ -140,45 +139,29 @@ export default {
|
||||
},
|
||||
addFile() {
|
||||
const _this = this;
|
||||
dialog
|
||||
.showOpenDialog({
|
||||
properties: ["multiSelections"],
|
||||
})
|
||||
.then(async (res) => {
|
||||
for (const item of res.filePaths) {
|
||||
await fs.stat(item, function (err, res) {
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
_this.insertList({
|
||||
name: getFileName(item),
|
||||
path: item,
|
||||
size: res.size,
|
||||
folder: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
platform.showOpenFileDialog({ properties: ["multiSelections"] }).then((rel) => {
|
||||
if (rel.file && !(platform.hasNativeFs && platform.hasNativeFs())) {
|
||||
const pathKey = 'web://' + rel.file.name + '_' + Date.now();
|
||||
_this.insertList({ name: getFileName(rel.file.name), path: pathKey, size: rel.file.size || 0, folder: false });
|
||||
return;
|
||||
}
|
||||
(rel.filePaths || []).forEach((item) => {
|
||||
platform.stat(item).then((res) => {
|
||||
_this.insertList({ name: getFileName(item), path: item, size: res.size, folder: false });
|
||||
}).catch(() => {});
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
addFolder() {
|
||||
const _this = this;
|
||||
dialog
|
||||
.showOpenDialog({
|
||||
properties: ["openDirectory", "multiSelections"],
|
||||
})
|
||||
.then((res) => {
|
||||
for (const item of res.filePaths) {
|
||||
const result = _this.insertList({
|
||||
name: getFileName(item),
|
||||
path: item,
|
||||
size: -1,
|
||||
folder: true,
|
||||
});
|
||||
if (result) {
|
||||
calcSize(item, _this.folderCalcCallback);
|
||||
}
|
||||
platform.showOpenDirectoryDialog({ properties: ["openDirectory", "multiSelections"] }).then((res) => {
|
||||
(res.filePaths || []).forEach((item) => {
|
||||
const result = _this.insertList({ name: getFileName(item), path: item, size: -1, folder: true });
|
||||
if (result && platform.hasNativeFs && platform.hasNativeFs()) {
|
||||
calcSize(item, _this.folderCalcCallback);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
folderCalcCallback(err, res, path) {
|
||||
if (this.filesList[path]) {
|
||||
@@ -194,17 +177,9 @@ export default {
|
||||
this.allNumber--;
|
||||
},
|
||||
dropFolderCheck(f) {
|
||||
//T是文件夹 F不是文件夹
|
||||
//拖放无法从参数判断是否为文件夹,需要额外处理
|
||||
if (f.size != 0 && f.size != 4096) {
|
||||
//返回大小不是0,则不是文件夹
|
||||
return false;
|
||||
}
|
||||
if (f.type != "") {
|
||||
//如果type不是空,则不是文件夹
|
||||
return false;
|
||||
}
|
||||
return isFolder(f.path);
|
||||
if (f.size != 0 && f.size != 4096) return false;
|
||||
if (f.type != "") return false;
|
||||
return isFolder(f.path || f.name || '');
|
||||
},
|
||||
sizeChange(size) {
|
||||
this.allSize = this.allSize + size;
|
||||
@@ -255,18 +230,13 @@ export default {
|
||||
if (this.isCopy) {
|
||||
this.overNumber = 0;
|
||||
this.changeProgressvisible(true);
|
||||
if (!copyFn) {
|
||||
this.$message && this.$message({ message: '仅桌面端支持', type: 'warning' });
|
||||
return;
|
||||
}
|
||||
for (let i in this.filesList) {
|
||||
// const path = "D:\\copytest\\1\\" + this.filesList[i].name;
|
||||
const path = this.copyPath + this.filesList[i].name;
|
||||
console.log(this.copyPath);
|
||||
console.log(path);
|
||||
copy(
|
||||
i,
|
||||
path,
|
||||
this.filesList[i].folder,
|
||||
this.fileBack,
|
||||
this.filesList[i]
|
||||
);
|
||||
copyFn(i, path, this.filesList[i].folder, this.fileBack, this.filesList[i]);
|
||||
}
|
||||
} else {
|
||||
//2023-04-24修改为所有都只上传文件路径,不需要压缩
|
||||
@@ -281,18 +251,24 @@ export default {
|
||||
} else if (file_form == 1) {
|
||||
//电子光盘
|
||||
} else if (file_form == 2) {
|
||||
//zip
|
||||
if (!zipFn) {
|
||||
this.$message && this.$message({ message: '仅桌面端支持', type: 'warning' });
|
||||
return;
|
||||
}
|
||||
this.archiverIsover = false;
|
||||
this.archiverIsfalse = false;
|
||||
this.zip_path = "D:/archivertest/1.zip";
|
||||
zip(this.filesList, this.zip_path, this.archiverBack, false);
|
||||
zipFn(this.filesList, this.zip_path, this.archiverBack, false);
|
||||
} else if (file_form == 3) {
|
||||
//加密zip
|
||||
if (!zipFn) {
|
||||
this.$message && this.$message({ message: '仅桌面端支持', type: 'warning' });
|
||||
return;
|
||||
}
|
||||
this.archiverIsover = false;
|
||||
this.archiverIsfalse = false;
|
||||
this.zip_path = "D:/archivertest/2.zip";
|
||||
let password = "123456";
|
||||
zip(this.filesList, this.zip_path, this.archiverBack, true, password);
|
||||
zipFn(this.filesList, this.zip_path, this.archiverBack, true, password);
|
||||
} else if (file_form == 4) {
|
||||
//u盘
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const { dialog } = require("@electron/remote");
|
||||
const fs = require("fs");
|
||||
import platform from "@/platform";
|
||||
import fileEmpty from "./fileEmpty";
|
||||
import fileList from "./fileList";
|
||||
import progressdialog from "./progressdialog";
|
||||
@@ -109,15 +108,15 @@ export default {
|
||||
e.preventDefault();
|
||||
// e.stopPropagation();
|
||||
for (const f of e.dataTransfer.files) {
|
||||
const pathKey = f.path || ('web://' + (f.name || 'file') + '_' + Date.now() + Math.random());
|
||||
const isFolder = _this.dropFolderCheck(f);
|
||||
_this.insertList({
|
||||
name: getFileName(f.path),
|
||||
path: f.path,
|
||||
name: getFileName(f.path || f.name),
|
||||
path: pathKey,
|
||||
size: isFolder ? -1 : f.size,
|
||||
folder: isFolder,
|
||||
});
|
||||
|
||||
if (isFolder) {
|
||||
if (isFolder && platform.hasNativeFs && platform.hasNativeFs()) {
|
||||
calcSize(f.path, _this.folderCalcCallback);
|
||||
}
|
||||
}
|
||||
@@ -144,65 +143,51 @@ export default {
|
||||
},
|
||||
addFile() {
|
||||
const _this = this;
|
||||
console.log(_this.copyType)
|
||||
const properties = _this.copyType == 2 ? [] : ["multiSelections"];
|
||||
const filters = _this.copyType == 2 ? [{ name: '镜像文件', extensions: ['ISO', 'IMG'] }] : [{ name: '所有文件', extensions: ['*'] }]
|
||||
dialog
|
||||
.showOpenDialog({
|
||||
filters,
|
||||
properties
|
||||
})
|
||||
.then(async (rel) => {
|
||||
console.log(rel);
|
||||
for (const item of rel.filePaths) {
|
||||
const stats = fs.statSync(item);
|
||||
const filters = _this.copyType == 2 ? [{ name: '镜像文件', extensions: ['ISO', 'IMG'] }] : [{ name: '所有文件', extensions: ['*'] }];
|
||||
platform.showOpenFileDialog({ filters }).then((rel) => {
|
||||
if (rel.file && !platform.hasNativeFs()) {
|
||||
const pathKey = 'web://' + rel.file.name + '_' + Date.now();
|
||||
_this.insertList({ name: getFileName(rel.file.name), path: pathKey, size: rel.file.size || 0, folder: false });
|
||||
return;
|
||||
}
|
||||
const paths = rel.filePaths || [];
|
||||
paths.forEach((item) => {
|
||||
try {
|
||||
const stats = platform.statSync(item);
|
||||
if (stats.isFile()) {
|
||||
await fs.stat(item, function (err, res) {
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
platform.stat(item).then((res) => {
|
||||
if (_this.copyType == 2) {
|
||||
_this.filesList = {}
|
||||
_this.filesList = {};
|
||||
_this.allNumber = 1;
|
||||
_this.filesList[item] = {
|
||||
name: getFileName(item),
|
||||
path: item,
|
||||
size: res.size,
|
||||
folder: false,
|
||||
};
|
||||
calcSize(item, _this.folderCalcCallback);
|
||||
return
|
||||
_this.$set(_this.filesList, item, { name: getFileName(item), path: item, size: res.size, folder: false });
|
||||
_this.sizeChange(res.size);
|
||||
return;
|
||||
}
|
||||
_this.insertList({
|
||||
name: getFileName(item),
|
||||
path: item,
|
||||
size: res.size,
|
||||
folder: false,
|
||||
});
|
||||
_this.insertList({ name: getFileName(item), path: item, size: res.size, folder: false });
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
addFolder() {
|
||||
const _this = this;
|
||||
dialog
|
||||
.showOpenDialog({
|
||||
properties: ["openDirectory", "multiSelections"],
|
||||
})
|
||||
.then((res) => {
|
||||
for (const item of res.filePaths) {
|
||||
const result = _this.insertList({
|
||||
name: getFileName(item),
|
||||
path: item,
|
||||
size: -1,
|
||||
folder: true,
|
||||
});
|
||||
if (result) {
|
||||
calcSize(item, _this.folderCalcCallback);
|
||||
}
|
||||
platform.showOpenDirectoryDialog({ properties: ["openDirectory", "multiSelections"] }).then((res) => {
|
||||
const filePaths = res.filePaths || [];
|
||||
filePaths.forEach((item) => {
|
||||
const result = _this.insertList({
|
||||
name: getFileName(item),
|
||||
path: item,
|
||||
size: -1,
|
||||
folder: true,
|
||||
});
|
||||
if (result && platform.hasNativeFs && platform.hasNativeFs()) {
|
||||
calcSize(item, _this.folderCalcCallback);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
folderCalcCallback(err, res, path) {
|
||||
if (this.filesList[path]) {
|
||||
|
||||
@@ -4,194 +4,196 @@
|
||||
:close-on-click-modal="false" :show-close="false">
|
||||
<div class="work_body" v-loading="submitLoading" element-loading-text="正在准备任务,您也可以直接右上角关闭作业窗口"
|
||||
element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)">
|
||||
<div v-if="currentStep && parseInt(currentStep) >= 8" class="bg_box"
|
||||
style="position: absolute; top: 0; left: 0; z-index: 10; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5)">
|
||||
</div>
|
||||
<div @click="close" class="close_box">
|
||||
<img src="@/assets/images/Close_icon.png" />
|
||||
<i class="el-icon-close"></i>
|
||||
</div>
|
||||
<div class="work_box">
|
||||
<div class="flex_box flex_row_between work_top">
|
||||
<div class="flex_box">
|
||||
<div class="flex_box work_top_item">
|
||||
<div class="work_top_label">光盘类型:</div>
|
||||
<div class="work_top_val">
|
||||
<el-select v-model="form.cd_type" placeholder="请选择">
|
||||
<el-option v-for="item in cd_types" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[8].placement" width="250" trigger="manual"
|
||||
v-model="guideStep[8].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">新手引导<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ guideStep[8].step }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">跳过引导</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1"
|
||||
size="mini">上一步</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep ==
|
||||
guideStep.length
|
||||
- 1 ? '完成引导' : '下一步' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box work_top_item">
|
||||
<div class="work_top_label">任务类型:</div>
|
||||
<div class="work_top_val">
|
||||
<el-select v-model="form.copy_type" placeholder="请选择">
|
||||
<el-option v-for="item in copyList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<div class="flex_box" slot="reference" :class="{ 'guide_body': currentStep == 8 }">
|
||||
<div class="flex_box work_top_item">
|
||||
<div class="work_top_label">光盘类型:</div>
|
||||
<div class="work_top_val">
|
||||
<el-select v-model="form.cd_type" placeholder="请选择">
|
||||
<el-option v-for="item in cd_types" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box work_top_item">
|
||||
<div class="work_top_label">任务类型:</div>
|
||||
<div class="work_top_val">
|
||||
<el-select v-model="form.copy_type" placeholder="请选择">
|
||||
<el-option v-for="item in copyList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-popover v-model="setShow" placement="bottom-end" trigger="click" @show="initSet">
|
||||
<div class="set_box">
|
||||
<div class="set_list">
|
||||
<!-- <div class="flex_box set_item">
|
||||
<div class="set_label">优先级:</div>
|
||||
<div class="set_val">
|
||||
<el-select v-model="setForm.priority" placeholder="请选择">
|
||||
<el-option v-for="item in priorityList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">目标工作站:</div>
|
||||
<div class="set_val">
|
||||
<el-select v-model="setForm.stage" placeholder="请选择">
|
||||
<el-option v-for="item in stageList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录速率:</div>
|
||||
<div class="set_val">
|
||||
<el-select v-model="setForm.speed" placeholder="请选择">
|
||||
<el-option v-for="item in speedList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录完成后校验光盘:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isCheck" active-color="#07C160" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录完成后关闭光盘:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isClose" active-color="#07C160" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录完成后删除缓存:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isDel" active-color="#07C160" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">作业失败打印标签:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isPrint" active-color="#07C160" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">允许跨盘:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isArrow" active-color="#07C160" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">是否虚拟刻录:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.is_sim" active-color="#07C160" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="setForm.isArrow" class="flex_box flex_col_top set_item">
|
||||
<div class="set_label">分盘设置:</div>
|
||||
<div class="set_val">
|
||||
<el-radio-group v-model="setForm.disk">
|
||||
<el-radio :label="item.id" v-for="(item, index) in diskSet" :key="index">
|
||||
<div class="set_disk_name">{{ item.name }}</div>
|
||||
<div class="set_disk_desc">{{ item.desc }}</div>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box flex_row_center set_btns">
|
||||
<el-button @click="setCancel" class="set_btn1">取消</el-button>
|
||||
<el-button @click="setSure" class="set_btn2" type="primary">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box work_top_set" slot="reference">
|
||||
</el-popover>
|
||||
<template v-if="!currentStep || currentStep < 8">
|
||||
<div @click="openSetDialog" class="flex_box work_top_set">
|
||||
<img src="@/assets/images/Setting_icon.png" />
|
||||
<div>高级作业设置</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[11].placement" width="250" trigger="manual"
|
||||
v-model="guideStep[11].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">新手引导<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ guideStep[11].step }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">跳过引导</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1"
|
||||
size="mini">上一步</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep ==
|
||||
guideStep.length
|
||||
- 1 ? '完成引导' : '下一步' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="reference" :class="{ 'guide_body': currentStep == 11 }">
|
||||
<div @click="openSetDialog" class="flex_box work_top_set">
|
||||
<img src="@/assets/images/Setting_icon.png" />
|
||||
<div>高级作业设置</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex_box flex_col_top work_main">
|
||||
<div class="work_left">
|
||||
<div v-if="form.copy_type == 3 || form.copy_type == 4" class="flex_box flex_row_center work_no">
|
||||
<img src="@/assets/images/ElBanCircle.png" />
|
||||
</div>
|
||||
<div class="flex_box flex_row_between work_left_top">
|
||||
<div class="flex_box">
|
||||
<div class="work_left_top_label">内容</div>
|
||||
<div v-if="form.copy_type !== 2" @click="addFolder" class="flex_box work_left_top_btn">
|
||||
<img src="@/assets/images/file_icon.png" />
|
||||
<div>添加文件夹</div>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[9].placement" width="250" trigger="manual"
|
||||
v-model="guideStep[9].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">新手引导<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div @click="addFile" class="flex_box work_left_top_btn">
|
||||
<img src="@/assets/images/paper_icon.png" />
|
||||
<div>添加文件</div>
|
||||
<div class="guide_desc">{{ guideStep[9].step }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">跳过引导</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1"
|
||||
size="mini">上一步</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep ==
|
||||
guideStep.length
|
||||
- 1 ? '完成引导' : '下一步' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div @contextmenu.prevent="showContextMenu" class="work_left_top_input">
|
||||
<el-input placeholder="请输入卷标" v-model="form.label">
|
||||
<template slot="prepend">卷标</template>
|
||||
</el-input>
|
||||
<div slot="reference" :class="{ 'guide_body': currentStep == 9 }">
|
||||
<div v-if="form.copy_type == 3 || form.copy_type == 4" class="flex_box flex_row_center work_no">
|
||||
<img src="@/assets/images/ElBanCircle.png" />
|
||||
</div>
|
||||
<div class="flex_box flex_row_between work_left_top">
|
||||
<div class="flex_box">
|
||||
<div class="work_left_top_label">内容</div>
|
||||
<div v-if="form.copy_type !== 2" @click="addFolder" class="flex_box work_left_top_btn">
|
||||
<img src="@/assets/images/file_icon.png" />
|
||||
<div>添加文件夹</div>
|
||||
</div>
|
||||
<div @click="addFile" class="flex_box work_left_top_btn">
|
||||
<img src="@/assets/images/paper_icon.png" />
|
||||
<div>添加文件</div>
|
||||
</div>
|
||||
</div>
|
||||
<div @contextmenu.prevent="showContextMenu" class="work_left_top_input">
|
||||
<el-input placeholder="请输入卷标" v-model="form.label">
|
||||
<template slot="prepend">卷标</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="work_files">
|
||||
<div class="work_files_box">
|
||||
<!-- 文件列表 -->
|
||||
<files ref="files" :copyType="form.copy_type" :onSizechange="sizeChange" :complete="upload_over">
|
||||
</files>
|
||||
</div>
|
||||
<div class="work_files_progress">
|
||||
<el-progress :text-inside="true" :percentage="file_percent" :format="format"
|
||||
:color="customColors"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="work_files">
|
||||
<div class="work_files_box">
|
||||
<!-- 文件列表 -->
|
||||
<files ref="files" :copyType="form.copy_type" :onSizechange="sizeChange" :complete="upload_over"></files>
|
||||
</div>
|
||||
<div class="work_files_progress">
|
||||
<el-progress :text-inside="true" :percentage="file_percent" :format="format"
|
||||
:color="customColors"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="work_right">
|
||||
<!-- <div v-if="form.copy_type == 3" class="flex_box flex_row_center work_no">
|
||||
<img src="@/assets/images/ElBanCircle.png" />
|
||||
</div> -->
|
||||
<div class="flex_box work_right_top">
|
||||
<div class="flex_box">
|
||||
<div class="work_right_top_label">标签</div>
|
||||
<el-select @change="changeTemplate" v-model="currentTemplate" placeholder="选择一个标签或点击创建标签">
|
||||
<el-option v-for="item in templates" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-tooltip content="选择更多模版" placement="bottom">
|
||||
<div @click="openFile" class="work_right_top_file">···</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div @click="openDesign" class="work_right_top_add">新建标签</div>
|
||||
</div>
|
||||
<div class="wook_soon" :class="{ wook_soon1: !showList }">
|
||||
<div class="flex_box flex_row_center wook_soon_top">
|
||||
<img class="wook_soon_img" :src="soonImg" />
|
||||
<div @click="showList = !showList" class="flex_box flex_row_center wook_soon_btn"
|
||||
:class="{ wook_soon_btn1: !showList }">
|
||||
<img src="@/assets/images/Arrow_up_icon.png" />
|
||||
<el-popover v-if="guideStep" :placement="guideStep[10].placement" width="250" trigger="manual"
|
||||
v-model="guideStep[10].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">新手引导<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ guideStep[10].step }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">跳过引导</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1"
|
||||
size="mini">上一步</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep ==
|
||||
guideStep.length
|
||||
- 1 ? '完成引导' : '下一步' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wook_soon_list">
|
||||
<div v-for="(item, index) in soonList" :key="index" class="flex_box wook_soon_item">
|
||||
<div class="wook_soon_label">{{ item.name }}</div>
|
||||
<div class="wook_soon_input">
|
||||
<div v-if="item.type == 1">
|
||||
<input type="file" accept="image/*" :ref="item.origin_name" :data-name="item.origin_name" />
|
||||
</div>
|
||||
<div v-if="item.type == 3 || item.type == 4 || item.type == 5">
|
||||
<el-input clearable :disabled="csvIsExist" v-model="item.val" placeholder="请输入"></el-input>
|
||||
<div slot="reference" :class="{ 'guide_body': currentStep == 10 }">
|
||||
<!-- <div v-if="form.copy_type == 3" class="flex_box flex_row_center work_no">
|
||||
<img src="@/assets/images/ElBanCircle.png" />
|
||||
</div> -->
|
||||
<div class="flex_box work_right_top">
|
||||
<div class="flex_box">
|
||||
<div class="work_right_top_label">标签</div>
|
||||
<el-select @change="changeTemplate" v-model="currentTemplate" placeholder="选择一个标签或点击创建标签">
|
||||
<el-option v-for="item in templates" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-tooltip content="选择更多模版" placement="bottom">
|
||||
<div @click="openFile" class="work_right_top_file">···</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-if="hasRunCmd" @click="openDesign" class="work_right_top_add">新建标签</div>
|
||||
<el-tooltip v-else content="仅桌面端支持" placement="bottom"><span class="work_right_top_add work_right_top_add_disabled">新建标签</span></el-tooltip>
|
||||
</div>
|
||||
<div class="wook_soon" :class="{ wook_soon1: !showList }">
|
||||
<div class="flex_box flex_row_center wook_soon_top">
|
||||
<img class="wook_soon_img" :src="soonImg" />
|
||||
<div @click="showList = !showList" class="flex_box flex_row_center wook_soon_btn"
|
||||
:class="{ wook_soon_btn1: !showList }">
|
||||
<i class="el-icon-arrow-up"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="openFile2" v-if="item.type == 3 || item.type == 4 || item.type == 5"
|
||||
class="wook_soon_add">添加字段
|
||||
<div class="wook_soon_list">
|
||||
<div v-for="(item, index) in soonList" :key="index" class="flex_box wook_soon_item">
|
||||
<div class="wook_soon_label">{{ item.name }}</div>
|
||||
<div class="wook_soon_input">
|
||||
<div v-if="item.type == 1">
|
||||
<input type="file" accept="image/*" :ref="item.origin_name" :data-name="item.origin_name" />
|
||||
</div>
|
||||
<div v-if="item.type == 3 || item.type == 4 || item.type == 5">
|
||||
<el-input clearable :disabled="csvIsExist" v-model="item.val" placeholder="请输入"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="openFile2" v-if="item.type == 3 || item.type == 4 || item.type == 5"
|
||||
class="wook_soon_add">
|
||||
添加字段
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -209,10 +211,26 @@
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box footer_btns">
|
||||
<el-button type="primary" class="footer_save" @click="workSave" :loading="saveFlag">保存</el-button>
|
||||
<el-button type="primary" class="footer_submit" @click="workSubmit" :loading="submitFlag">提交</el-button>
|
||||
</div>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[12].placement" width="250" trigger="manual"
|
||||
v-model="guideStep[12].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">新手引导<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ guideStep[12].step }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">跳过引导</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1"
|
||||
size="mini">上一步</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep ==
|
||||
guideStep.length
|
||||
- 1 ? '完成引导' : '下一步' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box footer_btns" slot="reference" :class="{ 'guide_body': currentStep == 12 }">
|
||||
<el-button type="primary" class="footer_save" @click="workSave" :loading="saveFlag">保存</el-button>
|
||||
<el-button type="primary" class="footer_submit" @click="workSubmit" :loading="submitFlag">提交</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 上传文件区域 -->
|
||||
@@ -220,22 +238,99 @@
|
||||
<input type="file" accept=".csv" @change="fileLoad2" ref="refFile2" style="display: none" />
|
||||
<input type="file" accept="..zip,.rar" @change="fileLoad3" ref="refFile3" style="display: none" />
|
||||
</el-dialog>
|
||||
<!-- 高级作业设置弹窗 -->
|
||||
<el-dialog title="高级作业设置" :visible.sync="setShow" width="600px" @open="initSet">
|
||||
<div class="set_box">
|
||||
<div class="set_list">
|
||||
<!-- <div class="flex_box set_item">
|
||||
<div class="set_label">优先级:</div>
|
||||
<div class="set_val">
|
||||
<el-select v-model="setForm.priority" placeholder="请选择">
|
||||
<el-option v-for="item in priorityList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">目标工作站:</div>
|
||||
<div class="set_val">
|
||||
<el-select v-model="setForm.stage" placeholder="请选择">
|
||||
<el-option v-for="item in stageList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录速率:</div>
|
||||
<div class="set_val">
|
||||
<el-select v-model="setForm.speed" placeholder="请选择">
|
||||
<el-option v-for="item in speedList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录完成后校验光盘:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isCheck" active-color="#009688" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录完成后关闭光盘:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isClose" active-color="#009688" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">刻录完成后删除缓存:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isDel" active-color="#009688" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">作业失败打印标签:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isPrint" active-color="#009688" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">允许跨盘:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.isArrow" active-color="#009688" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box set_item">
|
||||
<div class="set_label">是否虚拟刻录:</div>
|
||||
<div class="set_val">
|
||||
<el-switch v-model="setForm.is_sim" active-color="#009688" inactive-color="#aaa"> </el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="setForm.isArrow" class="flex_box flex_col_top set_item">
|
||||
<div class="set_label">分盘设置:</div>
|
||||
<div class="set_val">
|
||||
<el-radio-group v-model="setForm.disk">
|
||||
<el-radio :label="item.id" v-for="(item, index) in diskSet" :key="index">
|
||||
<div class="set_disk_name">{{ item.name }}</div>
|
||||
<div class="set_disk_desc">{{ item.desc }}</div>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box flex_row_center set_btns">
|
||||
<el-button @click="setCancel" class="set_btn1">取消</el-button>
|
||||
<el-button @click="setSure" class="set_btn2" type="primary">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let fs = require('fs')
|
||||
let path = require('path')
|
||||
const { app, dialog } = require('@electron/remote')
|
||||
const { exec } = require('child_process')
|
||||
const { ipcRenderer } = require('electron')
|
||||
import { mapGetters } from 'vuex'
|
||||
const dayjs = require('dayjs')
|
||||
|
||||
import platform from '@/platform'
|
||||
import fileEmpty from './files/fileEmpty'
|
||||
import files from './files/file'
|
||||
|
||||
const exePath = !app.isPackaged ? process.cwd() : path.dirname(process.execPath)
|
||||
export default {
|
||||
name: 'UserInfo',
|
||||
components: { fileEmpty, files },
|
||||
@@ -425,6 +520,9 @@ export default {
|
||||
}
|
||||
],
|
||||
cdList: [],
|
||||
// 新手引导
|
||||
guideStep: null,
|
||||
currentStep: 0,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -461,7 +559,7 @@ export default {
|
||||
req_info: {}
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -492,33 +590,23 @@ export default {
|
||||
that.form[key] = that.saveWorkList[key]
|
||||
}
|
||||
that.form.task_name = that.saveWorkList.task_name
|
||||
// 恢复task_uuid后,同步更新csvForm.req_info.uuid
|
||||
if (that.form.task_uuid) {
|
||||
that.csvForm.req_info.uuid = that.form.task_uuid
|
||||
}
|
||||
if (that.saveWorkList.json_file) {
|
||||
fs.readFile(that.saveWorkList.json_file, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error('读取文件时出错:', err)
|
||||
return
|
||||
}
|
||||
let name = that.saveWorkList.json_file.split('\\')[1]
|
||||
that.fileLists[0] = new File([data], name, {
|
||||
type: ''
|
||||
})
|
||||
// that.readFile(data)
|
||||
})
|
||||
platform.readFile(that.saveWorkList.json_file, 'utf8').then((data) => {
|
||||
const name = (that.saveWorkList.json_file || '').split(/[/\\]/).pop() || 'file'
|
||||
that.fileLists[0] = new File([data], name, { type: '' })
|
||||
}).catch((err) => { console.error('读取文件时出错:', err) })
|
||||
}
|
||||
if (that.saveWorkList.udf_file) {
|
||||
that.csvIsExist = true
|
||||
console.log(that.saveWorkList.udf_file, that.csvIsExist)
|
||||
fs.readFile(that.saveWorkList.udf_file, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error('读取文件时出错:', err)
|
||||
return
|
||||
}
|
||||
let name = that.saveWorkList.udf_file.split('\\')[1]
|
||||
platform.readFile(that.saveWorkList.udf_file, 'utf8').then((data) => {
|
||||
const name = (that.saveWorkList.udf_file || '').split(/[/\\]/).pop() || 'file'
|
||||
that.file_name = name
|
||||
that.fileLists[0] = new File([data], name, {
|
||||
type: ''
|
||||
})
|
||||
})
|
||||
that.fileLists[0] = new File([data], name, { type: '' })
|
||||
}).catch((err) => { console.error('读取文件时出错:', err) })
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -527,6 +615,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['name', 'roles']),
|
||||
hasRunCmd() {
|
||||
return platform.hasRunCmd && platform.hasRunCmd()
|
||||
},
|
||||
file_percent() {
|
||||
let disk = this.cd_types.find((item) => item.value === this.form.cd_type)
|
||||
if (!disk) {
|
||||
@@ -535,7 +626,7 @@ export default {
|
||||
const diskSize = disk.size
|
||||
let t = diskSize ? parseFloat(this.$accDiv(this.totalSize, diskSize)) * 100 : 0
|
||||
return t > 100 ? 100 : t
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
let workForm = localStorage.getItem('workForm')
|
||||
@@ -547,13 +638,21 @@ export default {
|
||||
if (setForm) {
|
||||
this.$store.dispatch('setDatas', { name: 'setForm', data: JSON.parse(setForm) })
|
||||
}
|
||||
|
||||
// 新手引导
|
||||
const guideStepStr = localStorage.getItem('guideStep')
|
||||
this.guideStep = guideStepStr ? JSON.parse(guideStepStr) : null
|
||||
this.currentStep = localStorage.getItem('currentStep') || -1
|
||||
if (this.guideStep && this.guideStep[this.currentStep]) {
|
||||
this.guideStep[this.currentStep].show = false
|
||||
setTimeout(() => {
|
||||
this.guideStep[this.currentStep].show = true
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 右键事件
|
||||
showContextMenu() {
|
||||
console.log(123)
|
||||
ipcRenderer.send('show-context-menu');
|
||||
platform.showContextMenu()
|
||||
},
|
||||
// 进度条处理
|
||||
format(percentage) {
|
||||
@@ -575,15 +674,13 @@ export default {
|
||||
sizeChange(size) {
|
||||
this.totalSize = size
|
||||
},
|
||||
// 选择路径
|
||||
// 选择路径(仅桌面端支持)
|
||||
selectPath() {
|
||||
dialog
|
||||
.showOpenDialog({
|
||||
properties: ["openDirectory"],
|
||||
})
|
||||
platform.showOpenDirectoryDialog({ properties: ['openDirectory'] })
|
||||
.then((res) => {
|
||||
if (res.filePaths[0]) this.form.archive_path = res.filePaths[0]
|
||||
});
|
||||
if (res.filePaths && res.filePaths[0]) this.form.archive_path = res.filePaths[0]
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
async upload_over() { },
|
||||
// 保存
|
||||
@@ -619,21 +716,24 @@ export default {
|
||||
save.soonImg = that.soonImg
|
||||
save.soonList = that.soonList
|
||||
const v = JSON.stringify(save)
|
||||
dialog
|
||||
.showSaveDialog({
|
||||
platform
|
||||
.showSaveFileDialog({
|
||||
title: 'Save',
|
||||
filters: [{ name: 'Soon Work', extensions: ['dwk'] }]
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.filePath == "") { return; }
|
||||
if (result.filePath.substring(result.filePath.length - 5).indexOf('.') == -1) {
|
||||
result.filePath += '.dwk';
|
||||
let filePath = result.filePath || ''
|
||||
if (!filePath) return
|
||||
if (filePath.substring(filePath.length - 5).indexOf('.') == -1) {
|
||||
filePath += '.dwk'
|
||||
}
|
||||
if (platform.hasNativeFs && platform.hasNativeFs()) {
|
||||
platform.writeFileSync(filePath, v)
|
||||
that.$notify({ message: '保存成功至' + filePath, type: 'success' })
|
||||
} else {
|
||||
platform.downloadFile(v, filePath)
|
||||
that.$notify({ message: '已下载 ' + filePath, type: 'success' })
|
||||
}
|
||||
fs.writeFileSync(result.filePath, v)
|
||||
that.$notify({
|
||||
message: '保存成功至' + result.filePath,
|
||||
type: 'success'
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
@@ -661,16 +761,21 @@ export default {
|
||||
for (let key in files) {
|
||||
let file = { ...files[key] }
|
||||
try {
|
||||
const fileStats = fs.statSync(files[key].path)
|
||||
const fileStats = platform.statSync(files[key].path)
|
||||
file.mtime = fileStats.mtimeMs
|
||||
fileList.push(file)
|
||||
} catch (error) {
|
||||
console.error('Error reading file:', error)
|
||||
if (platform.hasNativeFs && platform.hasNativeFs()) {
|
||||
console.error('Error reading file:', error)
|
||||
} else {
|
||||
file.mtime = Date.now()
|
||||
fileList.push(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
let isISO = fileList.every(item => item.name.indexOf('.ISO') > -1 || item.name.indexOf('.IMG') > -1 || item.name.indexOf('.iso') > -1 || item.name.indexOf('.img') > -1)
|
||||
if (that.form.copy_type == 2) {
|
||||
if (fileList.length != 1 || !isISO) {
|
||||
if (fileList.length != 1 || !isISO) {
|
||||
this.$notify({
|
||||
message: '仅可上传一个类型为ISO/IMG的文件',
|
||||
type: 'warning'
|
||||
@@ -686,7 +791,7 @@ export default {
|
||||
return
|
||||
}
|
||||
if (that.form.copy_type == 3) {
|
||||
if(that.cdList.length > 0) {
|
||||
if (that.cdList.length > 0) {
|
||||
that.$store
|
||||
.dispatch('chat/websocketsend', {
|
||||
req_name: 'openCDDevByPath',
|
||||
@@ -865,41 +970,30 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取模板文件列表
|
||||
// 获取模板文件列表(仅桌面端有本地模板目录)
|
||||
getTemplates() {
|
||||
let that = this
|
||||
const filePath = path.join(exePath, 'User Templates');
|
||||
fs.readdir(filePath, (err, files) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
console.log(files)
|
||||
const fileList = files.map((file) => {
|
||||
return {
|
||||
label: file,
|
||||
value: path.join('User Templates', file)
|
||||
}
|
||||
})
|
||||
that.templates = fileList
|
||||
}
|
||||
const that = this
|
||||
const filePath = platform.pathJoin(platform.getAppRoot(), 'User Templates')
|
||||
platform.readdir(filePath).then((files) => {
|
||||
const fileList = (files || []).map((file) => ({
|
||||
label: file,
|
||||
value: platform.pathJoin('User Templates', file)
|
||||
}))
|
||||
that.templates = fileList
|
||||
}).catch(() => {
|
||||
that.templates = []
|
||||
})
|
||||
},
|
||||
// 选择模板
|
||||
// 选择模板(仅桌面端)
|
||||
changeTemplate(e) {
|
||||
let that = this
|
||||
const fullPath = path.join(exePath, e)
|
||||
const that = this
|
||||
const fullPath = platform.pathJoin(platform.getAppRoot(), e)
|
||||
that.form.json_file = fullPath
|
||||
fs.readFile(fullPath, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error('读取文件时出错:', err)
|
||||
return
|
||||
}
|
||||
let name = path.basename(fullPath)
|
||||
that.fileLists[0] = new File([data], name, {
|
||||
type: ''
|
||||
})
|
||||
platform.readFile(fullPath, 'utf8').then((data) => {
|
||||
const name = platform.pathBasename(fullPath)
|
||||
that.fileLists[0] = new File([data], name, { type: '' })
|
||||
that.readFile(data)
|
||||
})
|
||||
}).catch((err) => { console.error('读取文件时出错:', err) })
|
||||
},
|
||||
// 读取文件信息
|
||||
readFile(data) {
|
||||
@@ -938,19 +1032,10 @@ export default {
|
||||
that.file_name = null
|
||||
that.file_name3 = '添加图片文件'
|
||||
},
|
||||
// 打开标签程序
|
||||
// 打开标签程序(仅桌面端)
|
||||
openDesign() {
|
||||
// 启动exe程序
|
||||
console.log('启动soondesign')
|
||||
// exec('"D:\\Program Files\\Cardsoon\\SoonDesign\\SoonDesign.exe"', (error, stdout, stderr) => {
|
||||
exec('soondesign', (error, stdout, stderr) => {
|
||||
|
||||
if (error) {
|
||||
console.error(`执行的错误: ${error}`)
|
||||
return
|
||||
}
|
||||
// console.log(`stdout: ${stdout}`)
|
||||
// console.error(`stderr: ${stderr}`)
|
||||
platform.runCmd('soondesign').catch((err) => {
|
||||
console.error('执行的错误:', err)
|
||||
})
|
||||
},
|
||||
// 文件上传处理
|
||||
@@ -1021,16 +1106,28 @@ export default {
|
||||
show() {
|
||||
this.addShow = true
|
||||
this.getTemplates()
|
||||
this.form.task_uuid = this.genTaskUUID()
|
||||
this.csvForm.req_info.uuid = this.form.task_uuid
|
||||
this.form.label = dayjs().format('YYYY-MM-DD')
|
||||
console.log(this.form.task_uuid)
|
||||
// 只有新建作业时才生成新的task_uuid和label,打开已有文件时保留原有的值
|
||||
if (this.isNew) {
|
||||
this.form.task_uuid = this.genTaskUUID()
|
||||
this.csvForm.req_info.uuid = this.form.task_uuid
|
||||
this.form.label = dayjs().format('YYYY-MM-DD')
|
||||
} else {
|
||||
// 打开已有文件时,使用从文件加载的task_uuid和label(isNew watch会恢复)
|
||||
// 使用$nextTick确保isNew watch先执行,恢复task_uuid
|
||||
this.$nextTick(() => {
|
||||
this.csvForm.req_info.uuid = this.form.task_uuid
|
||||
})
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.addShow = false
|
||||
this.$emit('close')
|
||||
},
|
||||
// 高级设置
|
||||
openSetDialog() {
|
||||
this.initSet()
|
||||
this.setShow = true
|
||||
},
|
||||
setCancel() {
|
||||
this.setShow = false
|
||||
},
|
||||
@@ -1043,7 +1140,44 @@ export default {
|
||||
initSet() {
|
||||
this.setForm = { ...this.$store.state.user.setForm }
|
||||
console.log(this.$store.state.user.setForm)
|
||||
}
|
||||
},
|
||||
// 退出新手引导
|
||||
exitGuide() {
|
||||
for (let key in this.guideStep) {
|
||||
this.guideStep[key].show = false
|
||||
}
|
||||
this.currentStep = -1
|
||||
localStorage.setItem('currentStep', this.currentStep)
|
||||
localStorage.setItem('guideStep', JSON.stringify(this.guideStep))
|
||||
this.close()
|
||||
},
|
||||
// 上一步
|
||||
prevStep(e) {
|
||||
this.guideStep[this.currentStep].show = false
|
||||
this.currentStep = parseInt(this.currentStep) - 1
|
||||
if (this.guideStep[this.currentStep]) {
|
||||
this.guideStep[this.currentStep].show = true
|
||||
localStorage.setItem('guideStep', JSON.stringify(this.guideStep))
|
||||
localStorage.setItem('currentStep', this.currentStep)
|
||||
if (this.currentStep == 7) {
|
||||
this.close()
|
||||
}
|
||||
} else {
|
||||
this.exitGuide()
|
||||
}
|
||||
},
|
||||
// 下一步
|
||||
nextStep(e) {
|
||||
this.guideStep[this.currentStep].show = false
|
||||
this.currentStep = parseInt(this.currentStep) + 1
|
||||
if (this.guideStep[this.currentStep]) {
|
||||
this.guideStep[this.currentStep].show = true
|
||||
localStorage.setItem('guideStep', JSON.stringify(this.guideStep))
|
||||
localStorage.setItem('currentStep', this.currentStep)
|
||||
} else {
|
||||
this.exitGuide()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1078,15 +1212,17 @@ export default {
|
||||
.close_box {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
right: 0;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
cursor: pointer;
|
||||
padding: 24px 24px 0 0;
|
||||
|
||||
img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
color: #000;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.work_box {
|
||||
@@ -1142,11 +1278,13 @@ export default {
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 195, 37, 0.32);
|
||||
border: 1px solid rgba(0, 195, 37, 0.32);
|
||||
color: #00c325;
|
||||
color: #009688;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
@@ -1203,10 +1341,11 @@ export default {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-right: 22px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 195, 37, 0.32);
|
||||
border: 1px solid rgba(0, 195, 37, 0.32);
|
||||
color: #00c325;
|
||||
color: #009688;
|
||||
}
|
||||
|
||||
img {
|
||||
@@ -1332,6 +1471,12 @@ export default {
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.work_right_top_add_disabled {
|
||||
cursor: not-allowed;
|
||||
background: #f5f5f5;
|
||||
border-color: #ddd;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.wook_soon {
|
||||
@@ -1356,15 +1501,12 @@ export default {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
z-index: 9;
|
||||
background: #ffffff;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 25px;
|
||||
height: 16px;
|
||||
}
|
||||
color: #000;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.wook_soon_btn1 {
|
||||
@@ -1512,6 +1654,7 @@ export default {
|
||||
border: 1px solid #999;
|
||||
padding: 0;
|
||||
margin-right: 22px;
|
||||
|
||||
&:hover {
|
||||
background: #00bcc3;
|
||||
color: #ffffff;
|
||||
@@ -1521,7 +1664,7 @@ export default {
|
||||
.footer_submit {
|
||||
width: 166px;
|
||||
height: 45px;
|
||||
background: #00c325;
|
||||
background: #009688;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 19px;
|
||||
@@ -1621,7 +1764,7 @@ export default {
|
||||
.set_btn2 {
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
background: #00c325;
|
||||
background: #009688;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
|
||||
@@ -9,7 +9,8 @@ import 'element-ui/lib/theme-chalk/index.css'
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import { accAdd, accSub, accMul, accDiv, filterSize, runCmd } from './utils'
|
||||
import { accAdd, accSub, accMul, accDiv, filterSize } from './utils'
|
||||
import { runCmd } from '@/platform'
|
||||
|
||||
import i18n from './lang' // internationalization
|
||||
import './permission' // permission control
|
||||
@@ -18,7 +19,7 @@ if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
|
||||
Vue.http = Vue.prototype.$http = axios
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// 设置公共方法
|
||||
// 设置公共方法(runCmd 来自 platform,网页端为“仅桌面端支持”的 Promise.reject)
|
||||
Vue.prototype.$accAdd = accAdd
|
||||
Vue.prototype.$accSub = accSub
|
||||
Vue.prototype.$accMul = accMul
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 桌面端平台实现:依赖 Node/Electron,仅在此文件中 require,且仅被 Electron 构建加载
|
||||
*/
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { exec } = require('child_process')
|
||||
const { ipcRenderer } = require('electron')
|
||||
const { app, dialog } = require('@electron/remote')
|
||||
|
||||
export function getAppRoot() {
|
||||
return !app.isPackaged ? process.cwd() : path.dirname(process.execPath)
|
||||
}
|
||||
|
||||
export function getPlatform() {
|
||||
const p = process.platform
|
||||
if (p === 'win32') return 'windows'
|
||||
if (p === 'darwin') return 'mac'
|
||||
if (p === 'linux') return 'linux'
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
export function getBaseSize() {
|
||||
const os = getPlatform()
|
||||
const baseSizes = { windows: 1024, mac: 1024, linux: 1024 }
|
||||
return baseSizes[os] || 1024
|
||||
}
|
||||
|
||||
export function openHelp() {
|
||||
ipcRenderer.send('open-help-file')
|
||||
}
|
||||
|
||||
export function runCmd(cmd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(cmd, (err, stdout, stderr) => {
|
||||
if (err) reject(err.message || err)
|
||||
else if (stderr) reject(stderr)
|
||||
else resolve(stdout)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function showContextMenu() {
|
||||
ipcRenderer.send('show-context-menu')
|
||||
}
|
||||
|
||||
export function showOpenFileDialog(options = {}) {
|
||||
return dialog.showOpenDialog(options).then(res => {
|
||||
if (res.canceled || !res.filePaths || !res.filePaths[0]) {
|
||||
return Promise.reject(new Error('取消选择'))
|
||||
}
|
||||
return { filePaths: res.filePaths, path: res.filePaths[0] }
|
||||
})
|
||||
}
|
||||
|
||||
export function showOpenDirectoryDialog(options = {}) {
|
||||
const opts = { ...options, properties: ['openDirectory'].concat(options.properties || []) }
|
||||
return dialog.showOpenDialog(opts).then(res => {
|
||||
if (res.canceled || !res.filePaths || !res.filePaths[0]) {
|
||||
return Promise.reject(new Error('取消选择'))
|
||||
}
|
||||
return { filePaths: res.filePaths, path: res.filePaths[0] }
|
||||
})
|
||||
}
|
||||
|
||||
export function showSaveFileDialog(options = {}) {
|
||||
return dialog.showSaveDialog(options).then(result => {
|
||||
if (result.canceled || result.filePath === '') {
|
||||
return Promise.reject(new Error('取消保存'))
|
||||
}
|
||||
return { filePath: result.filePath }
|
||||
})
|
||||
}
|
||||
|
||||
export function readFile(filePath, encoding) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(filePath, encoding || 'utf8', (err, data) => {
|
||||
if (err) reject(err)
|
||||
else resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function readFileSync(filePath, encoding) {
|
||||
return fs.readFileSync(filePath, encoding || 'utf8')
|
||||
}
|
||||
|
||||
export function writeFile(filePath, content) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(filePath, content, err => {
|
||||
if (err) reject(err)
|
||||
else resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function writeFileSync(filePath, content) {
|
||||
return fs.writeFileSync(filePath, content)
|
||||
}
|
||||
|
||||
export function readdir(dirPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readdir(dirPath, (err, files) => {
|
||||
if (err) reject(err)
|
||||
else resolve(files)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function stat(filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.stat(filePath, (err, stats) => {
|
||||
if (err) reject(err)
|
||||
else resolve(stats)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function statSync(filePath) {
|
||||
return fs.statSync(filePath)
|
||||
}
|
||||
|
||||
export function hasNativeFs() {
|
||||
return true
|
||||
}
|
||||
|
||||
export function hasRunCmd() {
|
||||
return true
|
||||
}
|
||||
|
||||
/** 桌面端不需要触发下载,保存走 dialog + writeFile */
|
||||
export function downloadFile(/* content, filename */) {
|
||||
// no-op on desktop
|
||||
}
|
||||
|
||||
/** path 工具:仅桌面端可用 */
|
||||
export const pathJoin = path.join
|
||||
export const pathDirname = path.dirname
|
||||
export const pathBasename = path.basename
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 平台抽象层入口:按构建环境导出桌面端或网页端实现,避免网页包中引入 Node/Electron
|
||||
*/
|
||||
const IS_WEB = process.env.IS_WEB === 'true'
|
||||
|
||||
const platform = IS_WEB ? require('./web.js') : require('./desktop.js')
|
||||
|
||||
export default platform
|
||||
|
||||
export const getAppRoot = platform.getAppRoot
|
||||
export const getPlatform = platform.getPlatform
|
||||
export const getBaseSize = platform.getBaseSize
|
||||
export const openHelp = platform.openHelp
|
||||
export const runCmd = platform.runCmd
|
||||
export const showContextMenu = platform.showContextMenu
|
||||
export const showOpenFileDialog = platform.showOpenFileDialog
|
||||
export const showOpenDirectoryDialog = platform.showOpenDirectoryDialog
|
||||
export const showSaveFileDialog = platform.showSaveFileDialog
|
||||
export const downloadFile = platform.downloadFile
|
||||
export const readFile = platform.readFile
|
||||
export const readFileSync = platform.readFileSync
|
||||
export const writeFile = platform.writeFile
|
||||
export const writeFileSync = platform.writeFileSync
|
||||
export const readdir = platform.readdir
|
||||
export const stat = platform.stat
|
||||
export const statSync = platform.statSync
|
||||
export const hasNativeFs = platform.hasNativeFs
|
||||
export const hasRunCmd = platform.hasRunCmd
|
||||
export const pathJoin = platform.pathJoin || (() => '')
|
||||
export const pathDirname = platform.pathDirname || (() => '')
|
||||
export const pathBasename = platform.pathBasename || (p => p)
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* 网页端平台实现:不依赖 Node/Electron,使用浏览器 API 或占位
|
||||
*/
|
||||
|
||||
// 可选:网页端帮助文档 URL,可由构建或运行时配置覆盖
|
||||
const HELP_PDF_URL = typeof process !== 'undefined' && process.env.HELP_PDF_URL
|
||||
? process.env.HELP_PDF_URL
|
||||
: '/help/User Manual.pdf'
|
||||
|
||||
export function getAppRoot() {
|
||||
return ''
|
||||
}
|
||||
|
||||
export function getPlatform() {
|
||||
return 'web'
|
||||
}
|
||||
|
||||
export function getBaseSize() {
|
||||
return 1024
|
||||
}
|
||||
|
||||
export function openHelp() {
|
||||
try {
|
||||
window.open(HELP_PDF_URL, '_blank')
|
||||
} catch (e) {
|
||||
console.warn('openHelp:', e)
|
||||
}
|
||||
}
|
||||
|
||||
export function runCmd(/* cmd */) {
|
||||
return Promise.reject(new Error('仅桌面端支持'))
|
||||
}
|
||||
|
||||
export function showContextMenu() {
|
||||
// no-op in web
|
||||
}
|
||||
|
||||
/**
|
||||
* 网页端:通过 input[type=file] 选文件,返回 { filePaths: [name], content }(无真实路径)
|
||||
* @param {Object} options - { title?, filters: [{ name, extensions }] }
|
||||
* @returns {Promise<{ filePaths: string[], content?: string }>}
|
||||
*/
|
||||
export function showOpenFileDialog(options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.style.display = 'none'
|
||||
const exts = (options.filters && options.filters[0] && options.filters[0].extensions)
|
||||
? options.filters[0].extensions
|
||||
: []
|
||||
if (exts.length) {
|
||||
input.accept = exts.map(e => '.' + e).join(',')
|
||||
}
|
||||
input.onchange = () => {
|
||||
const file = input.files && input.files[0]
|
||||
document.body.removeChild(input)
|
||||
if (!file) {
|
||||
reject(new Error('未选择文件'))
|
||||
return
|
||||
}
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
resolve({
|
||||
filePaths: [file.name],
|
||||
path: file.name,
|
||||
content: reader.result,
|
||||
file
|
||||
})
|
||||
}
|
||||
reader.onerror = () => reject(reader.error)
|
||||
reader.readAsText(file, 'utf-8')
|
||||
}
|
||||
input.oncancel = () => {
|
||||
document.body.removeChild(input)
|
||||
reject(new Error('取消选择'))
|
||||
}
|
||||
document.body.appendChild(input)
|
||||
input.click()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 网页端:无系统保存对话框,通过 downloadFile 触发浏览器下载
|
||||
* @returns {Promise<{ filePath: string }>} - 仅返回默认文件名,实际保存用 downloadFile
|
||||
*/
|
||||
export function showSaveFileDialog() {
|
||||
return Promise.resolve({ filePath: 'work.dwk' })
|
||||
}
|
||||
|
||||
/** 网页端:选择目录仅桌面端支持 */
|
||||
export function showOpenDirectoryDialog() {
|
||||
return Promise.reject(new Error('仅桌面端支持选择目录'))
|
||||
}
|
||||
|
||||
/**
|
||||
* 网页端:触发浏览器下载
|
||||
* @param {string} content - 文件内容
|
||||
* @param {string} filename - 建议文件名
|
||||
*/
|
||||
export function downloadFile(content, filename) {
|
||||
const blob = new Blob([content], { type: 'application/octet-stream' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = filename || 'download'
|
||||
a.style.display = 'none'
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
export function readFile(path, encoding) {
|
||||
return Promise.reject(new Error('仅桌面端支持本地路径读取'))
|
||||
}
|
||||
|
||||
export function readFileSync(path, encoding) {
|
||||
throw new Error('仅桌面端支持')
|
||||
}
|
||||
|
||||
export function writeFile(path, content) {
|
||||
return Promise.reject(new Error('仅桌面端支持'))
|
||||
}
|
||||
|
||||
export function writeFileSync(path, content) {
|
||||
throw new Error('仅桌面端支持')
|
||||
}
|
||||
|
||||
export function readdir(path) {
|
||||
return Promise.reject(new Error('仅桌面端支持'))
|
||||
}
|
||||
|
||||
export function stat(path) {
|
||||
return Promise.reject(new Error('仅桌面端支持'))
|
||||
}
|
||||
|
||||
export function statSync(path) {
|
||||
throw new Error('仅桌面端支持')
|
||||
}
|
||||
|
||||
/** 是否支持本地文件系统(路径读写、readdir 等) */
|
||||
export function hasNativeFs() {
|
||||
return false
|
||||
}
|
||||
|
||||
/** 是否支持 runCmd / 脚本执行 */
|
||||
export function hasRunCmd() {
|
||||
return false
|
||||
}
|
||||
|
||||
/** 简单路径拼接(仅用于显示或相对路径,无真实文件系统) */
|
||||
export function pathJoin(...parts) {
|
||||
return parts.filter(Boolean).join('/')
|
||||
}
|
||||
export function pathDirname(p) {
|
||||
const i = Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\'))
|
||||
return i < 0 ? '' : p.slice(0, i)
|
||||
}
|
||||
export function pathBasename(p) {
|
||||
const i = Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\'))
|
||||
return i < 0 ? p : p.slice(i + 1)
|
||||
}
|
||||
|
||||
export default {
|
||||
getAppRoot,
|
||||
getPlatform,
|
||||
getBaseSize,
|
||||
openHelp,
|
||||
runCmd,
|
||||
showContextMenu,
|
||||
showOpenFileDialog,
|
||||
showOpenDirectoryDialog,
|
||||
showSaveFileDialog,
|
||||
downloadFile,
|
||||
readFile,
|
||||
readFileSync,
|
||||
writeFile,
|
||||
writeFileSync,
|
||||
readdir,
|
||||
stat,
|
||||
statSync,
|
||||
hasNativeFs,
|
||||
hasRunCmd,
|
||||
pathJoin,
|
||||
pathDirname,
|
||||
pathBasename
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import getters from './getters'
|
||||
|
||||
import { createPersistedState, createSharedMutations } from 'vuex-electron'
|
||||
|
||||
import modules from './modules'
|
||||
|
||||
// 仅桌面端引入 vuex-electron,避免网页构建报错
|
||||
if (process.env.IS_WEB !== 'true') {
|
||||
require('vuex-electron')
|
||||
}
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules,
|
||||
getters,
|
||||
plugins: [
|
||||
// createPersistedState(),
|
||||
// createSharedMutations()
|
||||
],
|
||||
plugins: [],
|
||||
strict: process.env.NODE_ENV !== 'production'
|
||||
})
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
let timeouter = null
|
||||
let websock = null
|
||||
|
||||
// WebSocket 地址:优先使用用户配置(localStorage),便于网页端部署后连接远程或本机服务
|
||||
const WS_SOCKET_API_KEY = 'WS_SOCKET_API'
|
||||
function getSocketUrl() {
|
||||
try {
|
||||
const saved = localStorage.getItem(WS_SOCKET_API_KEY)
|
||||
if (saved && saved.trim()) return saved.trim()
|
||||
} catch (e) {}
|
||||
const envUrl = typeof process !== 'undefined' && process.env && process.env.VUE_APP_SOCKET_API
|
||||
return envUrl || 'ws://127.0.0.1:10010'
|
||||
}
|
||||
|
||||
function asArray(val) {
|
||||
return Array.isArray(val) ? val : []
|
||||
}
|
||||
|
||||
// 状态JSON
|
||||
// 设备状态
|
||||
const PrinterStatus = {
|
||||
@@ -213,6 +228,7 @@ const defaultData = {
|
||||
lack_ink: 0, // 缺墨警告
|
||||
cover_flag: false, //是否盖盖
|
||||
printer_name: '', //设备名称
|
||||
printer_path: '', //设备路径
|
||||
printer_status: 'I', //设备状态 数字转成字符为I
|
||||
printer_tray: 'I', //设备托盘状态 数字转成字符为I
|
||||
red_last: 0, //红色墨盒遗留
|
||||
@@ -221,6 +237,8 @@ const defaultData = {
|
||||
system_name: '', //主机名称
|
||||
is_set_cddev: 0,
|
||||
serial_no: '', //设备序列号
|
||||
free_cache_space: 51587919872, //缓存空间
|
||||
total_cache_space: 105152176128, //总缓存空间
|
||||
test_use: '', //试用天数
|
||||
// 左右光盘桶
|
||||
strong_list: [
|
||||
@@ -345,7 +363,8 @@ const actions = {
|
||||
return
|
||||
}
|
||||
commit('setData', { name: 'connecting', data: true })
|
||||
websock = new WebSocket(process.env.VUE_APP_SOCKET_API)
|
||||
const socketUrl = getSocketUrl()
|
||||
websock = new WebSocket(socketUrl)
|
||||
websock.onmessage = function (res) {
|
||||
dispatch('websocketonmessage', res)
|
||||
}
|
||||
@@ -361,20 +380,28 @@ const actions = {
|
||||
},
|
||||
// 接收消息
|
||||
websocketonmessage({ state, commit, dispatch }, e) {
|
||||
const data = JSON.parse(e.data) // 转json对象
|
||||
if (data.resp_name) {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(e.data)
|
||||
} catch (err) {
|
||||
console.warn('websocket parse error:', err)
|
||||
return
|
||||
}
|
||||
if (!data.resp_name) return
|
||||
try {
|
||||
const resp = data.resp_info || {}
|
||||
// 作业列表
|
||||
if (data.resp_name === 'task_list') {
|
||||
commit('setData', { name: 'task_list', data: data.resp_info.task_list })
|
||||
commit('setData', { name: 'task_list', data: asArray(resp.task_list) })
|
||||
// 设备信息
|
||||
} else if (data.resp_name === 'printer_info') {
|
||||
let info = data.resp_info
|
||||
info.printer_status = String.fromCharCode(info.printer_status)
|
||||
info.printer_tray = String.fromCharCode(info.printer_tray)
|
||||
let strong_list = {}
|
||||
for (let i = 0; i < info.strong_list.length; i++) {
|
||||
strong_list[info.strong_list[i].strong_pos] = info.strong_list[i]
|
||||
}
|
||||
const info = { ...resp }
|
||||
if (info.printer_status != null) info.printer_status = String.fromCharCode(info.printer_status)
|
||||
if (info.printer_tray != null) info.printer_tray = String.fromCharCode(info.printer_tray)
|
||||
const strong_list = {}
|
||||
asArray(info.strong_list).forEach((item) => {
|
||||
strong_list[item.strong_pos] = item
|
||||
})
|
||||
info.strongList = strong_list
|
||||
// if (info.cover_flag && info.printer_name !== 'SE3') {
|
||||
// commit('setData', { name: 'printer_error', data: { type: 'error', notify: '设备门盖被打开,请关闭门盖。' } })
|
||||
@@ -385,7 +412,7 @@ const actions = {
|
||||
commit('setData', { name: 'printer_info', data: info })
|
||||
// 驱动信息
|
||||
} else if (data.resp_name === 'cd_info') {
|
||||
const list = data.resp_info.cd_list
|
||||
const list = asArray(resp.cd_list)
|
||||
let cds = []
|
||||
let isBD = false
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
@@ -414,13 +441,13 @@ const actions = {
|
||||
commit('setData', { name: 'cd_list', data: cds })
|
||||
// 手动设置驱动
|
||||
} else if (data.resp_name === 'get_all_cdlist') {
|
||||
commit('setData', { name: 'cdList', data: data.resp_info.cd_list || [] })
|
||||
commit('setData', { name: 'cdList', data: asArray(resp.cd_list) })
|
||||
// 系统配置
|
||||
} else if (data.resp_name === 'get_system_config') {
|
||||
commit('setData', { name: 'settings', data: data.resp_info })
|
||||
commit('setData', { name: 'settings', data: resp })
|
||||
// 新增日志
|
||||
} else if (data.resp_name === 'add_log') {
|
||||
let logs = data.resp_info.add_log
|
||||
const logs = asArray(resp.add_log)
|
||||
let loglist = []
|
||||
let logLists = [...state.logList]
|
||||
for (let i = 0; i < logs.length; i++) {
|
||||
@@ -436,7 +463,7 @@ const actions = {
|
||||
commit('setData', { name: 'logList', data: list })
|
||||
// 全部日志
|
||||
} else if (data.resp_name === 'all_log') {
|
||||
let logs = data.resp_info.add_log
|
||||
const logs = asArray(resp.add_log)
|
||||
let loglist = []
|
||||
for (let i = 0; i < logs.length; i++) {
|
||||
let texts = logs[i].split('|')
|
||||
@@ -449,14 +476,15 @@ const actions = {
|
||||
commit('setData', { name: 'logList', data: loglist })
|
||||
// 提交任务报错处理
|
||||
} else if (data.resp_name === 'submit_task_res') {
|
||||
let error = data.resp_info
|
||||
let task_res = '0x' + error.task_res.toString(16)
|
||||
commit('setData', { name: 'printer_error', data: { type: 'error', notify: '任务' + error.task_uuid + '提交失败:' + submitErros[task_res] } })
|
||||
if (resp.task_res != null) {
|
||||
const task_res = '0x' + resp.task_res.toString(16)
|
||||
commit('setData', { name: 'printer_error', data: { type: 'error', notify: '任务' + resp.task_uuid + '提交失败:' + submitErros[task_res] } })
|
||||
}
|
||||
// 打印报错处理
|
||||
} else if (data.resp_name === 'printer_error') {
|
||||
let error = data.resp_info
|
||||
if (error.error) {
|
||||
let errors = error.error
|
||||
const error = resp
|
||||
const errors = asArray(error.error)
|
||||
if (errors.length) {
|
||||
let continues = [8, 9, 10, 11, 12, 17, 19]
|
||||
let nojumps = [7, 8, 9, 10, 11, 12, 17, 19]
|
||||
let isClears = [20, 21]
|
||||
@@ -525,15 +553,14 @@ const actions = {
|
||||
}, 1000);
|
||||
// 执行错误提示
|
||||
} else {
|
||||
if (data.resp_info.res_code === 0) {
|
||||
if (resp.res_code === 0) {
|
||||
|
||||
} else {
|
||||
commit('setData', { name: 'printer_error', data: { type: 'error', notify: data.resp_info.res_msg || '操作失败' } })
|
||||
commit('setData', { name: 'printer_error', data: { type: 'error', notify: resp.res_msg || '操作失败' } })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 未知消息
|
||||
console.log('报错信息: ' + JSON.stringify(data))
|
||||
} catch (err) {
|
||||
console.warn('websocket handle error:', data.resp_name, err)
|
||||
}
|
||||
},
|
||||
// 心跳检测重连
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
* 处理不同操作系统的文件大小差异
|
||||
*/
|
||||
|
||||
// 检测操作系统
|
||||
// 检测操作系统(网页端无 process.platform,返回 'web')
|
||||
export function getOS() {
|
||||
if (typeof process === 'undefined' || process.platform === undefined) return 'web'
|
||||
const platform = process.platform
|
||||
if (platform === 'win32') return 'windows'
|
||||
if (platform === 'darwin') return 'mac'
|
||||
@@ -19,11 +20,12 @@ export function getBaseSize() {
|
||||
// 不同操作系统的基础单位
|
||||
const baseSizes = {
|
||||
windows: 1024, // Windows 使用 1024 进制 (二进制)
|
||||
mac: 1000, // macOS 使用 1000 进制 (十进制)
|
||||
linux: 1000 // Linux 使用 1000 进制 (十进制,遵循 SI 标准)
|
||||
mac: 1024, // macOS 使用 1024 进制 (十进制)
|
||||
linux: 1024, // Linux 使用 1024 进制 (十进制,遵循 SI 标准)
|
||||
web: 1024 // 网页端固定 1024
|
||||
}
|
||||
|
||||
return baseSizes[os] || 1000
|
||||
return baseSizes[os] || 1024
|
||||
}
|
||||
|
||||
// 计算光盘类型大小
|
||||
@@ -36,10 +38,10 @@ export function calculateDiscSize(sizeInGB, os = null) {
|
||||
export function getBaseSizeForOS(os) {
|
||||
const baseSizes = {
|
||||
windows: 1024, // Windows 使用 1024 进制 (二进制)
|
||||
mac: 1000, // macOS 使用 1000 进制 (十进制)
|
||||
linux: 1000 // Linux 使用 1000 进制 (十进制,遵循 SI 标准)
|
||||
mac: 1024, // macOS 使用 1024 进制 (十进制)
|
||||
linux: 1024 // Linux 使用 1024 进制 (十进制,遵循 SI 标准)
|
||||
}
|
||||
return baseSizes[os] || 1000
|
||||
return baseSizes[os] || 1024
|
||||
}
|
||||
|
||||
// 光盘类型配置
|
||||
@@ -50,37 +52,37 @@ export function getDiscTypes(os = null) {
|
||||
{
|
||||
value: 1,
|
||||
label: 'CD 700MB',
|
||||
size: 700 * Math.pow(baseSize, 2)
|
||||
size: 700 * Math.pow(1000, 2)
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'DVD 4.7GB',
|
||||
size: 4.7 * Math.pow(baseSize, 3)
|
||||
size: 4.7 * Math.pow(1000, 3)
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'DVD_DL 8.5GB',
|
||||
size: 8.5 * Math.pow(baseSize, 3)
|
||||
size: 8.5 * Math.pow(1000, 3)
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: 'BD 25GB',
|
||||
size: 25 * Math.pow(baseSize, 3)
|
||||
size: 25 * Math.pow(1000, 3)
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: 'BD_DL 50GB',
|
||||
size: 50 * Math.pow(baseSize, 3)
|
||||
size: 50 * Math.pow(1000, 3)
|
||||
},
|
||||
{
|
||||
value: 6,
|
||||
label: 'BD_TL 100GB',
|
||||
size: 100 * Math.pow(baseSize, 3)
|
||||
size: 100 * Math.pow(1000, 3)
|
||||
},
|
||||
{
|
||||
value: 7,
|
||||
label: 'BD_QL 128GB',
|
||||
size: 128 * Math.pow(baseSize, 3)
|
||||
size: 128 * Math.pow(1000, 3)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -477,18 +477,4 @@ export const pow1024 = (num) => {
|
||||
return Math.pow(baseSize, num)
|
||||
}
|
||||
|
||||
// 执行脚本
|
||||
const { exec } = require('child_process');
|
||||
export const runCmd = (cmd) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(cmd, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
if (stderr) {
|
||||
reject(stderr)
|
||||
}
|
||||
resolve(stdout)
|
||||
})
|
||||
})
|
||||
}
|
||||
// runCmd 已迁移至 @/platform,由 main.js 挂载到 Vue.prototype.$runCmd
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,21 +2,23 @@
|
||||
<div class="login-container">
|
||||
<img class="login_bg" src="@/assets/images/login_bg.jpg" />
|
||||
<div class="flex_box form_box">
|
||||
<el-form class="flex_box flex_col flex_row_center login-form" autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left">
|
||||
<el-form class="flex_box flex_col flex_row_center login-form" autoComplete="on" :model="loginForm"
|
||||
:rules="loginRules" ref="loginForm" label-position="left">
|
||||
<img class="login_logo" src="@/assets/images/logo.png" />
|
||||
<div class="login_box">
|
||||
<div class="login_title">用户登录</div>
|
||||
<div class="login_label">用户名</div>
|
||||
<el-form-item prop="username">
|
||||
<el-input name="username" type="text" v-model="loginForm.username" autoComplete="on" placeholder="username" />
|
||||
<el-input name="username" type="text" v-model="loginForm.username" autoComplete="on"
|
||||
placeholder="username" />
|
||||
</el-form-item>
|
||||
<div class="login_label">密码</div>
|
||||
<el-form-item prop="password">
|
||||
<el-input name="password" :type="pwdType" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="on"
|
||||
placeholder="password"></el-input>
|
||||
<span class="show-pwd" @click="showPwd">
|
||||
|
||||
</span>
|
||||
<el-input name="password" :type="pwdType" @keyup.enter.native="handleLogin" v-model="loginForm.password"
|
||||
autoComplete="on" placeholder="password"></el-input>
|
||||
<span class="show-pwd" @click="showPwd">
|
||||
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="login_check">
|
||||
@@ -24,11 +26,40 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="login_btn" type="primary" :loading="loading" @click.native.prevent="handleLogin">{{ $t('login.logIn') }}</el-button>
|
||||
<el-button class="login_btn" type="primary" :loading="loading" @click.native.prevent="handleLogin">{{
|
||||
$t('login.logIn') }}</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="login_ws_setting">
|
||||
<el-button type="text" @click="showSocketSetting = !showSocketSetting">
|
||||
{{ showSocketSetting ? '收起' : '设置 WebSocket 服务地址' }}
|
||||
</el-button>
|
||||
<div v-if="showSocketSetting" class="login_ws_input">
|
||||
<el-input v-model="socketApi" placeholder="例如 ws://127.0.0.1:10010 或 ws://服务器IP:10010"
|
||||
@blur="saveSocketApi" size="small" />
|
||||
<span class="login_ws_tip">网页端部署后请填写实际服务地址,保存后刷新或重新登录生效</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-popover v-if="guideStep" :placement="guideStep[0].placement" width="250" trigger="manual"
|
||||
v-model="guideStep[0].show">
|
||||
<div class="guide_box">
|
||||
<div class="guide_title">新手引导<span>({{ parseInt(currentStep) + 1 }}/{{ guideStep.length }})</span>
|
||||
</div>
|
||||
<div class="guide_desc">{{ guideStep[0].step }}</div>
|
||||
<div class="guide_btns">
|
||||
<el-button @click="exitGuide" class="guide_btn1" size="mini" type="text">跳过引导</el-button>
|
||||
<el-button v-if="parseInt(currentStep) > 0" @click="prevStep" class="guide_btn1" size="mini">上一步</el-button>
|
||||
<el-button @click="nextStep" class="guide_btn2" size="mini" type="primary">{{ currentStep ==
|
||||
guideStep.length
|
||||
- 1 ? '完成引导' : '下一步' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -64,10 +95,25 @@ export default {
|
||||
},
|
||||
isChecked: true,
|
||||
loading: false,
|
||||
pwdType: 'password'
|
||||
pwdType: 'password',
|
||||
showSocketSetting: false,
|
||||
socketApi: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
try {
|
||||
this.socketApi = localStorage.getItem('WS_SOCKET_API') || 'ws://127.0.0.1:10010'
|
||||
} catch (e) {
|
||||
this.socketApi = 'ws://127.0.0.1:10010'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveSocketApi() {
|
||||
try {
|
||||
const v = (this.socketApi || '').trim()
|
||||
if (v) localStorage.setItem('WS_SOCKET_API', v)
|
||||
} catch (e) {}
|
||||
},
|
||||
showPwd() {
|
||||
if (this.pwdType === 'password') {
|
||||
this.pwdType = ''
|
||||
@@ -81,10 +127,10 @@ export default {
|
||||
this.loading = true
|
||||
setToken('data.token')
|
||||
if (localStorage.getItem('setForm')) {
|
||||
this.$store.dispatch('setDatas', {name: 'setForm', data: localStorage.getItem('setForm')})
|
||||
this.$store.dispatch('setDatas', { name: 'setForm', data: localStorage.getItem('setForm') })
|
||||
}
|
||||
this.$store.dispatch('setDatas', {name: 'token', data: 'data.token'})
|
||||
this.$store.dispatch('setDatas', {name: 'oneLogin', data: true})
|
||||
this.$store.dispatch('setDatas', { name: 'token', data: 'data.token' })
|
||||
this.$store.dispatch('setDatas', { name: 'oneLogin', data: true })
|
||||
setTimeout(() => {
|
||||
this.loading = false
|
||||
this.$router.push({ path: '/' })
|
||||
@@ -106,8 +152,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
$bg:#F1F1F1;
|
||||
$light_gray:#000000;
|
||||
$bg: #F1F1F1;
|
||||
$light_gray: #000000;
|
||||
|
||||
/* reset element-ui css */
|
||||
.login-container {
|
||||
@@ -119,6 +165,7 @@ $light_gray:#000000;
|
||||
background: #F1F1F1;
|
||||
border-radius: 30px;
|
||||
color: #000;
|
||||
|
||||
input {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
@@ -128,6 +175,7 @@ $light_gray:#000000;
|
||||
height: 60px;
|
||||
font-size: 24px;
|
||||
border-radius: 30px;
|
||||
|
||||
&:-webkit-autofill {
|
||||
border-radius: 30px;
|
||||
-webkit-box-shadow: 0 0 0px 1000px $bg inset !important;
|
||||
@@ -136,23 +184,25 @@ $light_gray:#000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
$bg:#F1F1F1;
|
||||
$dark_gray:#889aa4;
|
||||
$light_gray:#eee;
|
||||
$bg: #F1F1F1;
|
||||
$dark_gray: #889aa4;
|
||||
$light_gray: #eee;
|
||||
|
||||
.login-container {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.login_bg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.form_box {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@@ -162,60 +212,72 @@ $light_gray:#eee;
|
||||
height: 100%;
|
||||
padding: 0 0 0 120px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
height: 100%;
|
||||
|
||||
.login_logo {
|
||||
width: 844px;
|
||||
height: 158px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.login_box {
|
||||
width: 450px;
|
||||
background-color: #fff;
|
||||
border-radius: 24px;
|
||||
padding: 48px;
|
||||
|
||||
.login_title {
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
color: #00C325;
|
||||
color: #009688;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.login_label {
|
||||
font-size: 24px;
|
||||
color: #B2B2B2;
|
||||
line-height: 36px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.login_check {
|
||||
::v-deep {
|
||||
.el-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-checkbox__inner {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background-color: #00C325;
|
||||
border-color: #00C325;
|
||||
background-color: #009688;
|
||||
border-color: #009688;
|
||||
}
|
||||
|
||||
.el-checkbox__inner::after {
|
||||
width: 6px;
|
||||
height: 14px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.el-checkbox__label {
|
||||
font-size: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-checked+.el-checkbox__label {
|
||||
color: #00C325;
|
||||
color: #009688;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login_btn {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
@@ -225,28 +287,47 @@ $light_gray:#eee;
|
||||
font-size: 24px;
|
||||
color: #00BCC3;
|
||||
}
|
||||
|
||||
.login_ws_setting {
|
||||
width: 100%;
|
||||
.login_ws_input {
|
||||
margin-top: 8px;
|
||||
.el-input { width: 100%; }
|
||||
}
|
||||
.login_ws_tip {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
margin-bottom: 10px;
|
||||
|
||||
span {
|
||||
&:first-of-type {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.svg-container {
|
||||
padding: 6px 5px 6px 15px;
|
||||
color: $dark_gray;
|
||||
vertical-align: middle;
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
|
||||
&_login {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
@@ -255,6 +336,7 @@ $light_gray:#eee;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.show-pwd {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
<div class="form_label">设置光驱</div>
|
||||
<div class="form_val">
|
||||
<div class="flex_box flex_wrap form_tabs">
|
||||
<div @click="handleAction(item)" v-for="(item, index) in actionList" :key="index" class="form_tab">{{ item.name }}</div>
|
||||
<div @click="handleAction(item)" v-for="(item, index) in actionList" :key="index" class="form_tab">{{
|
||||
item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -24,7 +25,8 @@
|
||||
<div class="form_label">校准设备</div>
|
||||
<div class="form_val">
|
||||
<div class="flex_box flex_wrap form_tabs">
|
||||
<div @click="handleMove(item)" v-for="(item, index) in moveList" :key="index" class="form_tab">{{ item.name }}</div>
|
||||
<div @click="handleMove(item)" v-for="(item, index) in moveList" :key="index" class="form_tab">{{
|
||||
item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,7 +34,8 @@
|
||||
<div class="form_label">维护设备</div>
|
||||
<div class="form_val">
|
||||
<div class="flex_box flex_wrap form_tabs">
|
||||
<div @click="handleService(item)" v-for="(item, index) in serviceList" :key="index" class="form_tab">{{ item.name }}</div>
|
||||
<div @click="handleService(item)" v-for="(item, index) in serviceList" :key="index" class="form_tab">{{
|
||||
item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,6 +75,12 @@
|
||||
<el-checkbox v-model="isDel">服务重启时是否删除已完成和已取消任务?</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box form_option">
|
||||
<div class="form_label">新手引导</div>
|
||||
<div class="form_val">
|
||||
<el-checkbox v-model="hideStepEnabled" @change="handleGuideChange">是否开启新手引导功能</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="form_item">
|
||||
@@ -111,7 +120,8 @@
|
||||
<div class="cd_name">{{ item.dev_name }}({{ item.dev_path }})</div>
|
||||
<div class="flex_box cd_r">
|
||||
<el-select v-model="item.cdType" @change="cdChange(index, item.cdType)">
|
||||
<el-option v-for="type in cdOptions" :key="type.value" :label="type.label" :value="type.value"> </el-option>
|
||||
<el-option v-for="type in cdOptions" :key="type.value" :label="type.label" :value="type.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button @click="cdOpen(index)" type="success">打开</el-button>
|
||||
<el-button @click="cdClose(index)" type="danger">关闭</el-button>
|
||||
@@ -124,6 +134,32 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 设置光盘桶类型 -->
|
||||
<el-dialog center title="设置光盘桶类型" :visible.sync="setCDShow" width="600px">
|
||||
<div class="cd_box">
|
||||
<div class="cd_title">请选择左右光盘桶所对应的光盘类型。</div>
|
||||
<div class="cd_desc">修改后将影响作业可选择的光盘类型,请谨慎操作。</div>
|
||||
<div class="cd_list">
|
||||
<div class="flex_box flex_row_between cd_item" v-for="(item, index) in changeCD.req_info.strong_list" :key="index">
|
||||
<div class="cd_name">光盘桶 {{ item.strong_pos }}</div>
|
||||
<div class="flex_box cd_r">
|
||||
<el-select v-model="item.strong_type" @change="(val) => handleSetCDChange(index, val)">
|
||||
<el-option
|
||||
v-for="(label, key) in CDTypes"
|
||||
:key="key"
|
||||
:label="label"
|
||||
:value="parseInt(key)">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex_box flex_row_center cd_footer">
|
||||
<el-button @click="handleSetCDCancel" class="cd_footer_btn1">取消</el-button>
|
||||
<el-button @click="handleSetCDSubmit" class="cd_footer_btn2" type="primary">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -170,6 +206,10 @@ export default {
|
||||
// }
|
||||
],
|
||||
serviceList: [
|
||||
{
|
||||
id: 7,
|
||||
name: '设置光盘桶'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '刷新光盘桶'
|
||||
@@ -198,6 +238,7 @@ export default {
|
||||
refreshTime: 10,
|
||||
sleepTime: 300000,
|
||||
isDel: true,
|
||||
hideStepEnabled: false, // 新手引导开关状态
|
||||
langVal: 'zhCN',
|
||||
langList: [
|
||||
{
|
||||
@@ -228,7 +269,18 @@ export default {
|
||||
value: 3,
|
||||
label: '下光驱'
|
||||
}
|
||||
]
|
||||
],
|
||||
// 设置光盘桶
|
||||
setCDShow: false,
|
||||
CDTypes: {},
|
||||
changeCD: {
|
||||
req_name: 'setCDStrong',
|
||||
req_type: 2,
|
||||
req_info: {
|
||||
strong_list: []
|
||||
}
|
||||
},
|
||||
default_cd: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -260,7 +312,7 @@ export default {
|
||||
handler(val) {
|
||||
let data = { ...val }
|
||||
this.printer_info = { ...data }
|
||||
if (val.printer_name && val.printer_name === 'SE3') {
|
||||
if (val.printer_name && val.printer_name.indexOf('SE3') > -1) {
|
||||
this.moveList = [
|
||||
{
|
||||
id: 4,
|
||||
@@ -341,6 +393,10 @@ export default {
|
||||
}
|
||||
]
|
||||
this.serviceList = [
|
||||
{
|
||||
id: 7,
|
||||
name: '设置光盘桶'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '刷新光盘桶'
|
||||
@@ -370,6 +426,13 @@ export default {
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
},
|
||||
'$store.state.chat.CDTypes': {
|
||||
handler(val) {
|
||||
this.CDTypes = { ...val }
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -377,6 +440,9 @@ export default {
|
||||
if (type && type === 'cddev') {
|
||||
this.handleAction(this.actionList[0])
|
||||
}
|
||||
// 初始化新手引导开关状态
|
||||
const hideStep = localStorage.getItem('hideStep')
|
||||
this.hideStepEnabled = !hideStep || hideStep == 0
|
||||
},
|
||||
methods: {
|
||||
// 校准设备
|
||||
@@ -526,7 +592,7 @@ export default {
|
||||
case 5:
|
||||
// 校准墨盒上的打印头
|
||||
that
|
||||
.$confirm('请在进盘仓放入一张空白的可打印光盘,再点击确定', '温馨提示', {
|
||||
.$confirm('请点击确定对齐打印机墨盒', '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
@@ -689,7 +755,7 @@ export default {
|
||||
case 5:
|
||||
// 进入循环模式
|
||||
that
|
||||
.$confirm('请确保已经取掉左右光盘桶托架,点击确定后设备将进入循环模式', '温馨提示', {
|
||||
.$confirm('点击确定后设备将进入循环模式', '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
@@ -732,6 +798,32 @@ export default {
|
||||
})
|
||||
})
|
||||
break
|
||||
case 7:
|
||||
// 设置光盘桶(手动选择左右光盘桶类型)
|
||||
// 正在任务中禁止操作(与 dashboard handleRun 中的限制保持一致)
|
||||
const cdList = that.$store.state.chat.cd_list || []
|
||||
if ((cdList[0] && cdList[0].copy_scheduler) || (cdList[1] && cdList[1].copy_scheduler)) {
|
||||
that.$message({
|
||||
message: '正在任务中,无法进行操作',
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!that.printer_info || !that.printer_info.strong_list || !that.printer_info.strong_list.length) {
|
||||
that.$message({
|
||||
message: '当前设备暂无光盘桶信息,无法设置',
|
||||
type: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
// 初始化默认光盘桶类型
|
||||
that.changeCD.req_info.strong_list = that.printer_info.strong_list.map((item, index) => ({
|
||||
strong_pos: index + 1,
|
||||
strong_type: item.cd_type
|
||||
}))
|
||||
that.default_cd = JSON.parse(JSON.stringify(that.changeCD.req_info.strong_list))
|
||||
that.setCDShow = true
|
||||
break
|
||||
}
|
||||
},
|
||||
cdChange(index, e) {
|
||||
@@ -787,7 +879,7 @@ export default {
|
||||
device_pos: 2
|
||||
}
|
||||
]
|
||||
if (this.printer_info.printer_name !== 'SE3') {
|
||||
if (this.printer_info.printer_name.indexOf('SE3') === -1) {
|
||||
let top = this.cdList.filter((item) => item.cdType === 2)
|
||||
let bottom = this.cdList.filter((item) => item.cdType === 3)
|
||||
if (!top || top.length === 0) {
|
||||
@@ -863,6 +955,147 @@ export default {
|
||||
message: '设置成功',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
// 新手引导开关变化
|
||||
handleGuideChange(value) {
|
||||
if (value) {
|
||||
// 开启新手引导:设置 hideStep 为 0
|
||||
localStorage.setItem('hideStep', 0)
|
||||
// 初始化 guideStep(如果不存在)
|
||||
const guideStepStr = localStorage.getItem('guideStep')
|
||||
if (!guideStepStr) {
|
||||
this.initGuideStep()
|
||||
}
|
||||
this.$message({
|
||||
message: '新手引导已开启',
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
// 关闭新手引导:设置 hideStep 为 1
|
||||
localStorage.setItem('hideStep', 1)
|
||||
this.$message({
|
||||
message: '新手引导已关闭',
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
},
|
||||
// 初始化新手引导步骤
|
||||
initGuideStep() {
|
||||
const guideStep = [
|
||||
{
|
||||
show: false,
|
||||
placement: 'right',
|
||||
step: '系统状态面板,显示系统名称,耗材信息等,通过下拉菜单可以执行设置光盘桶等常见操作。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'left',
|
||||
step: '光驱状态面板,显示当前光驱工作状态,刻录进度和速度等相关信息。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'left',
|
||||
step: '日志面板,显示当前日志动态。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'top',
|
||||
step: '作业列表面板,显示当前作业信息。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'bottom',
|
||||
step: '服务启动和停止切换按钮。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'bottom',
|
||||
step: '任务统计面板。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'bottom',
|
||||
step: '系统设置按钮,详情请见软件使用手册。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'bottom',
|
||||
step: '新建作业和打开作业按钮。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'bottom',
|
||||
step: '选择作业的光盘类型和任务类型。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'right',
|
||||
step: '作业内容面板,选择文件夹和文件作为光盘刻录内容,修改光盘卷标名称。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'left',
|
||||
step: '作业标签面板,选择或创建一个光盘标签,也可以通过下拉菜单选择默认标签,默认标签路径为软件目录下User Templates文件夹中。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'bottom',
|
||||
step: '高级作业设置按钮,配置刻录速度,校验,跨盘,虚拟刻录等详细的作业设置。',
|
||||
},
|
||||
{
|
||||
show: false,
|
||||
placement: 'top',
|
||||
step: '点击保存作业或提交按钮,还可以点击链接查看更多软件使用手册。',
|
||||
},
|
||||
]
|
||||
localStorage.setItem('guideStep', JSON.stringify(guideStep))
|
||||
localStorage.setItem('currentStep', 0)
|
||||
},
|
||||
// 设置光盘桶 - 选择类型
|
||||
handleSetCDChange(index, val) {
|
||||
if (!this.changeCD.req_info.strong_list[index]) return
|
||||
this.changeCD.req_info.strong_list[index].strong_type = val
|
||||
this.$set(this.changeCD.req_info.strong_list, index, {
|
||||
...this.changeCD.req_info.strong_list[index]
|
||||
})
|
||||
},
|
||||
// 设置光盘桶 - 取消
|
||||
handleSetCDCancel() {
|
||||
this.setCDShow = false
|
||||
this.changeCD.req_info.strong_list = JSON.parse(JSON.stringify(this.default_cd || []))
|
||||
},
|
||||
// 设置光盘桶 - 确认
|
||||
handleSetCDSubmit() {
|
||||
let that = this
|
||||
if (!that.changeCD.req_info.strong_list || !that.changeCD.req_info.strong_list.length) {
|
||||
that.$message({
|
||||
message: '请先设置光盘桶类型',
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
that.$store
|
||||
.dispatch('chat/websocketsend', that.changeCD)
|
||||
.then(() => {
|
||||
// 同 dashboard 的行为:根据是否包含 BD 类型更新可选光盘类型
|
||||
try {
|
||||
const { getCurrentOSDiscTypes } = require('../../utils/fileSize')
|
||||
let typeLists = getCurrentOSDiscTypes()
|
||||
let isBD = that.changeCD.req_info.strong_list.findIndex(item => parseInt(item.strong_type) > 3)
|
||||
if (isBD === -1) {
|
||||
typeLists = typeLists.slice(0, 3)
|
||||
}
|
||||
that.$store.dispatch('chat/setDatas', { name: 'cd_types', data: typeLists })
|
||||
} catch (err) {
|
||||
// 安全兜底:不影响主流程
|
||||
console.error(err)
|
||||
}
|
||||
that.$message({
|
||||
message: '已发送指令',
|
||||
type: 'success'
|
||||
})
|
||||
that.setCDShow = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -872,16 +1105,20 @@ export default {
|
||||
.container_box {
|
||||
.form_box {
|
||||
padding: 0 32px;
|
||||
|
||||
.form_item {
|
||||
padding: 10px 0;
|
||||
|
||||
.form_title {
|
||||
border-bottom: 2px solid #e1f5e5;
|
||||
|
||||
.form_title_text {
|
||||
font-weight: 500;
|
||||
font-size: 25px;
|
||||
color: #000000;
|
||||
line-height: 37px;
|
||||
}
|
||||
|
||||
.form_title_line {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
@@ -889,19 +1126,24 @@ export default {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.form_content {
|
||||
padding: 10px 0;
|
||||
|
||||
.form_option {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.form_label {
|
||||
width: 140px;
|
||||
font-weight: 500;
|
||||
font-size: 19px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.form_val {
|
||||
max-width: 1100px;
|
||||
|
||||
.form_tabs {
|
||||
.form_tab {
|
||||
width: 250px;
|
||||
@@ -915,13 +1157,15 @@ export default {
|
||||
font-weight: 500;
|
||||
font-size: 19px;
|
||||
margin: 6px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 195, 37, 0.12);
|
||||
border: 1px solid rgba(0, 195, 37, 0.32);
|
||||
color: #00c325;
|
||||
color: #009688;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form_input {
|
||||
width: 500px;
|
||||
height: 42px;
|
||||
@@ -929,6 +1173,7 @@ export default {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #999;
|
||||
overflow: hidden;
|
||||
|
||||
::v-deep {
|
||||
.el-input-group__append {
|
||||
font-size: 16px;
|
||||
@@ -936,6 +1181,7 @@ export default {
|
||||
font-weight: 500;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
width: 360px;
|
||||
height: 40px;
|
||||
@@ -945,6 +1191,7 @@ export default {
|
||||
font-size: 19px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.el-input__icon {
|
||||
font-size: 19px;
|
||||
height: 40px;
|
||||
@@ -952,6 +1199,7 @@ export default {
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
width: 140px;
|
||||
height: 40px;
|
||||
@@ -962,8 +1210,9 @@ export default {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
|
||||
&:hover {
|
||||
background: #00c325;
|
||||
background: #009688;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
@@ -976,36 +1225,44 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-checkbox__inner {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background-color: #00c325;
|
||||
border-color: #00c325;
|
||||
background-color: #009688;
|
||||
border-color: #009688;
|
||||
}
|
||||
|
||||
.el-checkbox__inner::after {
|
||||
width: 6px;
|
||||
height: 14px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.el-checkbox__label {
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.el-checkbox__input.is-checked + .el-checkbox__label {
|
||||
|
||||
.el-checkbox__input.is-checked+.el-checkbox__label {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.form_select {
|
||||
width: 500px;
|
||||
height: 42px;
|
||||
background: #ffffff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #98e8a6;
|
||||
|
||||
.el-select {
|
||||
width: 100%;
|
||||
padding-left: 10px;
|
||||
|
||||
::v-deep {
|
||||
.el-input__inner {
|
||||
border: none;
|
||||
@@ -1016,6 +1273,7 @@ export default {
|
||||
line-height: 42px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.el-input__icon {
|
||||
font-size: 20px;
|
||||
height: 42px;
|
||||
@@ -1031,11 +1289,13 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cd_box {
|
||||
.el-input {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.el-input__inner {
|
||||
width: 240px;
|
||||
@@ -1045,6 +1305,7 @@ export default {
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-input__icon {
|
||||
font-size: 16px;
|
||||
height: 40px;
|
||||
@@ -1053,23 +1314,28 @@ export default {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.cd_title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.cd_desc {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.cd_list {
|
||||
padding: 32px 0;
|
||||
|
||||
.cd_item {
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -1077,17 +1343,21 @@ export default {
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.cd_name {
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.cd_r {
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.cd_footer {
|
||||
gap: 24px;
|
||||
|
||||
.cd_footer_btn1 {
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
@@ -1100,63 +1370,11 @@ export default {
|
||||
padding: 0;
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.cd_footer_btn2 {
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
background: #00c325;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .el-dialog__title {
|
||||
font-size: 24px;
|
||||
}
|
||||
</style>
|
||||
t: 24px;
|
||||
}
|
||||
.cd_list {
|
||||
padding: 32px 0;
|
||||
.cd_item {
|
||||
padding: 12px 0;
|
||||
}
|
||||
.el-button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 80px;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.cd_name {
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
line-height: 24px;
|
||||
}
|
||||
.cd_r {
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
.cd_footer {
|
||||
gap: 24px;
|
||||
.cd_footer_btn1 {
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
background: #ffffff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #000000;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #000000;
|
||||
padding: 0;
|
||||
margin: 0 12px;
|
||||
}
|
||||
.cd_footer_btn2 {
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
background: #00c325;
|
||||
background: #009688;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
@@ -1165,6 +1383,7 @@ t: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-dialog__title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,7 @@
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_select">
|
||||
<el-select v-model="form.log_level" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in levelList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
<el-option v-for="item in levelList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
@@ -33,11 +29,7 @@
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_select">
|
||||
<el-select v-model="form.retry_freq" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in timesList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
<el-option v-for="item in timesList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
@@ -49,11 +41,7 @@
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_select">
|
||||
<el-select v-model="form.print_quality" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in qualityList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
<el-option v-for="item in qualityList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
@@ -83,11 +71,7 @@
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_select">
|
||||
<el-select v-model="form.has_sr0" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in sr0CheckList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
<el-option v-for="item in sr0CheckList" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
@@ -98,7 +82,7 @@
|
||||
<div class="form_label">RejectConfig</div>
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_switch">
|
||||
<el-switch active-color="#07C160" inactive-color="#aaa" v-model="form.RejectConfig"></el-switch>
|
||||
<el-switch active-color="#009688" inactive-color="#aaa" v-model="form.RejectConfig"></el-switch>
|
||||
</div>
|
||||
<div class="form_text">出卡盒配置。默认勾选为正常出卡,取消勾选则从Reject出卡盒出卡</div>
|
||||
</div>
|
||||
@@ -107,7 +91,7 @@
|
||||
<div class="form_label">StopOnFailure</div>
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_switch">
|
||||
<el-switch active-color="#07C160" inactive-color="#aaa" v-model="form.StopOnFailure"></el-switch>
|
||||
<el-switch active-color="#009688" inactive-color="#aaa" v-model="form.StopOnFailure"></el-switch>
|
||||
</div>
|
||||
<div class="form_text">作业失败后停止接受任务,默认不勾选</div>
|
||||
</div>
|
||||
@@ -116,7 +100,7 @@
|
||||
<div class="form_label">KeepCombinedImage</div>
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_switch">
|
||||
<el-switch active-color="#07C160" inactive-color="#aaa" v-model="form.KeepCombinedImage"></el-switch>
|
||||
<el-switch active-color="#009688" inactive-color="#aaa" v-model="form.KeepCombinedImage"></el-switch>
|
||||
</div>
|
||||
<div class="form_text">是否保留作业临时图片和字段数据</div>
|
||||
</div>
|
||||
@@ -125,7 +109,7 @@
|
||||
<div class="form_label">CleanTaskFile</div>
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_switch">
|
||||
<el-switch active-color="#07C160" inactive-color="#aaa" v-model="form.CleanTaskFile"></el-switch>
|
||||
<el-switch active-color="#009688" inactive-color="#aaa" v-model="form.CleanTaskFile"></el-switch>
|
||||
</div>
|
||||
<div class="form_text">是否清除作业临时文件</div>
|
||||
</div>
|
||||
@@ -134,7 +118,7 @@
|
||||
<div class="form_label">UploadSharedDir</div>
|
||||
<div class="flex_box form_val">
|
||||
<div class="form_switch">
|
||||
<el-switch active-color="#07C160" inactive-color="#aaa" v-model="form.UploadSharedDir"></el-switch>
|
||||
<el-switch active-color="#009688" inactive-color="#aaa" v-model="form.UploadSharedDir"></el-switch>
|
||||
</div>
|
||||
<div class="form_text">启用作业缓存路径,勾选后作业内容会先刻录到缓存路径后再刻录到存储卡</div>
|
||||
</div>
|
||||
@@ -241,7 +225,7 @@ export default {
|
||||
watch: {
|
||||
'$store.state.chat.settings': {
|
||||
handler(val) {
|
||||
this.form = {...val}
|
||||
this.form = { ...val }
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
@@ -255,9 +239,9 @@ export default {
|
||||
setSubmit() {
|
||||
if (this.form.cache_path === '') {
|
||||
this.$message({
|
||||
message: '请输入作业缓存路径',
|
||||
type: 'warning'
|
||||
})
|
||||
message: '请输入作业缓存路径',
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$store
|
||||
@@ -281,16 +265,20 @@ export default {
|
||||
.container_box {
|
||||
.form_box {
|
||||
padding: 0 32px;
|
||||
|
||||
.form_item {
|
||||
padding: 10px 0;
|
||||
|
||||
.form_title {
|
||||
border-bottom: 2px solid #E1F5E5;
|
||||
|
||||
.form_title_text {
|
||||
font-weight: 500;
|
||||
font-size: 25px;
|
||||
color: #000000;
|
||||
line-height: 37px;
|
||||
}
|
||||
|
||||
.form_title_line {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
@@ -298,17 +286,21 @@ export default {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.form_content {
|
||||
padding: 10px 0;
|
||||
|
||||
.form_option {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.form_label {
|
||||
width: 300px;
|
||||
font-weight: 500;
|
||||
font-size: 19px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.form_val {
|
||||
.form_input {
|
||||
width: 300px;
|
||||
@@ -317,6 +309,7 @@ export default {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #98E8A6;
|
||||
overflow: hidden;
|
||||
|
||||
::v-deep {
|
||||
.el-input__inner {
|
||||
width: 360px;
|
||||
@@ -327,6 +320,7 @@ export default {
|
||||
font-size: 19px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.el-input__icon {
|
||||
font-size: 20px;
|
||||
height: 40px;
|
||||
@@ -334,19 +328,21 @@ export default {
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
width: 140px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: #00C325;
|
||||
background: #009688;
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.form_switch {
|
||||
width: 300px;
|
||||
|
||||
.el-switch {
|
||||
zoom: 1.4;
|
||||
}
|
||||
@@ -358,9 +354,11 @@ export default {
|
||||
background: #FFFFFF;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #98E8A6;
|
||||
|
||||
.el-select {
|
||||
width: 100%;
|
||||
padding-left: 10px;
|
||||
|
||||
::v-deep {
|
||||
.el-input__inner {
|
||||
border: none;
|
||||
@@ -371,6 +369,7 @@ export default {
|
||||
line-height: 42px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.el-input__icon {
|
||||
font-size: 20px;
|
||||
height: 42px;
|
||||
@@ -381,6 +380,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form_text {
|
||||
font-weight: 500;
|
||||
font-size: 19px;
|
||||
@@ -390,8 +390,10 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer_btns {
|
||||
height: 80px;
|
||||
|
||||
.footer_submit {
|
||||
width: 166px;
|
||||
height: 45px;
|
||||
@@ -402,8 +404,9 @@ export default {
|
||||
font-size: 19px;
|
||||
color: #333;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
background: #00C325;
|
||||
background: #009688;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
<el-table-column prop="logintime" label="最后登录时间"></el-table-column>
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template slot-scope="scope">
|
||||
<div class="table_status" :class="{'table_status1': scope.row.status === 1}">{{ scope.row.status === 1 ? '在线' : '离线' }}</div>
|
||||
<div class="table_status" :class="{ 'table_status1': scope.row.status === 1 }">{{ scope.row.status === 1 ?
|
||||
'在线' : '离线' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -49,7 +50,7 @@ export default {
|
||||
...mapGetters(['name', 'roles'])
|
||||
},
|
||||
methods: {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -57,16 +58,20 @@ export default {
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.container_box {
|
||||
height: 100vh;
|
||||
|
||||
.table_box {
|
||||
padding: 32px;
|
||||
|
||||
.el-table {
|
||||
::v-deep {
|
||||
th.el-table__cell {
|
||||
background-color: #F2F9F4;
|
||||
}
|
||||
|
||||
.el-table__cell {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.cell {
|
||||
line-height: 28px;
|
||||
font-size: 12px;
|
||||
@@ -74,12 +79,14 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table_status {
|
||||
font-weight: 500;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.table_status1 {
|
||||
color: #00C325;
|
||||
color: #009688;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user