Commit b28530b1 by 朱哲铨

项目初始化

parents
> 1%
last 2 versions
module.exports = {
root: true,
env: {
node: true
},
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"],
parserOptions: {
parser: "babel-eslint"
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
}
};
.DS_Store
node_modules
/dist
/dist_electron
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
#Electron-builder output
/dist_electron
\ No newline at end of file
### 前言:
本文讲述 Vue 3.0 + Electron + Express + Lowdb 框架搭建过程, 以及少量示例代码;
重点讲解框架部分如何衔接和配合形成一个整体:
顺序:
1. 先保证让Vue的Web形式能正常运行
2. 通过Electron给Vue套一层App外壳, 变为App程序
3. 部署Express充当Vue+Electron的Web Restful Api后端, 并通过Lowdb记录一些App的系统设置信息到文件中, 以便于下一次启动时仍能访问(不同于Browser端保存)。
4. 让Vue+Electron+Express三者共同协作, 看起来是某一个云上服务的App形式的Client端。
### 准备部分:
- 安装node.js
```shell
官网下载安装
https://nodejs.org/zh-cn/
安装完毕查看版本号:
node -v
```
- 设置npm代理
- (推荐) 使用淘宝镜像:
```shell
npm config set proxy http://"username:pass"@server:port
npm config set http-proxy http://"username:pass"@server:port
npm config set https-proxy http://"username:pass"@server:port
npm config set strict-ssl false
npm config set registry=https://registry.npm.taobao.org
```
- 安装 vue-cli3:
``` shell
# 卸载1.x或2.x旧版本
npm uninstall vue-cli-g
# 安装@vue/cli ( 即 vue-cli3 )
npm install -g @vue/cli
# 查看vue-cli版本
vue -V
```
### 前端部分(Vue3.0)
- 创建项目
```powershell
vue create vue-node-db
# 创建过程中 勾选特性: babel, Router, Vuex, CSS Pre-processors
# 使用 "history router"
# CSS 预处理器使用 SCSS/SASS
```
- 测试项目
```shell
npm run serve
```
访问 localhost:8080 出现web界面, 即为OK
- 增加 ElementUI支持
```shell
npm install -S element-ui
```
- 增加 axios 支持, 用于前端发起ajax请求
```shell
npm install -S axios
```
- 增加normalize.css 支持, 用户前端页面整体的规整化
```shell
npm install -S normalize.css
```
- 增加 pug/jade 的HTML模板语法支持:
```shell
npm install -D pug pug-html-loader pug-plain-loader
```
- 增加 sass/scss 的CSS模板语法支持支持:
```shell
npm install -D node-sass sass-loader
```
- 生成 src/styles/main.scss 和 src/styles/config.css, 作为预装载全局css定义
main.scss: 内容暂时为空,仅仅引入 config.scss, 后续根据业务需要添加
```scss
// 全局CSS常量定义
@import "./config.scss";
// 所有修改element-ui的样式, 以避免单页面scoped中修改权限不够的问题
//@import "./elementui.scss"
```
config.scss: 内容暂时为空, 后续根据需要业务添加
```scss
// 定义全局CSS常亮, 便于在各CSS中统一共享
```
- 增加 Vue 3.0 @vue/composition-api 支持
```shell
npm install -S @vue/composition-api
```
- 修改 src/main.js, 其中的 行首 + 表示为新增行, - 表示删除行, M 表示修改行( 下同, 不赘述)
```javascript
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
+ import "normalize.css";
+ import ElementUI from 'element-ui';
+ import 'element-ui/lib/theme-chalk/index.css';
+ import VueCompositionApi from "@vue/composition-api";
+ Vue.use(ElementUI);
+ Vue.use(VueCompositionApi);
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
```
- 运行查看结果:
```shell
npm run serve
```
访问浏览器地址展示默认Vue Web 界面
- 手工生成vue.config.js
```javascript
const path = require("path");
module.exports = {
// 基本路径
publicPath: process.env.NODE_ENV === "production" ? "" : "/",
// 输出文件目录
outputDir: process.env.NODE_ENV === "production" ? "dist" : "devdist",
// eslint-loader 是否在保存的时候检查
lintOnSave: false,
/** vue3.0内置了webpack所有东西,
* webpack配置,see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
**/
chainWebpack: config => {
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]",
include: ["./src/icons" ]
});
config.module
.rule("pug")
.test(/\.pug$/)
.use("pug-html-loader")
.loader("pug-html-loader")
.end();
},
configureWebpack: config => {
config.resolve = {
// 配置解析别名
extensions: [".js", ".json", ".vue"], // 自动添加文件名后缀
alias: {
vue: "vue/dist/vue.js",
"@": path.resolve(__dirname, "./src"),
"@c": path.resolve(__dirname, "./src/components")
}
};
},
// 生产环境是否生成 sourceMap 文件
productionSourceMap: false,
// css相关配置
css: {
// 是否使用css分离插件 ExtractTextPlugin
extract: true,
// 开启 CSS source maps?
sourceMap: false,
// css预设器配置项
loaderOptions: {
sass: {
prependData: `@import "./src/styles/main.scss";`
}
},
// 启用 CSS modules for all css / pre-processor files.
requireModuleExtension: true, // 是否开启支持‘foo.module.css’样式
},
// use thread-loader for babel & TS in production build
// enabled by default if the machine has more than 1 cores
parallel: require("os").cpus().length > 1,
/**
* PWA 插件相关配置,see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
*/
pwa: {},
// webpack-dev-server 相关配置
devServer: {
open: false, // 编译完成是否打开网页
host: "0.0.0.0", // 指定使用地址,默认localhost,0.0.0.0代表可以被外界访问
port: 8090, // 访问端口
https: false, // 编译失败时刷新页面
hot: true, // 开启热加载
hotOnly: false,
proxy: {
// 配置跨域
"/devApi": {
//要访问的跨域的api的域名
target: "http://www.web-jshtml.cn",
ws: true,
changOrigin: true,
pathRewrite: {
"^/devApi": "/productapi"
}
}
},
overlay: {
// 全屏模式下是否显示脚本错误
warnings: true,
errors: true
},
// before: app => {}
},
/**
* 第三方插件配置
*/
pluginOptions: {}
};
```
- 改造其中的vue为pug格式
以App.vue为例, 改写后如下:
```jade
<template lang="pug">
#app
.nav
router-link( to="/") Home
| &nbsp; | &nbsp;
router-line( to="/about" ) About
router-view
</template>
```
- 添加vue对i18n的支持
- 执行命令行安装包
```shell
npm install -S vue-i18n
```
- 准备语言文件
- 建立 src/i18n/en.js 和 src/i18n/zh.js
- zh.js文件内容如下,en.js和zh.js一一对应, 但value不同
```javascript
export const lang = {
slice: {
placeholder: {
name: "请输出切片名称",
nst: "请选择NST模板"
},
tips: {
sla: "带宽:{0} Mbps, 时延: {1} ms"
}
}
}
```
- 把i18n加入到Vue, 修改 src/main.js:
```javascript
import VueI18n from 'vue-i18n';
...
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: 'zh',
messages: {
'zh': require('./i18n/zh.js'),
'en': require('./i18n/en.js')
}
});
...
new Vue({
router,
store,
i18n,
render: h => h(App)
}).$mount("#app");
```
- 在Vue文件中, 在template渲染部分可以使用 {{ \$('lang.slice.tips.sla') }} 来引用; 如果是在script部分使用, 则应使用 root.​\$t() 函数来访问;
- 使用 root.$i18n.locale 来获知当前语言
- 添加路由和路由动画
- 添加Vuex支持
### 前端部分(Electron)
- 安装 electron 支持
可参考: https://www.jianshu.com/p/d2ab300f8a9a
```shell
npm config set registry http://registry.npm.taobao.org/
npm install -g electron
electron -v
```
注: 如果一直卡在 node install.js, 可以设置%USERPROFILE%/.npmrc文件, 加入一行:
```ini
ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
```
- 新增 vue-cli方面的plug支持, 即vue-cli-plugin-electron-builder 支持, 主要作用是修改package.json, 并新增一个electron主程序入口文件 src/background.js
```shell
vue add electron-builder
```
选择Electron Version时, 选择 ^6.0.0;
修改后的package.json变化如下:
```json
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
+ "electron:build": "vue-cli-service electron:build",
+ "electron:serve": "vue-cli-service electron:serve",
+ "postinstall": "electron-builder install-app-deps",
+ "postuninstall": "electron-builder install-app-deps"
},
+ "main": "background.js",
...
"devDependencies":{
...
+ "vue-cli-plugin-electron-builder": "^1.4.6",
+ "electron": "^6.0.0",
}
```
- 新生成的src/background.js内容如下, 修改为如下代码, 相比自动生成代码, 多了如下功能:
修改了App窗口大小, 取消了跨域限制, 取消了菜单栏, 修改了App的窗口Icon:
```javascript
"use strict";
import { app, protocol, BrowserWindow, Menu } from "electron";
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
const isDevelopment = process.env.NODE_ENV !== "production";
let win;
protocol.registerSchemesAsPrivileged([
{ scheme: "app", privileges: { secure: true, standard: true } }
]);
function createWindow() {
win = new BrowserWindow({
width: 1200,
height: 800,
icon: "./src/assets/logo.png",
webPreferences: {
webSecurity: false,
nodeIntegration: true
}
});
// 取消菜单
Menu.setApplicationMenu( null );
if (process.env.WEBPACK_DEV_SERVER_URL) {
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
if (!process.env.IS_TEST) win.webContents.openDevTools();
} else {
createProtocol("app");
win.loadURL("app://./index.html");
}
win.on("closed", () => {
win = null;
});
}
app.on("activate", () => {
if (win === null) {
createWindow();
}
});
app.on("ready", async () => {
createWindow();
});
if (isDevelopment) {
if (process.platform === "win32") {
process.on("message", data => {
if (data === "graceful-exit") {
app.quit();
}
});
} else {
process.on("SIGTERM", () => {
app.quit();
});
}
}
```
其中, 修改App窗口大小, 取消跨域限制, 取消菜单栏, 修改App图标 的代码集中在createWindow()中:
```javascript
function createWindow () {
// Create the browser window.
win = new BrowserWindow({
M width: 1200,
M height: 800,
+ icon: "./src/assets/logo.png",
webPreferences: {
+ webSecurity: false,
nodeIntegration: true
}
})
```
注: 还可以参考 https://www.jianshu.com/p/9e066a57de1a, https://www.jianshu.com/p/f3e69b4f1827, 加入托盘,消息闪烁,全局快捷键等
- 测试electron 运行
```shell
# 运行
npm run electron:serve
# 打包
npm run electron:build
```
此时出现的界面和之前用web浏览器打开的一样, 只是被装在窗口中.
### 后端部分(Express)
- 后端目录准备:
建立src/backend目录, 作为整个后台(含Electron, lowdb, Express等, 不含Vue)的目录. 后台目录:
```shell
backend
log 目录: 存放日志文件
store 目录: 存放数据访问工具文件
db.js
webserver 目录: 存放
entity 目录: 存放数据实体文件
function.js Slice管理
be_sysConfig.js 系统设置
routes 目录: 存放 exporess 路由
be_routes.js 路由定义
be_nodeSrv.js express 服务器
```
- 增加 lowdb 支持
```shell
npm install -S lowdb
# 为字段增加一个唯一标识id
npm install -S lodash-id
```
- 增加 express 支持
```shell
npm install -S express
npm install -D nodemon
```
- src/backend/store/db.js: 提供数据对象访问能力
```javascript
import Datastore from "lowdb";
import FileSync from "lowdb/adapters/FileSync";
import path from "path";
import fs from "fs-extra";
import LodashId from "lodash-id";
// 引入remote模块
import { app, remote } from "electron";
// 根据process.type来分辨在哪种模式使用哪种模块,
// 在主进程调用 为 browser, 在渲染进程调用为 renderer
const APP = process.type === "renderer" ? remote.app : app;
// 获取用户目录 C:\Users\shihe\AppData\Roaming\vue-node-lowdb
const STORE_PATH = APP.getPath("userData");
if (process.type !== "renderer") {
// 如果不存在路径,创建
if (!fs.pathExistsSync(STORE_PATH)) {
fs.mkdirpSync(STORE_PATH);
}
}
const adapter = new FileSync(path.join(STORE_PATH, "database.json")); // 初始化lowdb读写的json文件名以及存储路径
const db = Datastore(adapter); // lowdb接管该文件
//通过lodash-id这个插件可以很方便地为每个新增的数据自动加上一个唯一标识的id字段
db._.mixin(LodashId);
// 初始化 ( 示例 )
if ( !db.read().has("NSTs").value()) {
db.set("NSTs", []).write();
db.get("NSTs").insert({ label: "差动保护", value: "nst_001"}).write();
db.get("NSTs").insert({ label: "龙门吊", value: "nst_002" }).write();
}
if (!db.read().has("PLMNs").value()) {
db.read().set("PLMNs", []).write();
db.read().get("PLMNs").insert({ label: "中国移动01", value: "960-001" }).write();
db.read().get("PLMNs").insert({ label: "中国联通03", value: "960-003" }).write();
db.read().get("PLMNs").insert({ label: "中国电信07", value: "960-007" }).write();
}
// ES6写法: 暴露
export { db as default };
```
其他lowdb的详细信息可以参考 LowDB.md文件, 以及网址: https://www.jianshu.com/p/d46abfa4ddc9
- Express Web Restful API 服务器:
注: 如果是全新项目, 则可以借助如下命令来快速生成应用的骨架, 但这个项目已经存在, 因此只能手工方式把express的 WebServer, routes, 路由响应函数 等添加到项目中:
```shell
npx express-generator
```
- Web Server 服务器
代码: src/backend/webserver/be_nodeSrv.js:
```javascript
import express from "express";
import router from "./routes/be_routes.js";
const PORT = 3000;
const webApp = express();
//webApp.use(logger("./logs"));
webApp.use(express.json());
webApp.use(express.urlencoded({ extended: false }));
webApp.use("/", router);
// catch 404
webApp.use((req, res, next) => {
res.status(404).send("Sorry! 404 Error.");
});
// error handler, 4个参数
webApp.use((err, req, res, next) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.json({
message: err.message,
error: err
});
});
webApp.set("port", PORT);
webApp.listen(PORT, () => console.log(`App listening on port ${PORT}`));
export { webApp as default };
```
- Express的API 路由:
代码: src/backend/webserver/routes/be_routes.js
```javascript
import express from "express";
// 导入slice mgt的具体数据操作函数
import sliceMgt from "../entity/function.js";
// 导入system config 的具体数据操作函数
// let sysConfig = require("../entity/be_sysConfig.js");
import sysConfig from "../entity/be_sysConfig.js";
// 生成路由对象
let router = express.Router();
// 设置路由
router.get("/nsmf/v1/nsts", sliceMgt.getNSTs);
router.get("/nsmf/v1/plmns", sliceMgt.getPLMNs);
router.get("/nsmf/v1/nsmfConfig", sysConfig.getNSMFConfig);
router.put("/nsmf/v1/nsmfConfig", sysConfig.setNSMFConfig);
// ES6写法: 暴露路由
export { router as default };
```
- sliceMgt实体文件:
代码: src/backend/webserver/entity/function.js
```javascript
import db from "@/backend/store/db.js";
let sliceMgt = new Object();
// wrap函数把不支持promise的库转为支持, 可以支持异步
// const wrap = fn => (...args) => fn(...args).catch(args[2]);
// router.get(
// "/",
// wrap(async (req, res) => {
// await console.log("Async call cost long time");
// console.log("return get function");
// res.send("Hello World!");
// })
// );
sliceMgt.getNSTs = (req, res) => {
console.log("GET: NST List");
// 查询数据
let data = db.get("NSTs").value();
res.json(data);
};
sliceMgt.getPLMNs = (req, res) => {
console.log("GET: PLMN List");
res.send("getPLMNs");
};
export { sliceMgt as default };
```
be_sysConfig.js类似处理
- 准备完毕后, 在 src/background.js 中加入Express Webserver的启动:
```javascript
"use strict";
import { app, protocol, BrowserWindow, Menu } from "electron";
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
+ import webApp from "@/backend/webserver/be_nodeSrv.js";
...
```
由于 webApp 本身在模块中就会执行, 因此import即可
- 启动 Vue + Electron + Express
```shell
npm run electron:serve
```
Vue+Electron部分应该保持和之前一样, 是一个带窗口的App, 同时console窗口会打印Express WebServer启动的信息:
```shell
App listening on port 3000
```
通过浏览器访问 Express 的API接口:
```shell
http://localhost:3000/nsmf/v1/nsts
```
浏览器会展示返回的json结构:
```json
[{"label":"差动保护","value":"nst_001","id":"007b1880-f259-4993-b786-a5d93310b306"},{"label":"龙门吊","value":"nst_002","id":"1ff1f498-b308-4649-a42e-77e7293e42b6"}]
```
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
};
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "vue-exectron-express-lowdb",
"productName": "Jomall Graphics Lab",
"description": "",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"electron:build": "vue-cli-service electron:build",
"electron:serve": "nodemon --watch src/backend --exec vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
},
"main": "background.js",
"dependencies": {
"@gausszhou/vue-drag-resize-rotate": "^2.0.15",
"@vue/composition-api": "^0.3.4",
"axios": "^0.19.2",
"core-js": "^3.6.4",
"electron-store": "^4.0.0",
"element-ui": "^2.13.0",
"express": "^4.17.1",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"morgan": "^1.10.0",
"multiparty": "^4.2.3",
"node-cmd": "^5.0.0",
"node-printer": "^1.0.4",
"nodemon": "^3.1.4",
"normalize.css": "^8.0.1",
"printer": "^0.4.0",
"shelljs": "^0.8.5",
"vue": "^2.6.11",
"vue-draggable-resizable": "^3.0.0",
"vue-i18n": "^8.16.0",
"vue-moveable": "^2.0.0-beta.75",
"vue-router": "^3.1.5",
"vuex": "^3.1.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.2.0",
"@vue/cli-plugin-eslint": "^4.2.0",
"@vue/cli-plugin-router": "^4.2.0",
"@vue/cli-plugin-vuex": "^4.2.0",
"@vue/cli-service": "^4.2.0",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.0.3",
"electron": "^6.0.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-vue": "^6.1.2",
"prettier": "^1.19.1",
"pug": "^2.0.4",
"pug-html-loader": "^1.1.5",
"pug-plain-loader": "^1.0.0",
"vue-cli-plugin-electron-builder": "^1.4.6",
"vue-template-compiler": "^2.6.11"
}
}
{
"name": "vue-node-lowdb",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
},
"main": "background_simple.js",
"dependencies": {
"@vue/composition-api": "^0.3.4",
"axios": "^0.19.2",
"core-js": "^3.6.4",
"element-ui": "^2.13.0",
"express": "^4.17.1",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"morgan": "^1.10.0",
"normalize.css": "^8.0.1",
"vue": "^2.6.11",
"vue-router": "^3.1.5",
"vuex": "^3.1.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.2.0",
"@vue/cli-plugin-eslint": "^4.2.0",
"@vue/cli-plugin-router": "^4.2.0",
"@vue/cli-plugin-vuex": "^4.2.0",
"@vue/cli-service": "^4.2.0",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.0.3",
"electron": "^6.0.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-vue": "^6.1.2",
"node-sass": "^4.13.1",
"prettier": "^1.19.1",
"pug": "^2.0.4",
"pug-html-loader": "^1.1.5",
"pug-plain-loader": "^1.0.0",
"sass-loader": "^8.0.2",
"vue-cli-plugin-electron-builder": "^1.4.6",
"vue-template-compiler": "^2.6.11"
}
}
<?xml version="1.0" encoding="utf-8"?>
<GTOPTION xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<szFileName />
<uiCopies>1</uiCopies>
<byPlatenSize>1</byPlatenSize>
<bMultiple>1</bMultiple>
<byResolution>1</byResolution>
<byInk>1</byInk>
<byInkVolume>1</byInkVolume>
<byDoublePrint>1</byDoublePrint>
<bySaturation>1</bySaturation>
<byBrightness>1</byBrightness>
<byContrast>1</byContrast>
<iCyanBalance>1</iCyanBalance>
<iMagentaBalance>1</iMagentaBalance>
<iYellowBalance>1</iYellowBalance>
<iBlackBalance>1</iBlackBalance>
<bUniPrint>1</bUniPrint>
</GTOPTION>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<GTOPTION xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<szFileName />
<uiCopies>1</uiCopies>
<byPlatenSize>1</byPlatenSize>
<byHighlight>1</byHighlight>
<byMask>1</byMask>
<bDivide>1</bDivide>
<bTransColor>1</bTransColor>
<bySaturation>1</bySaturation>
<byBrightness>1</byBrightness>
<byContrast>1</byContrast>
<bUniPrint>1</bUniPrint>
<byResolution>1</byResolution>
<byInk>1</byInk>
</GTOPTION>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<GTOPTION xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<szFileName />
<uiCopies>1</uiCopies>
<byPlatenSize>1</byPlatenSize>
<bEcoMode>1</bEcoMode>
<byInk>1</byInk>
<bMaterialBlack>1</bMaterialBlack>
<bMultiple>1</bMultiple>
<byHighlight>1</byHighlight>
<byMask>1</byMask>
<bFastMode>1</bFastMode>
<bDivide>1</bDivide>
<bPause>1</bPause>
<bTransColor>1</bTransColor>
<byChoke>1</byChoke>
<byMinWhite>1</byMinWhite>
<bySaturation>1</bySaturation>
<byBrightness>1</byBrightness>
<byContrast>1</byContrast>
<iCyanBalance>1</iCyanBalance>
<bUniPrint>1</bUniPrint>
<iMagentaBalance>1</iMagentaBalance>
<iYellowBalance>1</iYellowBalance>
<iBlackBalance>1</iBlackBalance>
<byResolution>1</byResolution>
</GTOPTION>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<GTOPTION xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<szFileName />
<uiCopies>1</uiCopies>
<byPlatenSize>1</byPlatenSize>
<byResolution>1</byResolution>
<byInk>1</byInk>
<bMultiple>1</bMultiple>
<byInkVolume>1</byInkVolume>
<byDoublePrint>1</byDoublePrint>
<bySaturation>1</bySaturation>
<byBrightness>1</byBrightness>
<byContrast>1</byContrast>
<iBlackBalance>1</iBlackBalance>
<bUniPrint>1</bUniPrint>
</GTOPTION>
\ No newline at end of file
GTXproCMD.exe print -X "Profile\CO12.xml" -I "Input\sample.png" -A "Output\pO12.arxp" -S 01000200 -L 02540254
echo %errorlevel%
\ No newline at end of file
GTXproCMD.exe send -A "D:\sample\Output\pO12.arxp" -P "Brother GTX pro" -D 1
echo %errorlevel%
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {}
}
</script>
<style>
body {
margin: 0;
height: 100vh;
}
#app {
height: 100vh;
display: flex;
overflow: hidden;
width: 100vw;
flex-direction: column;
}
</style>
import express from "express";
import router from "./routes/be_routes.js";
const PORT = 3000;
const webApp = express();
//webApp.use(logger("./logs"));
webApp.use(express.json());
webApp.use(express.urlencoded({ extended: false }));
webApp.use("/", router);
// catch 404
webApp.use((req, res, next) => {
res.status(404).send("Sorry! 404 Error.");
});
// error handler, 4个参数
webApp.use((err, req, res, next) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.json({
message: err.message,
error: err
});
});
webApp.set("port", PORT);
webApp.listen(PORT, () => console.log(`App listening on port ${PORT}`));
export { webApp as default };
import {downloadImage, toSend, writeProfileXml} from "@/backend/webserver/utils";
var uuid = require("uuid");
const path = require('path');
const fs = require('fs');
const os = require('os');
import axios from "axios";
var multiparty = require("multiparty");
let fn = new Object();
fn.getPodProductionInfo = async (req, res) => {
const token = req.headers["jwt-token"]
const company = req.headers["company"]
const productionNo = req.body.productionNo
try {
let {data} = await axios.post(`https://${company}/api/podDesignCenter/getPodImageByProduction`, {productionNo}, {headers: {'jwt-token': token}})
if (data.code === 200 && data.data && data.data.length > 0) {
downloadImage(data.data, company, data => {
res.json({code: 200, data})
}, (err) => {
res.json({code: 500, msg: '素材图下载失败'})
})
} else {
res.json({code: 200, msg: '未找到素材图', data: []})
}
} catch (err) {
console.log(err)
res.json({code: 500, msg: '接口调用失败'})
}
}
fn.findByPodProductionNo = async (req, res) => {
const token = req.headers["jwt-token"]
const company = req.headers["company"]
const q = req.body
try {
let {data} = await axios.get(`https://${company}/api/pod/podProductionInfo/findByPodProductionNo`, {
params: q,
headers: {'jwt-token': token}
})
res.json(data)
} catch (err) {
res.json({code: 500, msg: err})
}
}
fn.getCompanyList = async (req, res) => {
try {
let {data} = await axios.get('https://platform.jomalls.com/api/tools/getCompanyList')
res.send(data)
} catch (err) {
console.log(err)
res.json({code: 500, msg: err})
}
}
fn.commitApply = async (req, res) => {
const company = req.headers["company"]
const q = req.body
try {
let {data} = await axios.post(`https://${company}/api/sysDeviceInfo/commitApply`, q)
res.send(data)
} catch (err) {
console.log(err)
res.json({code: 500, msg: err})
}
}
fn.completeDelivery = async (req, res) => {
const token = req.headers["jwt-token"]
const company = req.headers["company"]
const q = req.body
try {
let {data} = await axios.post(`https://${company}/api/pod/podProductionInfo/completeDelivery`, q,{
headers: {'jwt-token': token}
})
res.json(data)
} catch (err) {
res.json({code: 500, msg: err})
}
}
fn.downloadByDesignId = async (req, res) => {
const token = req.headers["jwt-token"]
const company = req.headers["company"]
try {
let downloadByDesignId = await axios.post(`https://${company}/api/podDesignCenter/downloadByDesignId`, req.body, {
headers: {'jwt-token': token, responseType: 'blob',}
})
console.log(downloadByDesignId.headers)
const dispositionStr = downloadByDesignId.headers['content-disposition']
if (dispositionStr == null || dispositionStr === '') {
return res.json({code: 500, msg: '下载失败'})
}
let dispositionArr = dispositionStr.split(';')[1].split('filename=')[1]
let fileName = decodeURIComponent(dispositionArr)
let dir = path.join(os.homedir(), '/Desktop/' + fileName)
console.log(dir,'dir')
let ws=fs.createWriteStream(dir );
ws.write(downloadByDesignId.data);
ws.on('drain',function () {
console.log("内存干了");
});
ws.on('error',function (err) {
res.json({code: 500, msg: '文件下载失败'})
});
ws.on('close',function (err) {
});
ws.end()
res.json({code: 200, msg: fileName + '已下载到桌面'})
} catch (err) {
console.log(err)
res.json({code: 500, msg: err})
}
}
fn.uploadImage = async (req, res) => {
try {
const p = path.join(process.cwd(), './print/Input/')
let fileName = uuid.v4() + ".png";
console.log(fileName)
const form = new multiparty.Form({uploadDir: p});
form.parse(req, function (err, fields, files) {
console.log(fields, files, err)
if (err) {
res.send({code: 500, err})
} else {
fs.renameSync(files.file[0].path, path.join(p, fileName))
res.json({code: 200, data: {fileName, url: path.join(process.cwd(), './print/Input/' + fileName)}})
}
});
} catch (err) {
console.log(err)
res.json({code: 500, msg: err})
}
}
fn.getPngImg = async (req, res) => {
try {
const filePath = path.join(process.cwd(), `./print/Input/${req.body.fileName}`);
console.log(filePath)
// 给客户端返回一个文件流 type类型
res.set('content-type', {"png": "image/png", "jpg": "image/jpeg"});//设置返回类型
let stream = fs.createReadStream(filePath);
let responseData = [];//存储文件流
if (stream) {//判断状态
stream.on('data', function (chunk) {
responseData.push(chunk);
});
stream.on('end', function () {
const finalData = Buffer.concat(responseData);
res.write(finalData);
res.end();
});
}
} catch (err) {
res.send({code: 500, msg: err})
}
}
fn.login = async (req, res) => {
const {loginName, company, password, deviceId} = req.body
try {
let {data} = await axios.post(`https://${company}/api/sysLogin/login`, {loginName, password, deviceId})
res.send(data)
} catch (err) {
console.log(err)
res.json({code: 500, msg: err})
}
}
fn.toPrint = (req, res) => {
let body = req.body;
writeProfileXml(body) // 写入xml文件
// GTXproCMD.exe print -X "Profile\\CO12.xml" -I "Input\\sample.png" -A "Output\\pO12.arxp" -S 03000400 -L 02540254
toSend(body).then(r => {
res.send({code: 200, msg: '操作成功'})
}).catch(err => {
res.send({code: 500, msg: err})
})
};
export {fn as default};
import express from "express";
import fn from "../entity/function.js";
import {getPodProductionInfo, sendImg} from "@/backend/webserver/utils";
let router = express.Router();
router.post("/toPrint", fn.toPrint);
router.post("/login", fn.login);
router.post("/getPngImg", fn.getPngImg);
router.post("/uploadImage", fn.uploadImage);
router.get("/getCompanyList", fn.getCompanyList);
router.post("/commitApply", fn.commitApply);
router.post("/downloadByDesignId", fn.downloadByDesignId);
router.post("/getPodProductionInfo", fn.getPodProductionInfo);
router.post("/completeDelivery", fn.completeDelivery);
router.post("/findByPodProductionNo", fn.findByPodProductionNo);
export {router as default};
import {exec} from "child_process";
var fs = require("fs");
var path = require("path");
var request = require("request");
var uuid = require("uuid");
// 下载素材
export const downloadImage = (list, company, callback, errorBack = null) => {
try {
let dirPath = path.join(process.cwd(), "./print/Input/");
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath);
}
for (let i = 0; i < list.length; i++) {
if (!list[i].productionFile) return
if (!list[i].productionFile.includes('http')) return;
let fileName = uuid.v4() + ".png";
let p = path.join(process.cwd(), './print/Input/' + fileName)
let stream = fs.createWriteStream(p);
list[i].fileName = fileName
request(list[i].productionFile).pipe(stream).on("close", function (err) {
console.log("文件[" + fileName + "]下载完毕");
if (i === list.length - 1) {
callback && callback(list)
}
});
}
} catch (err) {
console.log(err)
errorBack && errorBack(err)
}
}
// 传递素材给前端
export const sendImg = (filename = 'sample.png') => {
let filePath = path.join(process.cwd(), './print/Input/' + filename)
if (!fs.existsSync(filePath)) {
return false
}
let data = fs.readFileSync(filePath);
data = Buffer.from(data).toString('base64');
return 'data:' + 'png' + ';base64,' + data;
}
export const toSend = (body) => {
return new Promise((resolve, reject) => {
exec(
body.cmd,
{cwd: path.join(process.cwd(), 'print'), shell: true},
(err, stdout, stderr) => {
console.log(stdout, 1)
console.log(stderr, 2)
console.log(err, 4)
if (!err) {
exec(
body.print_cmd,
{cwd: path.join(process.cwd(), 'print'), shell: true},
(err2, stdout2, stderr2) => {
console.log(stdout2, 1)
console.log(stderr2, 2)
console.log(err2, 4)
if (!err2) {
// 成功后删除 素材图,xml,output文件
fs.unlinkSync(path.join(process.cwd(), 'print/Input/' + body.fileName))
fs.unlinkSync(path.join(process.cwd(), 'print/Profile/' + body.fileName.replace('.png', '') + '.xml'))
fs.unlinkSync(path.join(process.cwd(), 'print/Output/' + body.fileName.replace('.png', '') + '.arxp'))
resolve('操作成功')
} else {
reject('操作出现问题')
}
})
} else {
reject('操作出现问题')
}
})
})
}
export const writeProfileXml = (b) => {
try {
let p = path.join(process.cwd(), `./print/Profile/${b.byInk}.xml`);
let file = fs.readFileSync(p, {encoding: "utf8"});
file = file.replace(/<uiCopies>(.*)<\/uiCopies>/i, `<uiCopies>${b.printNum}</uiCopies>`)
file = file.replace(/<byPlatenSize>(.*)<\/byPlatenSize>/i, `<byPlatenSize>${b.byPlatenSize}</byPlatenSize>`)
file = file.replace(/<bEcoMode>(.*)<\/bEcoMode>/i, `<bEcoMode>${b.bEcoMode}</bEcoMode>`)
file = file.replace(/<bMaterialBlack>(.*)<\/bMaterialBlack>/i, `<bMaterialBlack>${b.bMaterialBlack}</bMaterialBlack>`)
file = file.replace(/<byHighlight>(.*)<\/byHighlight>/i, `<byHighlight>${b.byHighlight}</byHighlight>`)
file = file.replace(/<byMask>(.*)<\/byMask>/i, `<byMask>${b.byMask}</byMask>`)
file = file.replace(/<bTransColor>(.*)<\/bTransColor>/i, `<bTransColor>${b.bTransColor}</bTransColor>`)
file = file.replace(/<bPause>(.*)<\/bPause>/i, `<bPause>${b.bPause}</bPause>`)
file = file.replace(/<bDivide>(.*)<\/bDivide>/i, `<bDivide>${b.bDivide}</bDivide>`)
file = file.replace(/<byChoke>(.*)<\/byChoke>/i, `<byChoke>${b.byChoke}</byChoke>`)
file = file.replace(/<byInk>(.*)<\/byInk>/i, `<byInk>${b.byInk}</byInk>`)
file = file.replace(/<bFastMode>(.*)<\/bFastMode>/i, `<bFastMode>${b.bFastMode}</bFastMode>`)
file = file.replace(/<byResolution>(.*)<\/byResolution>/i, `<byResolution>${1}</byResolution>`)
file = file.replace(/<byInkVolume>(.*)<\/byInkVolume>/i, `<byInkVolume>${b.byInkVolume}</byInkVolume>`)
file = file.replace(/<byDoublePrint>(.*)<\/byDoublePrint>/i, `<byDoublePrint>${b.byDoublePrint}</byDoublePrint>`)
file = file.replace(/<bMultiple>(.*)<\/bMultiple>/i, `<bMultiple>${b.bMultiple}</bMultiple>`)
file = file.replace(/<bySaturation>(.*)<\/bySaturation>/i, `<bySaturation>${b.bySaturation}</bySaturation>`)
file = file.replace(/<byBrightness>(.*)<\/byBrightness>/i, `<byBrightness>${b.byBrightness}</byBrightness>`)
file = file.replace(/<byContrast>(.*)<\/byContrast>/i, `<byContrast>${b.byContrast}</byContrast>`)
file = file.replace(/<iCyanBalance>(.*)<\/iCyanBalance>/i, `<iCyanBalance>${b.iCyanBalance}</iCyanBalance>`)
file = file.replace(/<iMagentaBalance>(.*)<\/iMagentaBalance>/i, `<iMagentaBalance>${b.iMagentaBalance}</iMagentaBalance>`)
file = file.replace(/<iYellowBalance>(.*)<\/iYellowBalance>/i, `<iYellowBalance>${b.iYellowBalance}</iYellowBalance>`)
file = file.replace(/<iBlackBalance>(.*)<\/iBlackBalance>/i, `<iBlackBalance>${b.iBlackBalance}</iBlackBalance>`)
file = file.replace(/<bUniPrint>(.*)<\/bUniPrint>/i, `<bUniPrint>${b.bUniPrint}</bUniPrint>`)
fs.writeFileSync(path.join(process.cwd(), `./print/Profile/${b.fileName.replace('.png', '')}.xml`), file)
} catch (err) {
console.log(err)
}
}
"use strict";
import {contextBridge, ipcMain, ipcRenderer} from 'electron'
import {app, protocol, BrowserWindow, Menu, screen} from "electron";
import {createProtocol} from "vue-cli-plugin-electron-builder/lib";
import webApp from "@/backend/webserver/be_nodeSrv.js"
let isCreateWin = false
const isDevelopment = process.env.NODE_ENV !== "production";
let win;
let otherWindow;
protocol.registerSchemesAsPrivileged([
{scheme: "app", privileges: {secure: true, standard: true}}
]);
const winURL = process.env.NODE_ENV === 'development'
? 'http://localhost:8090'
: `file://${__dirname}/index.html`
function createWindow() {
win = new BrowserWindow({
width: 1500,
height: 1000,
icon: "./src/assets/logo.png",
webPreferences: {
webSecurity: false,
nodeIntegration: true,
// preload: path.join(__dirname, "./preload.js")
}
});
let displays = screen.getAllDisplays();
//寻找副屏幕
let externalDisplay = displays.find((display) => {
return display.bounds.x !== 0 || display.bounds.y !== 0
})
if (externalDisplay) {
otherWindow = new BrowserWindow({
fullscreen: false,
width: externalDisplay.bounds.width,
height: externalDisplay.bounds.height,
x: externalDisplay.bounds.x,
y: externalDisplay.bounds.y,
webPreferences: {
nodeIntegration: true,
//配置预加载脚本文件(preload),此处起名为preloadOther
//p.s.路径为绝对路径
// preload: path.join(__dirname, "./preloadOther.js")
}
})
//指定副屏幕打开的网页
otherWindow.loadURL(winURL + '#' + `/design-detail`);
otherWindow.on('closed', () => { //这一段放外面的话,如果你电脑没双屏会报错。
otherWindow = null
})
}
ipcMain.on('allPrint', () => {
// 获取到打印机列表
const printers = win.webContents.getPrinters()
// 把获取的打印机列表发送给渲染进程
win.webContents.send('printName', printers)
})
ipcMain.on('win-subScreen', (data, v) => {
if(otherWindow) otherWindow.webContents.send('getProductionNoInfo', v)
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
if (!process.env.IS_TEST) win.webContents.openDevTools();
} else {
createProtocol("app");
win.loadURL("app://./index.html");
}
ipcMain.on('send-card', (event, obj) => {
win.webContents.send('sendWebsockt', obj);
})
win.on("closed", () => {
win = null;
otherWindow = null;
});
}
app.on("activate", () => {
if (win === null) {
createWindow();
}
});
app.on("ready", async () => {
createWindow();
});
if (isDevelopment) {
if (process.platform === "win32") {
process.on("message", data => {
if (data === "graceful-exit") {
app.quit();
}
});
} else {
process.on("SIGTERM", () => {
app.quit();
});
}
}
import Vue from 'vue';
export default new Vue();
\ No newline at end of file
export const lang = {
slice: {
placeholder: {
name: "请输出切片名称",
nst: "请选择NST模板"
},
tips: {
sla: "带宽:{0} Mbps, 时延: {1} ms"
}
}
}
\ No newline at end of file
export const lang = {
slice: {
placeholder: {
name: "请输出切片名称",
nst: "请选择NST模板"
},
tips: {
sla: "带宽:{0} Mbps, 时延: {1} ms"
}
}
}
\ No newline at end of file
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import VueI18n from 'vue-i18n';
import VueCompositionApi from "@vue/composition-api";
import { Message } from "element-ui";
// 挂载到$message上
import api from '@/utils/axios'
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import dataStore from 'electron-store';
const DataStore = new dataStore();
Vue.config.productionTip = false;
Vue.use(ElementUI);
Vue.use(VueCompositionApi);
Vue.use(VueI18n);
Vue.prototype.$message = Message
Vue.prototype.$dataStore = DataStore
Vue.prototype.$store = store
Vue.prototype.$api = api
Vue.prototype.$message = Message
const i18n = new VueI18n({
locale: 'zh',
messages: {
'zh': require('./i18n/zh.js'),
'en': require('./i18n/en.js')
}
});
new Vue({
router,
store,
i18n,
render: h => h(App)
}).$mount("#app");
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
const routes = [
{
path: '/',
name: 'login',
meta: {
title: '登录'
},
component: (resolve) => require(['../views/login/index.vue'], resolve)
},
{
path: '/design',
name: 'design',
meta: {
title: '设计页面'
},
component: (resolve) => require(['../views/design/index.vue'], resolve)
}, {
path: '/design-detail',
name: 'design',
meta: {
title: '设计详情页面'
},
component: (resolve) => require(['../views/design/detail/index.vue'], resolve)
}, {
path: '*',
redirect: '/'
},
];
const router = new VueRouter({
mode: "hash",
base: process.env.BASE_URL,
routes
});
// router.beforeEach((to, from, next) => {
// let user = Vue.prototype.$dataStore.get('user')
// if (to.name === 'login') {
// if (user) {
// console.log(1)
// return next({path:'/design'})
// } else {
// console.log(2)
// return next( )
// }
//
// } else {
// if (user) {
// if(!to.name) return next({path:'/design'})
// return next()
// } else {
// return next('/login')
// }
// }
//
// })
export default router;
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const defaultSetting = {
gridShow: 1,
language: 'cn',
gridValue: 1,
autoPrint: false,
gridSpacing: "10mm"
}
export default new Vuex.Store({
state: {
actionList:[],
imgList:[],
actionIndex:-1,
systemSetting: {
gridShow: 1,
gridValue: 1,
language: 'cn',
autoPrint: false,
gridSpacing: "10mm"
}
},
mutations: {
setDefaultSetting(state) {
state.systemSetting = JSON.parse(JSON.stringify(defaultSetting))
},
updateSystemSetting(state, f) {
state.systemSetting[f.field] = f.value
},
changeActionList(state, value) {
state.actionList = value
},
changeActionIndex(state, value) {
state.actionIndex = value
},
changeImgList(state, value) {
state.imgList = value
}
},
getters: {
systemSetting(state) {
return state.systemSetting;
},
getActionList(state) {
return state.actionList;
},
getActionIndex:(state)=> state.actionIndex,
imgList:(state)=> state.imgList
},
actions: {},
modules: {}
});
import axios from 'axios'
import Vue from 'vue'
import {Loading} from 'element-ui'
import router from "../router/index";
// create an axios instance
const service = axios.create({
baseURL: 'http://localhost:3000', // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 50000000 // request timeout
})
let loading
function startLoading() {
loading = Loading.service({
lock: true,
text: '拼命加载中...',
spinner: 'el-icon-loading',
background: 'rgba(0,0,0,.7)'
})
}
function endLoading() {
loading.close()
}
// Add a request interceptor
service.interceptors.request.use(
config => {
const user = Vue.prototype.$dataStore.get('user')
const company = Vue.prototype.$dataStore.get('company')
if (user) {
config.headers['jwt-token'] = user.token
config.headers['company'] = company.domain
}
startLoading()
return config
},
error => {
endLoading()
// do something with request error
return Promise.reject(error)
}
)
// Add a response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
// do something with response data
const res = response.data
endLoading()
if (res.code) {
if (res.code !== 200) {
if (res.code === 411) {
return Promise.resolve(res)
}
if (res.code === 403) {
router.replace('/')
Vue.prototype.$message.error(res.msg || res.message || 'Error')
Vue.prototype.$dataStore.delete('user')
return Promise.reject(new Error(res.msg || res.message || 'Error'))
}
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// TODO: to re-login
}
Vue.prototype.$message.error(res.msg || res.message || 'Error')
// reject
return Promise.reject(new Error(res.msg || res.message || 'Error'))
} else {
return Promise.resolve(res)
}
}
return Promise.resolve(res)
},
error => {
endLoading()
// do something with response error
return Promise.reject(error)
}
)
export default service
<script>
import {ipcRenderer} from "electron";
export default {
mounted() {
let that = this;
ipcRenderer.on('getProductionNoInfo', (event, data) => {
console.log(data, 99622)
that.trackcodeInput(data)
})
},
watch: {
},
data() {
return {
shipForm: {shipmentType: '1'},
logistics: [],
placeholderText: '',
sendNum: 0,
type: 1,
isDownloadImage: false,
isAutoSure: false,
currentStatus: 'IN_PRODUCTION',
detail: {
imgList: []
},
dialogVisible: false,
audios: {
weight_warning: require(`../../../assets/audio/weight_warning.mp3`),
weight_success: require(`../../../assets/audio/weight_success.mp3`),
weight_repeat: require(`../../../assets/audio/weight_repeat.mp3`),
weight_search_error: require(`../../../assets/audio/weight_search_error.mp3`),
weight_search_success: require(`../../../assets/audio/weight_search_success.mp3`)
},
productInfo: {},
TrackingNumber: ''
}
},
methods: {
changeStatus() {
if (!this.detail || Object.keys(this.detail).length <= 1) {
return this.$message.warning('请扫码生产单号')
}
this.$confirm(
`确定${this.type === 1 ? '生产完成' : '确定发货'}?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
this.setData()
})
},
async setData() {
if (this.detail && this.detail.id) {
let url =
this.currentStatus == 'IN_PRODUCTION'
? 'pod/podProductionInfo/completeDelivery'
: 'pod/podProductionInfo/completeShipment'
try {
let data =
this.currentStatus == 'IN_PRODUCTION'
? {
id: this.detail.id
}
: {
list: [this.detail],
...this.shipForm
}
if (data.list) {
data.list[0].note =
data.list[0].note && typeof data.list[0].note === 'object'
? JSON.stringify(data.list[0].note)
: null
}
await post(url, data)
if (this.type === 2) {
this.sendNum = this.sendNum + 1
}
this.playAudio('weight_success')
this.detail = {imgList: []}
} catch (e) {
console.error(e)
this.detail = {imgList: []}
this.playAudio('weight_search_error')
}
}
},
downloadAllWe(data, designId) {
if (!this.detail || Object.keys(this.detail).length <= 1) {
return this.$message.warning('请扫码生产单号')
}
let params = {
productionNo: this.detail.podProductionNo,
method: 'POST'
}
if (data === 1) {
params = {
...params,
...{
designId
}
}
} else if (data === 2) {
params = {
...params,
...{
negativeDesignId: designId
}
}
}
downloadFile('podDesignCenter/downloadByDesignId', params)
.then((res) => {
const {data, fileName} = res
let url = window.URL.createObjectURL(data)
// 生成一个a标签
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
// 生成时间戳
link.download = fileName
link.click()
window.URL.revokeObjectURL(url)
})
.catch((err) => {
console.log(err)
})
},
playAudio(key, message) {
let text = ''
switch (key) {
case 'weight_search_success':
text = ''
break
case 'weight_search_error':
text = '请录入生产单号'
break
case 'weight_success':
text = ''
break
case 'weight_repeat':
text = '称重复录入'
break
default:
text = '请录入跟踪号或重量'
break
}
if (message || text) this.$message.warning(message || text)
let audio = document.createElement('audio')
console.log(key)
audio.src = this.audios[key]
audio.play()
},
async trackcodeInput(data) {
// if (this.type === 1) {
// if (this.isAutoSure) {
// await this.setData()
// }
// } else {
// await this.setData()
// }
try {
let d = JSON.parse(JSON.stringify(data))
if (d.note) {
d.note = JSON.parse(d.note)
} else {
d.note = []
}
let arr = []
if (!d.imageAry) {
arr = [
{url: d.variantImage, title: '正面', id: d.designId},
{url: d.negativeImage, title: '反面', id: d.negativeDesignId}
].filter((el) => el.url)
} else {
arr =
typeof d.imageAry == 'string' ? JSON.parse(d.imageAry) : d.imageAry
arr = arr.concat([
{url: d.negativeImage, title: '反面', id: d.negativeDesignId}
])
}
const result = []
console.log(arr)
arr = arr.filter((el) => el.url)
arr = arr.map((el) => {
return {
...el,
...{
url: el.url.includes('http')
? el.url
: window.location.origin + '/upload/erp' + el.url
}
}
})
for (let i = 0; i < arr.length; i += 2) {
result.push(arr.slice(i, i + 2))
}
d.imgList = result
console.log(d.imgList, 'this.detail.imgList ')
this.detail = d
// 自动下载素材
// if (this.isDownloadImage) {
// this.downloadAllWe()
// }
this.playAudio('weight_search_success')
// this.$nextTick(() => {
// if (this.$refs.trackingNumberRef) {
// this.$refs.trackingNumberRef.focus()
// }
// })
} catch (e) {
console.error(e)
this.productInfo = {}
this.playAudio('weight_search_error')
this.$nextTick(() => {
if (this.$refs.trackingNumberRef) {
this.$refs.trackingNumberRef.focus()
}
})
}
}
}
}
</script>
<template>
<div class="page">
<div class="sure-btn" v-if="type=== 2">
<el-button @click="changeStatus" size="small" type="success">
{{ type === 1 ? '生产完成' : '确定发货' }}
</el-button>
</div>
<div class="detail-div">
<div class="detail-content">
<div class="left">
<div class="left-images">
<el-carousel
v-if="detail.imgList.length > 0"
style="height: 100%"
:autoplay="false"
indicator-position="none">
<el-carousel-item
style="height: 100%"
:key="index"
v-for="(item, index) in detail.imgList">
<el-row :gutter="10" style="width: 100%; height: 100%">
<el-col
style="height: 100%"
v-for="(it, i) in item"
:key="i"
:span="12">
<div class="left-image">
<b v-show="it.title && it.url">
{{ it.title }}
<span
style="
text-decoration: underline;
cursor: pointer;
color: blue;
"
@click="
downloadAllWe(
it.title && it.title === '正面' ? 1 : 2,
it.id
)
"
v-if="it.id">
(DID:{{ it.id }}
</span>
</b>
<img :src="it.url" alt=""/>
</div>
</el-col>
</el-row>
</el-carousel-item>
</el-carousel>
</div>
</div>
<div class="right">
<!-- <div-->
<!-- class="input"-->
<!-- v-if="!detailData || Object.keys(detailData).length === 0">-->
<!--&lt;!&ndash; <el-input&ndash;&gt;-->
<!--&lt;!&ndash; v-model="TrackingNumber"&ndash;&gt;-->
<!--&lt;!&ndash; :placeholder="placeholderText"&ndash;&gt;-->
<!--&lt;!&ndash; style="width: 660px; margin-right: 10px"&ndash;&gt;-->
<!--&lt;!&ndash; clearable&ndash;&gt;-->
<!--&lt;!&ndash; ref="trackingNumberRef"&ndash;&gt;-->
<!--&lt;!&ndash; size="medium"&ndash;&gt;-->
<!--&lt;!&ndash; @keyup.enter.native="trackcodeInput()"></el-input>&ndash;&gt;-->
<!-- <el-button type="primary" size="medium" @click="trackcodeInput()">-->
<!-- 查询-->
<!-- </el-button>-->
<!-- </div>-->
<div class="div-text">
<b>生产单信息</b>
<div class="div-content">
<div :title="detail.podProductionNo" class="div-item">
<span>生产单号</span>
<p>{{ detail.podProductionNo }}</p>
<p
style="margin-left: 5px"
v-if="detail.podProductionNo"
:style="{
color: detail.customizedQuantity > 1 ? 'red' : '#67C23A'
}">
{{ detail.customizedQuantity > 1 ? '多' : '单' }}
</p>
</div>
<div :title="detail.podProcessName" class="div-item">
<span>生产工艺</span>
<p>{{ detail.podProcessName }}</p>
</div>
<div :title="detail.baseSku" class="div-item">
<span>基版</span>
<p>{{ detail.baseSku }}</p>
</div>
<div :title="detail.supplierItemNo" class="div-item">
<span>货号</span>
<p>{{ detail.supplierItemNo }}</p>
</div>
<div :title="detail.variantSku" class="div-item">
<span>变体SKU</span>
<p>{{ detail.variantSku }}</p>
</div>
<div :title="detail.categoryName" class="div-item">
<span>类别</span>
<p>{{ detail.categoryName }}</p>
</div>
<div :title="detail.color" class="div-item">
<span>颜色</span>
<p>{{ detail.color }}</p>
</div>
<div :title="detail.size" class="div-item">
<span>尺寸</span>
<p>{{ detail.size }}</p>
</div>
<div :title="detail.material" class="div-item">
<span>材质</span>
<p>{{ detail.material }}</p>
</div>
<div :title="detail.batchNo" class="div-item">
<span>批次号</span>
<p>{{ detail.batchNo }}</p>
</div>
<div
:title="detail.supplyMerchantCode"
v-if="currentStatus !== 'TO_BE_ASSIGN'"
class="div-item">
<span>供应商编号</span>
<p>{{ detail.supplyMerchantCode }}</p>
</div>
<div :title="detail.shopNumber" class="div-item">
<span>店铺单号</span>
<p>{{ detail.shopNumber }}</p>
</div>
<div :title="detail.shopShortName" class="div-item">
<span>店铺简称</span>
<p>{{ detail.shopShortName }}</p>
</div>
<div :title="detail.expectDeliveryTime" class="div-item">
<span>期望交货时间</span>
<p>{{ detail.expectDeliveryTime }}</p>
</div>
<div :title="detail.purchaseUserName" class="div-item">
<span>采购员</span>
<p>{{ detail.purchaseUserName }}</p>
</div>
<div :title="detail.productionConfirmTime" class="div-item">
<span>生产确认时间</span>
<p>{{ detail.productionConfirmTime }}</p>
</div>
<div :title="detail.followerName" class="div-item">
<span>跟单员</span>
<p>{{ detail.followerName }}</p>
</div>
<div :title="detail.actualDeliveryTime" class="div-item">
<span>实际交货时间</span>
<p>{{ detail.actualDeliveryTime }}</p>
</div>
<div :title="detail.storedName" class="div-item">
<span>入库人</span>
<p>{{ detail.storedName }}</p>
</div>
<div :title="detail.storedTime" class="div-item">
<span>入库时间</span>
<p>{{ detail.storedTime }}</p>
</div>
<div :title="detail.replenishmentNumber" class="div-item">
<span>补胚数量</span>
<p>{{ detail.replenishmentNumber }}</p>
</div>
<div
:title="detail.shipmentType == 1 ? '送货上门' : '快递'"
class="div-item">
<span>发货方式</span>
<p v-if="detail.shipmentType">
{{ detail.shipmentType == 1 ? '送货上门' : '快递' }}
</p>
</div>
</div>
</div>
<div
class="form"
style="margin: 15px 0; background: #4168ff; border-radius: 5px"
v-if="type === 2">
<el-form
:model="shipForm"
ref="form"
label-width="80px"
:inline="false"
size="small">
<el-form-item style="margin-top: 10px" label="发货方式">
<el-select
style="width: 384px"
v-model="shipForm.shipmentType"
value-key=""
placeholder="请选择">
<el-option label="送货上门" value="1"></el-option>
<el-option label="快递" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item
label="物流方式"
v-if="shipForm.shipmentType == '2'">
<el-select
v-model="shipForm.shipmentWay"
style="width: 384px"
placeholder="请选择">
<el-option
v-for="(item, index) in logistics"
:key="index"
:label="item.name"
:value="item.name"></el-option>
</el-select>
</el-form-item>
<el-form-item
label="物流单号"
v-if="shipForm.shipmentType == '2'">
<el-input
style="width: 384px"
v-model="shipForm.shipmentNumber"
placeholder="请输入"></el-input>
</el-form-item>
<el-form-item
label="物流费用"
v-if="shipForm.shipmentType == '2'">
<el-input
style="width: 384px"
v-model="shipForm.shipmentFreight"
placeholder="请输入"></el-input>
</el-form-item>
</el-form>
</div>
<!-- <div-->
<!-- class="btn"-->
<!-- v-if="type !== 2">-->
<!-- <div class="btn-sure">-->
<!-- <el-button style="width: 100%; height: 100%; font-size: 18px" size="large" @click="changeStatus"-->
<!-- type="success">-->
<!-- {{ type === 1 ? '生产完成' : '确定发货' }}-->
<!-- </el-button>-->
<!-- &lt;!&ndash; <div class="check">&ndash;&gt;-->
<!-- &lt;!&ndash; <el-checkbox v-if="type===1" v-model="isAutoSure">自动完成上一单</el-checkbox>&ndash;&gt;-->
<!-- &lt;!&ndash; </div>&ndash;&gt;-->
<!-- </div>-->
<!-- &lt;!&ndash; <div class="btn-down">&ndash;&gt;-->
<!-- &lt;!&ndash; <div class="check">&ndash;&gt;-->
<!-- &lt;!&ndash; <el-checkbox size="large" v-model="isDownloadImage">扫码下载素材</el-checkbox>&ndash;&gt;-->
<!-- &lt;!&ndash; </div>&ndash;&gt;-->
<!-- &lt;!&ndash; <el-button&ndash;&gt;-->
<!-- &lt;!&ndash; size="medium"&ndash;&gt;-->
<!-- &lt;!&ndash; @click="downloadAllWe"&ndash;&gt;-->
<!-- &lt;!&ndash; style="width: 100%; height: 100%; font-size: 18px"&ndash;&gt;-->
<!-- &lt;!&ndash; type="primary">&ndash;&gt;-->
<!-- &lt;!&ndash; 下载素材&ndash;&gt;-->
<!-- &lt;!&ndash; </el-button>&ndash;&gt;-->
<!-- &lt;!&ndash; </div>&ndash;&gt;-->
<!-- </div>-->
<div
v-if="type !== 2"
class="div-text"
style="
flex: 1;
margin-top: 30px;
flex-shrink: 0;
display: flex;
flex-direction: column;
">
<div style="height: 100%" class="div-content" v-if="detail.note">
<b style="position: absolute; top: -12px">客户留言信息</b>
<div
v-for="(item, index) in detail.note"
:key="index"
class="div-item">
<span>{{ item.prop }}:</span>
<p>
{{ item.value }}
</p>
</div>
</div>
</div>
<div
v-if="type === 2"
class="div-text"
style="
flex: 1;
flex-shrink: 0;
display: flex;
flex-direction: column;
">
<div style="height: 100%" class="div-content">
<b style="position: absolute; top: -12px">本次发货数量</b>
<div
class="div-item"
style="
display: flex;
align-items: center;
width: 100%;
height: 100%;
font-size: 40px;
justify-content: center;
">
<h2 style="color: red; font-weight: bold">{{ sendNum }}</h2>
</div>
</div>
</div>
</div>
</div>
<div
class="detail-images"
v-if="detail && Object.keys(detail).length > 1">
<div class="scroll-list">
<div class="img-title">
<b>商品信息</b>
<div class="id">
<img src="@/assets/id.png" alt=""/>
<span>{{ detail.variantSku.split('_')[0] }}</span>
</div>
</div>
<div class="scroll-content">
<div
class="scroll-item"
v-for="(it, i) in detail.colorImageList"
:key="i">
<el-image
style="width: 100%; height: 100%"
:src="it"
:preview-src-list="[it]"></el-image>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
::v-deep .el-dialog__body {
box-sizing: border-box;
flex: 1;
flex-shrink: 0;
overflow: hidden;
}
::v-deep .el-dialog {
display: flex;
height: calc(100% - 50px);
margin-top: 50px;
flex-direction: column;
}
.sure-btn {
position: absolute;
right: 62px;
top: 14px;
}
.detail-div {
display: flex;
height: 100%;
flex-direction: column;
justify-content: space-between;
}
.detail-div .detail-images .scroll-list {
background: #ececec;
display: flex;
height: 100px;
width: 100%;
padding: 5px;
}
.detail-div .detail-images .scroll-list .scroll-content {
margin-left: 10px;
overflow-x: auto;
overflow-y: hidden;
flex: 1;
display: flex;
flex-wrap: nowrap;
flex-shrink: 0;
}
.detail-div .detail-images .scroll-list .scroll-content .scroll-item {
height: 100%;
min-width: 100px;
background: white;
width: 80px;
margin-right: 5px;
}
.detail-div .detail-images .scroll-list .img-title {
display: flex;
flex-direction: column;
justify-content: center;
background: white;
padding: 10px;
}
.detail-div .detail-images .scroll-list .img-title b {
text-align: center;
color: black;
font-weight: bold;
font-size: 16px;
margin-bottom: 15px;
}
.detail-div .detail-images .scroll-list .img-title .id {
display: flex;
align-items: center;
padding: 3px 5px;
background: #ececec;
justify-content: center;
}
.detail-div .detail-images .scroll-list .img-title .id img {
width: 15px;
margin-right: 8px;
}
.detail-div .detail-content {
display: flex;
width: 100%;
flex: 1;
margin-bottom: 10px;
flex-shrink: 0;
justify-content: space-between;
}
.detail-div .right {
width: 710px;
height: 100%;
display: flex;
flex-direction: column;
}
.detail-div .right .btn {
margin: 20px 0;
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
width: 100%;
}
.detail-div .right .btn .btn-sure, .detail-div .right .btn .btn-down {
width: 100%;
position: relative;
}
.detail-div .right .btn .btn-sure .check, .detail-div .right .btn .btn-down .check {
position: absolute;
width: 144px;
height: 100%;
background: transparent;
display: flex;
align-items: center;
justify-content: center;
right: 0;
top: 1px;
}
.detail-div .right .div-text .div-content {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: flex-start;
padding: 15px 10px;
box-sizing: border-box;
}
.detail-div .right .div-text .div-content .div-item {
width: 50%;
margin-bottom: 10px;
font-size: 15px;
display: flex;
align-items: center;
}
.detail-div .right .div-text .div-content .div-item p {
font-weight: 400;
color: black;
}
.detail-div .right .div-text .div-content .div-item span {
display: inline-block;
text-align: right;
width: 100px;
}
.detail-div .right .div-text .div-content .div-item span::after {
content: ':';
margin: 0 3px;
}
.detail-div .right .div-text b {
position: relative;
background: white;
top: 9px;
left: 13px;
padding: 0 10px;
font-size: 18px;
color: black;
z-index: 3;
}
.detail-div .right .div-text .div-content {
position: relative;
border: 1px solid #ececec;
}
.detail-div .right .input {
display: flex;
align-items: center;
margin: 30px 0;
}
.detail-div .left {
flex: 1;
flex-shrink: 0;
margin-right: 20px;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.detail-div .left .left-image {
display: flex;
height: 100%;
flex-direction: column;
justify-content: center;
}
.detail-div .left .left-image img {
height: auto;
width: 100%;
max-height: 90%;
}
.detail-div .left .left-image b {
color: black;
font-size: 18px;
margin-bottom: 15px;
}
.detail-div .left .left-images {
display: flex;
width: 95%;
height: 100%;
flex-direction: column;
}
.detail-div .left .left-images b {
color: black;
text-align: center;
margin-bottom: 15px;
}
::v-deep .el-carousel__container {
height: 100%;
}
::v-deep .el-form-item__label {
font-weight: bold;
color: white;
}
::v-deep .el-dialog__title {
font-weight: bold;
font-size: 37px;
color: black;
position: relative;
left: 47%;
top: 13px;
}
.btn {
position: relative;
}
.btn .check ::v-deep .el-checkbox__inner {
background-color: transparent !important;
border-color: white !important;
width: 12px;
height: 12px;
}
.btn .check ::v-deep .el-checkbox__inner::after {
left: 3px;
}
.btn .check ::v-deep .el-checkbox__label {
padding-left: 5px;
font-size: 12px;
color: white !important;
}
.page{
width: 100%;
height: 100%;
}
p{
margin: 0 ;
}
</style>
export default {
byInk:`
<b>油墨选择</b>
<p>选择所使用油墨的组合方式:</p>
<p>选择所使用油墨的组合方式:</p>
<p>彩色+白色油墨:在白色油墨的遮光层上打印彩色图像。</p>
<p>仅彩色油墨:在白底或淡色底上打印彩色图像。</p>
<p>仅黑色油墨:只使用黑色油墨,打印黑白图像。</p>
`,
bEcoMode:`
<b>模式</b>
<p>自动调整底材的白色油墨量,抑制整体的油墨消耗量。</p>
<p>但是,根据颜色,完成品的亮度有时会略微降低。</p>
`,
bMaterialBlack:
`
<b>利用底材的黑色</b>
<p>在黑色底材上打印时,利用底材的黑色打印时,设定为ON。</p>
<p>自动将黑色(RGB=0)作为透明色处理。</p>
<p>已在详细设置菜单中将其它颜色指定为透明色时,设定为OFF。</p>
`,
bMultiple:
`
<b>彩色的复合路径打印</b>
<p>彩色的复合路径打印是针对含有彩色的图像通过增加压板的进给次数(LF)来喷出更精细的油墨液滴。尽管打印时间变长,但可以更加细致地再现色彩。</p>
`,
byHighlight:
`
<b>高光</b>
<p>选择RGB=254的白度(浓度)。值越大,白度越增加。</p>
<p>打印HighlightCheckPattern.arxp选择适当的值。</p>
`,
byMask:
`
<b>遮光</b>
<p>调节底材的白色油墨量。</p>
<p>底材饱和度高时请增加。</p>
<p>1-2:用于浅粉及灰色等淡色底。</p>
<p>3:默认。用于黑白底(黑、灰)。</p>
<p>4:用于红色、紫色、绿色等鲜艳的底色。</p>
<p>5:在暗色区以外的所有区域中以最大白色油墨量打印。</p>
`,
bFastMode:
`
<b>白色高速打印</b>
<p>在白色油墨使用模式下高速打印。油墨量和浓度不变。</p>
<p>横纹明显时设定为OFF。</p>
`,
bDivide:
`
<b>白色分次打印</b>
<p>分两层,每层用一半的油墨量进行白色打印。
总油墨量不变。</p>
`,
bPause:
`
<b>白色/彩色个别打印</b>
<p>打印白色层后,暂停(等待时间)后打印彩色层。打印极小的图像时请选择。</p>
`,
bTransColor:
`
<b>透明色</b>
<p>指定打印时不使用油墨而是作为透明色处理的颜色。</p>
<p>根据颜色的再现性,建议取消选中[利用底材的黑色]。</p>
`,
byChoke:
`
<b>白色油墨的削减范围</b>
<p>通过减少白色油墨的打印范围,防止白色油墨从彩色油墨下露出。</p>
<p>通常为1-3,数字越大,白色油墨的削减量增力口。</p>
<p>如果原图像的周围能看到白色的线或点,很可能是“抗锯齿“导致,请确认原图像。</p>
`,
byMinWhite:
`
<b>最小白色油墨量</b>
<p>设置值越大,使用“彩色+白色油墨"打印时的暗色区域底材的白色油墨量越增加。</p>
<p>如果选择“特殊”,则设置值可以选择大于“3”的值。</p>
<p>请注意,设置值越大,越容易渗透。</p>
`,
bySaturation:
`
<b>饱和度</b>
<p>增加颜色的鲜艳度。请用于稍暗或色调不足的图像、CMYK颜色模式的图像、扫描仪的图像旁。</p>
`,
byBrightness:
`
<b>亮度</b>
<p>值越大,颜色变得越亮。</p>
`,
byContrast:
`
<b>对比度</b>
<p>使亮的颜色更亮、暗的颜色更暗,从而给呆板的图像增加颜色强弱。</p>
`,
iCyanBalance :
`
<b>青色</b>
<p>调整青色油墨量。</p>
<p>如果增加值,整体会偏向淡蓝色。</p>
<p>如果减少值,整体会偏红。</p>
`,
iMagentaBalance :
`
<b>红色</b>
<p>调整青色油墨量。</p>
<p>如果增加值,整体会偏向红紫色。</p>
<p>如果减少值,整体会偏绿。</p>
`,
iYellowBalance :
`
<b>黄色</b>
<p>调整黄色油墨量。</p>
<p>如果增加值,整体会偏黄。</p>
<p>如果减少值,整体会偏蓝。</p>
`,
iBlackBalance :
`
<b>黑色</b>
<p>调整黑色油墨量。</p>
<p>如果增加值,整体会偏黑。</p>
<p>如果减少值,整体会偏白。</p>
`,
printTime :
`
<b>打印时间</b>
<p>“x1”以按照高光5打印的时间作为标准。</p>
<p>提高高光增加了白色油墨量时打印时间变长。</p>
`,
whiteInk :
`
<b>白色油墨量</b>
<p>高光5的油墨量为“400%",以此作为标准。</p>
<p>实际油墨消耗量取决于打印图像内的色彩。请将其当作油墨消耗量的标准。</p>
`,
byInkVolume :
`
<b>油墨量</b>
<p>减少彩色油墨量以防止油墨渗透。</p>
<p>根据InkVolume.arxp的打印结果选择适当的值。</p>
<p>0 不减少油墨量。用于棉100%、麻和棉50%6/聚酯纤维50%的混纺材料。</p>
<p>9-7 用于薄T恤、厚毛巾、斜纹棉布</p>
<p>6-4 用于薄平纹织物和棉/聚酯纤维/聚氨酯混纺。</p>
<p>3-1用于聚酯纤维针织物和没有吸水性合成纤维。</p>
`,
bUniPrint :
`
<b>单向打印</b>
<p>将打印方向固定为一个方向。</p>
<p>优先执行来自打印机操作面板的指定,仅在打印机的设置是[自动]时有效。</p>
`
}
\ No newline at end of file
<script>
import bus from '@/bus'
import PrintDialog from "./printDialog.vue";
import {ipcRenderer} from "electron";
export default {
components: {PrintDialog},
props: {
user: {
default: {
avatar: ''
},
type: Object
},
company: {
default: {},
type: Object
}
},
data() {
return {
selectGridIndex: 0,
actionIndex: -1,
isAutoFinish: false,
showPrintDialog: false,
productionNo: '',
setting: {},
detail: null,
selectBgColor: '#ececec',
predefineColors: [
'#ff4500',
'#ff8c00',
'#ffd700',
'#90ee90',
'#00ced1',
'#1e90ff',
'#c71585',
'rgba(255, 69, 0, 0.68)',
'rgb(255, 120, 0)',
'hsv(51, 100, 98)',
'hsva(120, 40, 94, 0.5)',
'hsl(181, 100%, 37%)',
'hsla(209, 100%, 56%, 0.73)',
'#c7158577'
],
grid: [
// {
// label: '7×8',
// value: 4,
// col: 7,
// row: 8
// },
// {
// label: '10×12',
// col: 10,
// value: 3,
// row: 12
// },
// {
// label: '14×16',
// col: 14,
// value: 2,
// row: 16
// },
{
label: '16×18',
value: 1,
col: 543,
row: 482
},
// {
// label: '16×21',
// value: 0,
// col: 16,
// row: 21
// }
]
}
},
computed: {
avatar() {
if (this.user && this.user.avatar) {
console.log(this.user, 9)
let host = this.company.domain
if (host.includes('console')) {
host = host.replace('.jomalls.com', '.joshine.cn')
}
let avatar = this.user.avatar.includes('http') ? this.user.avatar : `https://${host}/erpimg/${this.user.avatar}`
console.log(avatar)
return avatar
}
return ''
},
actionList() {
return this.$store.getters.getActionList
}
},
mounted() {
this.$nextTick(() => {
this.$refs.searchRef.focus()
})
},
created() {
this.setting = JSON.parse(JSON.stringify(this.$store.getters.systemSetting))
},
methods: {
dropdownCommand(v) {
switch (v) {
case 'logout':
this.$confirm('是否退出登录?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$dataStore.delete('user')
this.$router.push('/')
}).catch(() => {
});
break
case 'company':
this.$confirm('是否切换系统?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$dataStore.delete('company')
this.$dataStore.delete('user')
this.$router.push('/')
}).catch(() => {
});
}
},
async setData(data) {
if (!data) return this.$message.warning('请扫描生产单号')
await this.$api.post('/completeDelivery', {id: data.id}).then(() => {
})
this.$message.success('成功生产完成')
},
async sureData() {
if (!this.detail || Object.keys(this.detail).length <= 1) {
return this.$message.warning('请扫码生产单号')
}
this.$confirm(
`确定生产完成?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
await this.setData(this.detail)
})
},
async downloadImage() {
if (!this.detail || Object.keys(this.detail).length === 0) return this.$message.warning('请输入生产单号')
let params = {
productionNo: this.detail.podProductionNo
}
let res = await this.$api.post('/downloadByDesignId', params)
this.$message.success(res.msg)
},
async getDataInfo() {
if (this.detail && Object.keys(this.detail).length > 0 && this.isAutoFinish) {
await this.setData(this.detail)
}
if (this.productionNo === '') return this.$message.warning('请录入生产单号')
const findByPodProductionNo = await this.$api.post('/findByPodProductionNo', {
podProductionNo: this.productionNo,
status: 'IN_PRODUCTION'
})
this.detail = findByPodProductionNo.data
ipcRenderer.send('win-subScreen', findByPodProductionNo.data)
let res = await this.$api.post('/getPodProductionInfo', {productionNo: this.productionNo})
if (res.data.length === 0) return this.$message.warning('未找到素材图!')
for (let i = 0; i < res.data.length; i++) {
this.$api.post('/getPngImg', {fileName: res.data[i].fileName}, {
headers: {
'responseType': 'blob'
}
}).then(r => {
res.data[i].file = new File([r], res.data[i].fileName, {type: 'image/png'})
if (i === res.data.length - 1) {
bus.$emit('busEmit', {type: 'sendFile', value: res.data})
}
})
}
this.productionNo = ''
this.$refs.searchRef.focus()
},
changeActionIndex(t) {
let index = this.actionIndex
if (t === '+') {
if (index === this.$store.state.actionList.length - 1) return;
this.actionIndex = this.actionIndex + 1
} else {
if (index === 0) return
this.actionIndex = this.actionIndex - 1
}
console.log(this.actionIndex, 88)
bus.$emit('busEmit', {type: 'index', value: this.actionIndex})
this.$store.commit('changeActionIndex', this.actionIndex)
},
setDefaultSetting() {
this.$store.commit('setDefaultSetting')
this.setting = JSON.parse(JSON.stringify(this.$store.getters.systemSetting))
this.$message.success('重置应用程序设置成功')
},
settingChange(field, value) {
this.$store.commit('updateSystemSetting', {field, value});
},
setBg() {
bus.$emit('busEmit', {type: 'bg', value: this.selectBgColor})
},
async uploadImage(f) {
console.log(f, 6666)
let formData = new FormData()
formData.append('file', f)
let {data} = await this.$api.post('/uploadImage', formData, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
})
bus.$emit('busEmit', {type: 'uploadImage', value: data})
return false
},
command(i) {
this.selectGridIndex = i
this.$store.commit('updateSystemSetting', {field: 'gridValue', value: this.grid[i]})
bus.$emit('busEmit', {type: 'grid', value: this.grid[i]})
},
}
}
</script>
<style scoped>
.page-header {
background-color: #ececec;
display: flex;
justify-content: space-between;
height: 40px;
padding: 5px;
}
.left-btn {
display: flex;
}
</style>
<template>
<div>
<div class="page-header">
<div class="left-btn">
<!-- <el-tooltip content="文件">-->
<!-- <el-button style="margin-right: 8px" size="small">文件</el-button>-->
<!-- </el-tooltip>-->
<el-dropdown trigger="click" @command="command" :hide-on-click="false">
<el-button style="height: 100%;margin-right: 8px" size="small">{{ grid[selectGridIndex].label }}</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="i" v-for="(it,i) in grid" :key="i">
{{ it.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!-- <el-tooltip content="字体">-->
<!-- <el-button size="small">-->
<!-- <img style="width: 13px;height: 13px" src="@/static/icon/ziti.png" alt="">-->
<!-- </el-button>-->
<!-- </el-tooltip>-->
<el-tooltip content="图片">
<el-upload accept=".png" action="" :show-file-list="false" :before-upload="uploadImage">
<el-button style="height: 100%;margin:0 8px" size="small">
<img style="width: 14px;height: 14px" src="@/static/icon/tupian.png" alt="">
</el-button>
</el-upload>
</el-tooltip>
<!-- <el-tooltip content="撤销">-->
<!-- <el-button @click="changeActionIndex('+')"-->
<!-- :disabled="actionList.length===0 || actionIndex<-1 || actionIndex===actionList.length-1 " icon=""-->
<!-- size="small">-->
<!-- <img style="width: 14px;height: 14px" src="@/static/icon/chehuizuo.png" alt="">-->
<!-- </el-button>-->
<!-- </el-tooltip>-->
<!-- <el-tooltip content="恢复">-->
<!-- <el-button @click="changeActionIndex('-')"-->
<!-- :disabled="actionList.length===0 || actionIndex<=-1" icon=""-->
<!-- size="small">-->
<!-- <img style="width: 14px;height: 14px" src="@/static/icon/chehuiyou.png" alt="">-->
<!-- </el-button>-->
<!-- </el-tooltip>-->
<el-tooltip content="背景色">
<el-color-picker
v-model="selectBgColor"
@change="setBg"
color-format="hex"
:predefine="predefineColors">
</el-color-picker>
</el-tooltip>
<el-popover
width="320"
:offset="-120"
trigger="click"
placement="bottom"
>
<div class="setting-form">
<el-form :model="setting" label-position="left" size="small">
<el-form-item prop="setting" label="网格显示">
<el-select @change="(e)=>settingChange('gridShow',e)" clearable v-model="setting.gridShow">
<el-option :value="1" label="未缩放的网格"></el-option>
<el-option :value="0" label="无"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="gridSpacing" label="网格间隔">
<el-select @change="(e)=>settingChange('gridSpacing',e)" clearable v-model="setting.gridSpacing">
<el-option value="20mm" label="20mm"></el-option>
<el-option value="10mm" label="10mm"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="language" label="语言设置">
<el-select @change="(e)=>settingChange('language',e)" clearable v-model="setting.language">
<el-option value="cn" label="中文"></el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="自动打印模式">-->
<!-- <el-switch @change="(e)=>settingChange('autoPrint',e)" v-model="setting.autoPrint"></el-switch>-->
<!-- </el-form-item>-->
</el-form>
<!-- <div class="form-block">-->
<!-- <div @click="setDefaultSetting" class="block-item">重置应用程序设置</div>-->
<!-- </div>-->
<!-- <div class="form-block">-->
<!-- <div class="block-item">隐私政策</div>-->
<!-- <div class="block-item">版本信息</div>-->
<!-- </div>-->
</div>
<el-tooltip slot="reference" content="设置">
<el-button style="margin-right: 8px" size="small">
<img style="width: 15px;height: 15px" src="@/static/icon/shezhi.png" alt="">
</el-button>
</el-tooltip>
</el-popover>
<!-- <el-tooltip content="打印">-->
<!-- <el-button @click="showPrintDialog=true" size="small" type="primary">打印</el-button>-->
<!-- </el-tooltip>-->
<!-- <el-tooltip content="添加模板">-->
<!-- <el-button size="small">添加模板</el-button>-->
<!-- </el-tooltip>-->
</div>
<div class="center-input">
<el-input @keyup.enter.native="getDataInfo" style="width: 40%;" size="medium" placeholder="请输入生产单号"
ref="searchRef" v-model="productionNo"></el-input>
<el-button @click="getDataInfo" size="medium" style="margin-left: 10px" type="primary">查询</el-button>
<div class="sure-btn">
<el-button @click="sureData" size="medium" style="margin-left: 10px;width: 100%;" type="success">生产完成
</el-button>
<div class="check">
<el-checkbox v-model="isAutoFinish">自动完成上一单</el-checkbox>
</div>
</div>
<el-button @click="downloadImage" size="medium" style="margin-left: 15px" type="primary">下载素材
</el-button>
</div>
<div class="right-user">
<p>{{ company.companyName }}</p>
<el-avatar :src="avatar"></el-avatar>
<el-dropdown @command="dropdownCommand">
<b style="cursor:pointer;">{{ user.employeeName }}</b>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="company">切换系统</el-dropdown-item>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
<!-- <print-dialog :byPlatenSize="grid[selectGridIndex].value" @close="showPrintDialog=false"-->
<!-- :visible="showPrintDialog"></print-dialog>-->
</div>
</template>
<style scoped>
.right-user {
display: flex;
align-items: center;
}
.right-user b {
margin: 0 10px;
display: inline-block;
color: black;
font-size: 15px;
font-weight: bold;
}
.right-user p {
margin: 0 10px;
font-weight: bold;
display: inline-block;
font-size: 15px;
color: #e6a23c;
}
.setting-form .el-form {
padding: 10px;
box-sizing: border-box;
}
.form-block:nth-last-of-type(1) {
border-bottom: 1px solid #ececec;
}
.form-block {
padding: 15px 0;
border-top: 1px solid #ececec;
}
.block-item {
width: 100%;
text-align: left;
cursor: pointer;
font-size: 14px;
padding: 10px 10px;
}
.block-item:hover {
background-color: #ececec;
}
.center-input {
display: flex;
align-items: center;
justify-content: flex-start;
flex: 1;
flex-shrink: 0;
overflow: hidden;
padding-left: 100px;
}
.sure-btn {
width: 300px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.sure-btn ::v-deep .el-button span {
position: relative;
left: -40px;
}
.check {
position: absolute;
right: 10px;
width: fit-content;
line-height: 36px;
height: 100%;
z-index: 2;
color: white;
}
</style>
\ No newline at end of file
<script>
import fieldDescription from "./fieldDescription";
import {ipcRenderer} from "electron"
import axios from 'axios'
export default {
data() {
return {
dialogShow: false,
axios,
fieldDescription,
printerList: [],
description: '',
defaultList: [
{
label: 'default',
value: 'default'
}
],
printInkList: [
{
label: '仅彩色油墨',
},
{
label: '仅白色油墨'
},
{
label: '彩色 + 白色油墨'
},
{
label: '仅黑色油墨'
},
],
detailShow: false,
printSetting: {
printer: '',
byInk: 2,
byInkVolume: 1,
byDoublePrint: 3,
bEcoMode: false,
bMaterialBlack: false,
byHighlight: 1,
printNum: 1,
bFastMode: false,
bUniPrint: false,
bDivide: false,
bPause: false,
byMask: 3,
bTransColor: false,
byChoke: 2,
minWhiteInkType: 0,
bySaturation: 5,
byBrightness: 5,
byContrast: 5,
iCyanBalance: 0,
iMagentaBalance: 0,
iBlackBalance: 0,
iYellowBalance: 0,
byMinWhite: 1,
bMultiple: false,
default: 'default',
}
}
},
props: {
visible: {
default: false,
type: Boolean
},
byPlatenSize: {
default: 0,
type: Number
}
},
computed: {
inkNum() {
return 200 + (Number(this.printSetting.byHighlight) * 50) - 50
},
defaultTime() {
if ([6, 7].includes(Number(this.printSetting.byHighlight))) {
return '1.25'
} else if (this.printSetting.byHighlight < 6) {
return '1.00'
} else {
return '1.5'
}
},
imgList() {
return this.$store.state.imgList
}
},
watch: {
visible() {
this.dialogShow = this.visible
}
},
created() {
this.getPrinter()
},
methods: {
singleStr(str) {
// 不足四位前面用0补齐
str = Number(str).toString().replace('.', '')
if (str.length >= 4) {
str = str.slice(0, 5)
} else if (str.length === 3) {
str = '0' + str
} else if (str.length === 2) {
str = '00' + str
} else if (str.length === 1) {
str = '000' + str
}
return str
},
async getPrintCmd() {
if (this.imgList.length === 0) return this.$message.warning('素材不能为空')
console.log(this.imgList[0])
let {x, y, r, w, h} = this.imgList[0]
let f = JSON.parse(JSON.stringify(this.printSetting))
let x_mm = Number((x * 0.84).toFixed(1).replace('.00', '').split('.')[0])
let w_mm = Number((w * 0.84).toFixed(1).replace('.00', ''))
let h_mm = Number((h * 0.84).toFixed(1).replace('.00', ''))
let y_mm = Number((y * 0.84).toFixed(1).replace('.00', '').split('.')[0])
const imgFileName = this.imgList[0].fileName
let position = '' // 位置
let size = '' // 图像大小
if (Number(x_mm) < 0 || Number(y_mm) < 0) {
position = `"${x_mm},${y_mm}"`
} else {
position = `${this.singleStr(x_mm)}${this.singleStr(y_mm)}`
}
size = `${this.singleStr(w_mm)}${this.singleStr(h_mm)}`
const bigNum = '0031131' // 图像放大倍数 -R
const whitePrint = '0' // 白色打印
let cmd = `GTXproCMD.exe print -X "${`Profile\\${imgFileName.replace('.png', '')}.xml`}" -I "${'Input\\' + imgFileName}" -A "Output\\${imgFileName.replace('.png', '')}.arxp" -S ${size} -L ${position} -W ${whitePrint} -D ${r}`
let print_cmd = `GTXproCMD.exe send -A "Output\\${imgFileName.replace('.png', '')}.arxp" -P "${f.printer}" -D 1`
let data = {
...this.printSetting,
...{
byPlatenSize: this.byPlatenSize,
cmd,
fileName: imgFileName,
print_cmd,
}
}
let res = await this.$api.post('/toPrint', data)
this.$message.success(res.msg)
},
async getPrinter() {
ipcRenderer.send('allPrint')
ipcRenderer.once('printName', (event, data) => {
console.log(data, 996) // data就是返回的打印机数据列表
this.printerList = data
if (this.printerList.length > 0) {
this.printSetting.printer = this.printerList[0].name
}
})
},
mouseenter(e, f) {
console.log(e, f)
this.description = this.fieldDescription[f] || ''
},
mouseleave() {
this.description = ''
},
setDetailShow() {
this.detailShow = !this.detailShow
}
}
}
</script>
<template>
<div class="print-div">
<el-form label-width="170px" label-position="left" size="mini" :model="printSetting">
<el-form-item label-width="55px" label="打印机">
<div class="flex-row">
<el-select v-model="printSetting.printer" clearable>
<el-option v-for="(it,i) in printerList" :key="i" :label="it.name" :value="it.name"></el-option>
</el-select>
<el-button title="刷新" @click="getPrinter" style="margin-left: 10px" icon="el-icon-refresh"></el-button>
</div>
</el-form-item>
<!-- <el-form-item label-width="50px" label="预设">-->
<!-- <div class="flex-row">-->
<!-- <el-select v-model="printSetting.default" clearable>-->
<!-- <el-option v-for="(it,i) in defaultList" :key="i" :value="it.label" :label="it.label"></el-option>-->
<!-- </el-select>-->
<!-- <div style="margin-left: 15px" class="save-btn">-->
<!-- <el-button type="primary">保存预设</el-button>-->
<!-- <el-button type="danger">删除</el-button>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-form-item>-->
<div class="detail-setting">
<div class="setting-left">
<div class="left-block-item">
<b class="setting-title">基本设置</b>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byInk'] || ''"></div>
</template>
<el-form-item label="油墨选择">
<el-select v-model="printSetting.byInk" clearable>
<el-option :label="it.label" :value="i" :key="i" v-for="(it,i) in printInkList"></el-option>
</el-select>
</el-form-item>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bEcoMode'] || ''"></div>
</template>
<el-form-item v-if="printSetting.byInk===2" label="模式">
<el-select v-model="printSetting.bEcoMode" clearable>
<el-option label="通常" :value="false"></el-option>
<el-option label="白色油墨削减" :value="true"></el-option>
</el-select>
</el-form-item>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bMaterialBlack'] || ''"></div>
</template>
<div v-if="printSetting.byInk===2">
<el-form-item
label="利用底材的黑色">
<el-switch v-model="printSetting.bMaterialBlack"></el-switch>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bMultiple'] || ''"></div>
</template>
<div v-if="printSetting.byInk!==1"
>
<el-form-item
label="彩色的复合路径打印">
<el-switch v-model="printSetting.bMultiple"></el-switch>
</el-form-item>
</div>
</el-tooltip>
</div>
<div class="left-block-item" v-if="[0,3].includes(printSetting.byInk)">
<b class="setting-title">彩色油墨设置</b>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byInkVolume'] || ''"></div>
</template>
<div>
<el-form-item label="油墨量">
<div class="flex-row">
<el-slider :step="1" :min="1" :max="10" v-model="printSetting.byInkVolume"></el-slider>
<span>{{ printSetting.byInkVolume }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byDoublePrint'] || ''"></div>
</template>
<div>
<el-form-item label="2次打印">
<div class="flex-row">
<el-slider :step="1" :min="0" :max="3" v-model="printSetting.byDoublePrint"></el-slider>
<span>{{ printSetting.byDoublePrint }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
</div>
<div class="left-block-item" v-if="[1,2].includes(printSetting.byInk)">
<b class="setting-title">白色油墨设置</b>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byHighlight'] || ''"></div>
</template>
<div>
<el-form-item label="高光">
<div class="flex-row">
<el-slider :step="1" :min="1" :max="9" v-model="printSetting.byHighlight"></el-slider>
<span>{{ printSetting.byHighlight }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byMask'] || ''"></div>
</template>
<div>
<el-form-item label="遮光">
<div class="flex-row">
<el-slider :step="1" :min="1" :max="5" v-model="printSetting.byMask"></el-slider>
<span>{{ printSetting.byMask }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bFastMode'] || ''"></div>
</template>
<div v-if="[2].includes(printSetting.byInk)">
<el-form-item
label="白色高速打印">
<el-switch v-model="printSetting.bFastMode"></el-switch>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bDivide'] || ''"></div>
</template>
<div>
<el-form-item
label="白色分次打印">
<el-switch v-model="printSetting.bDivide"></el-switch>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bPause'] || ''"></div>
</template>
<div v-if="[2].includes(printSetting.byInk)"
>
<el-form-item
label="白色/彩色个别打印">
<el-switch v-model="printSetting.bPause"></el-switch>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bTransColor'] || ''"></div>
</template>
<div>
<el-form-item label="透明色">
<el-switch v-model="printSetting.bTransColor"></el-switch>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byChoke'] || ''"></div>
</template>
<div v-if="[2].includes(printSetting.byInk)"
>
<el-form-item
label="白色油墨的削减范围">
<div class="flex-row">
<el-slider :step="1" :min="0" :max="10" v-model="printSetting.byChoke"></el-slider>
<span>{{ printSetting.byChoke }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byMinWhite'] || ''"></div>
</template>
<div v-if="[2].includes(printSetting.byInk)"
>
<el-form-item
label="最小白色油墨量">
<div class="flex-column">
<el-select v-model="printSetting.minWhiteInkType">
<el-option label="通常" :value="0"></el-option>
<el-option label="特殊" :value="1"></el-option>
</el-select>
<div class="slider flex-row">
<el-slider :step="1" :min="1" :max="printSetting.minWhiteInkType===1?6:3"
v-model="printSetting.byMinWhite"></el-slider>
<span>{{ printSetting.byMinWhite }}</span>
</div>
</div>
</el-form-item>
</div>
</el-tooltip>
</div>
<div v-show="detailShow" class="left-block-item">
<b class="setting-title">画质设置</b>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bySaturation'] || ''"></div>
</template>
<div>
<el-form-item label="饱和度">
<div class="flex-row">
<el-slider :step="1" :min="0" :max="40" v-model="printSetting.bySaturation"></el-slider>
<span>{{ printSetting.bySaturation }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byBrightness'] || ''"></div>
</template>
<div>
<el-form-item label="亮度">
<div class="flex-row">
<el-slider :step="1" :min="0" :max="40" v-model="printSetting.byBrightness"></el-slider>
<span>{{ printSetting.byBrightness }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['byContrast'] || ''"></div>
</template>
<div>
<el-form-item label="对比度">
<div class="flex-row">
<el-slider :step="1" :min="0" :max="40" v-model="printSetting.byContrast"></el-slider>
<span>{{ printSetting.byContrast }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bUniPrint'] || ''"></div>
</template>
<div v-if="[1].includes(printSetting.byInk)"
>
<el-form-item
label="单向打印">
<el-switch v-model="printSetting.bUniPrint"></el-switch>
</el-form-item>
</div>
</el-tooltip>
</div>
<div v-if="detailShow && ![1].includes(printSetting.byInk)" class="left-block-item">
<b class="setting-title">彩色平衡</b>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['iCyanBalance'] || ''"></div>
</template>
<div v-if=" ![3].includes(printSetting.byInk)"
>
<el-form-item label="青色">
<div class="flex-row">
<el-slider :step="1" :min="-5" :max="5" v-model="printSetting.iCyanBalance"></el-slider>
<span>{{ printSetting.iCyanBalance }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['iMagentaBalance'] || ''"></div>
</template>
<div v-if=" ![3].includes(printSetting.byInk)"
>
<el-form-item label="红色">
<div class="flex-row">
<el-slider :step="1" :min="-5" :max="5" v-model="printSetting.iMagentaBalance"></el-slider>
<span>{{ printSetting.iMagentaBalance }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['iYellowBalance'] || ''"></div>
</template>
<div v-if=" ![3].includes(printSetting.byInk)"
>
<el-form-item label="黄色">
<div class="flex-row">
<el-slider :step="1" :min="-5" :max="5" v-model="printSetting.iYellowBalance"></el-slider>
<span>{{ printSetting.iYellowBalance }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['iBlackBalance'] || ''"></div>
</template>
<div>
<el-form-item label="黑色">
<div class="flex-row">
<el-slider :step="1" :min="-5" :max="5" v-model="printSetting.iBlackBalance"></el-slider>
<span>{{ printSetting.iBlackBalance }}</span>
</div>
</el-form-item>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['bUniPrint'] || ''"></div>
</template>
<div>
<el-form-item
label="单向打印">
<el-switch v-model="printSetting.bUniPrint"></el-switch>
</el-form-item>
</div>
</el-tooltip>
</div>
<el-button size="small" @click="setDetailShow">{{
detailShow ? '关闭详细设置' : '打开详细设置'
}}...
</el-button>
</div>
</div>
</el-form>
<div class="bottom-info flex-row">
<div class="info">
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['printTime'] || ''"></div>
</template>
<div class="info-item">
<span>打印时间</span>
<b>×{{ defaultTime }}</b>
</div>
</el-tooltip>
<el-tooltip placement="top-start">
<template slot="content">
<div v-html="fieldDescription['whiteInk'] || ''"></div>
</template>
<div class="info-item">
<span>白色油墨量</span>
<b>{{ inkNum }}%</b>
</div>
</el-tooltip>
</div>
<div class="btn">
<el-input-number v-model="printSetting.printNum" size="small"></el-input-number>
<span></span>
</div>
</div>
<el-button @click="getPrintCmd" type="primary" style="width: 100%;height: 50px">打印</el-button>
</div>
</template>
<style scoped>
.flex-row {
display: flex;
align-items: center;
}
.flex-row {
display: flex;
align-items: center;
}
.flex-row span {
margin-left: 10px;
}
.flex-row .el-slider {
flex: 1;
flex-shrink: 0;
margin: 0 10px;
}
.el-form {
height: 84%;
display: flex;
flex-direction: column;
padding-top: 15px;
border-top: 1px solid #ececec;
}
.detail-setting {
border-top: 2px solid transparent;
display: flex;
background-color: #ececec;
align-items: flex-start;
flex: 1;
flex-shrink: 0;
overflow: auto;
}
.left-block-item .setting-title {
font-weight: bold;
font-size: 20px;
color: #409EFF;
position: relative;
margin-bottom: 30px;
margin-top: 30px;
display: inline-block;
left: -15px;
}
.setting-right {
box-sizing: border-box;
padding: 20px;
width: 50%;
height: 100%;
color: #333;
}
.setting-left {
width: 100%;
box-sizing: border-box;
padding: 15px 30px;
background-color: white;
}
.fixed-top {
position: absolute;
word-break: break-all;
max-width: 44%;
}
.bottom-info {
padding: 15px 0;
margin-top: 20px;
font-size: 14px;
justify-content: space-between;
border-top: 1px solid #ececec;
}
.info {
width: 57%;
display: flex;
justify-content: space-between;
}
.info-item span {
margin-right: 5px;
}
.print-div {
background: #ececec;
border-left: 1px solid gray;
padding: 10px;
position: fixed;
box-sizing: border-box;
right: 0;
z-index: 4;
bottom: 0;
height: calc(100% - 51px);
width: 430px;
}
</style>
\ No newline at end of file
<script>
import PHead from './head/index.vue'
import PMain from './main/index.vue'
export default {
components: {PHead, PMain},
data() {
return {
user: {},
company:{}
}
},
mounted() {
this.user = this.$dataStore.get('user')
this.company = this.$dataStore.get('company')
console.log(this.$dataStore)
}
}
</script>
<template>
<div class="page">
<p-head :company="company" :user="user" />
<p-main/>
</div>
</template>
<style scoped>
.page {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
</style>
\ No newline at end of file
<script>
export default {
data() {
return {
drawerShow: false,
form: {
x: 0,
sx: 0,
sy: 0,
y: 0,
w: 0,
h: 0,
r: 0,
}
}
},
props: {
visible: {
type: Boolean,
default: false
},
item: {
type: Object,
default: () => {
}
},
},
watch: {
visible: {
handler() {
this.drawerShow = this.visible
},
immediate: true
},
item: {
handler() {
if (this.item) {
let x_mm = Number((this.item.x * 0.84).toFixed(2).replace('.00', ''))
let w_mm = Number((this.item.w * 0.84).toFixed(2).replace('.00', ''))
let h_mm = Number((this.item.h * 0.84).toFixed(2).replace('.00', ''))
let y_mm = Number((this.item.y * 0.84).toFixed(2).replace('.00', ''))
// return
this.form.x = x_mm
this.form.y = y_mm
this.form.h = h_mm
this.form.w = w_mm
this.form.r = this.item.r
}
},
immediate: true,
deep: true,
}
},
methods: {
conversion_getDPI() {
var arrDPI = new Array;
if (window.screen.deviceXDPI) {
arrDPI[0] = window.screen.deviceXDPI;
arrDPI[1] = window.screen.deviceYDPI;
} else {
var tmpNode = document.createElement("DIV");
tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden";
document.body.appendChild(tmpNode);
arrDPI[0] = parseInt(tmpNode.offsetWidth);
arrDPI[1] = parseInt(tmpNode.offsetHeight);
tmpNode.parentNode.removeChild(tmpNode);
}
return arrDPI;
},
formChange() {
this.$emit('change', this.form)
},
addValue(f) {
let nw = Number(this.form[f]) + 1
this.$set(this.form, f, nw)
this.formChange()
},
reduceValue(f) {
let nw = Number(this.form[f]) - 1
this.$set(this.form, f, nw)
this.formChange()
},
}
}
</script>
<template>
<div class="drawer" :style="{animation:visible?`ltr-drawer-in .3s 1ms`:`ltr-drawer-out .3s 1ms`}" v-show="visible">
<div class="title">
图片编辑
</div>
<div class="img-form">
<el-form size="small" style="" :model="form" label-position="left" label-width="30px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="x:" prop="sx">
<div class="form-content">
<el-input @blur="formChange" v-model="form.x"></el-input>
<div class="set-value">
<i @click="addValue('x')" class="el-icon-arrow-up"></i>
<i @click="reduceValue('x')" class="el-icon-arrow-down"></i>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="y:">
<div class="form-content">
<el-input @blur="formChange" v-model="form.y"></el-input>
<div class="set-value">
<i @click="addValue('y')" class="el-icon-arrow-up"></i>
<i @click="reduceValue('y')" class="el-icon-arrow-down"></i>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="w:">
<div class="form-content">
<el-input @blur="formChange" v-model="form.w"></el-input>
<div class="set-value">
<i @click="addValue('w')" class="el-icon-arrow-up"></i>
<i @click="reduceValue('w')" class="el-icon-arrow-down"></i>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="h:">
<div class="form-content">
<el-input @blur="formChange" v-model="form.h"></el-input>
<div class="set-value">
<i @click="addValue('h')" class="el-icon-arrow-up"></i>
<i @click="reduceValue('h')" class="el-icon-arrow-down"></i>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="r:">
<div class="form-content">
<el-input @blur="formChange" v-model="form.r">
<template slot="append">reg</template>
</el-input>
<div class="set-value">
<i @click="addValue('r')" class="el-icon-arrow-up"></i>
<i @click="reduceValue('r')" class="el-icon-arrow-down"></i>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-tooltip content="居中">
<el-button @click="$emit('ev','center')" size="mini">
<img style="width: 13px;height: 13px" src="@/static/icon/juzhong-01.png" alt="">
</el-button>
</el-tooltip>
<el-tooltip content="垂直居中">
<el-button @click="$emit('ev','y_center')" size="mini">
<img style="width: 13px;height: 13px" src="@/static/icon/vertical.png" alt="">
</el-button>
</el-tooltip>
<el-tooltip content="水平居中">
<el-button @click="$emit('ev','x_center')" size="mini">
<img style="width: 13px;height: 13px" src="@/static/icon/shuipingjuzhong.png" alt="">
</el-button>
</el-tooltip>
<el-tooltip content="上移一层">
<el-button @click="$emit('ev','add_index')" size="mini">
<img style="width: 13px;height: 13px" src="@/static/icon/shangyi.png" alt="">
</el-button>
</el-tooltip>
<el-tooltip content="下移一层">
<el-button @click="$emit('ev','reduce_index')" size="mini">
<img style="width: 13px;height: 13px" src="@/static/icon/xiayi.png" alt="">
</el-button>
</el-tooltip>
</el-col>
</el-row>
</el-form>
</div>
</div>
</template>
<style scoped>
.img-form {
padding: 10px;
}
::v-deep .el-drawer__header {
border-bottom: 1px solid #ececec;
padding-bottom: 10px;
margin-bottom: 20px;
}
.form-content {
display: flex;
align-items: center;
}
.set-value {
height: 100%;
margin-left: 6px;
}
.set-value i {
width: 20px;
height: 12px;
display: block;
text-align: center;
line-height: 12px;
cursor: pointer;
border: 1px solid transparent;
}
.set-value i:hover {
border-color: #ececec;
}
.drawer .title {
padding-bottom: 10px;
border-bottom: 1px solid #ececec;
}
@keyframes ltr-drawer-in {
0% {
transform: translate(-100%);
}
100% {
transform: translate(0);
}
}
@keyframes ltr-drawer-in {
0% {
transform: translate(-100%);
}
100% {
transform: translate(0);
}
}
@keyframes ltr-drawer-out {
0% {
transform: translate(0);
}
100% {
transform: translate(-100%);
}
}
.drawer {
width: 300px;
bottom: 0;
top: 50px;
padding: 10px;
z-index: 99;
background: white;
box-shadow: 0 8px 10px -5px rgba(0, 0, 0, .2), 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12);
left: 0;
position: fixed;
border-right: 1px solid #ececec;
height: calc(100vh - 50px);
//animation: ltr-drawer-in .3s 1ms;
}
</style>
\ No newline at end of file
<script>
import VueDragResizeRotate from "@gausszhou/vue-drag-resize-rotate";
import ImgSetting from './imgSetting.vue'
import bus from '@/bus'
import PrintDialog from "@/views/design/head/printDialog.vue";
const img = require('../../../assets/bg_tshirt_shadow.png')
export default {
components: {
PrintDialog,
VueDragResizeRotate,
ImgSetting,
},
computed: {
sysSetting() {
return this.$store.getters.systemSetting
},
gridSpacing() {
return this.$store.getters.systemSetting.gridSpacing.slice(0, 2) + 'px'
},
computedGridSize() {
const getter = this.$store.getters.systemSetting
const num = getter.gridSpacing.slice(0, 2)
return 10 + 'px'
},
returnItem() {
return this.imgList[this.selectIndex]
}
},
data() {
return {
actionList: [],
parentWidth: 0,
imgHeight: 0,
selectImgList: [],
selectImgIndex: 0,
bus: 0,
showImgSetting: false,
parentHeight: 0,
width: 0,
selectIndex: 0,
height: 0,
x: 0,
y: 0,
imgList: [],
row: 8,
img,
col: 7
}
},
watch: {
imgList: {
handler() {
this.$store.commit('changeImgList', this.imgList)
},
deep: true,
immediate: true
},
selectIndex() {
if (this.selectIndex >= 0) {
if (!this.showImgSetting) {
this.showImgSetting = true
}
} else {
this.showImgSetting = false
}
}
},
methods: {
indexChange(v) {
console.log(v)
if (this.imgList.length === 0) return;
let index = !this.imgList[this.selectIndex] ? 0 : this.selectIndex
let item = this.$store.state.actionList[v]
if (!item) return;
if (item.t === 'drag') {
this.$set(this.imgList[index], 'x', item.x)
this.$set(this.imgList[index], 'y', item.y)
} else if (item.t === 'resize') {
this.$set(this.imgList[index], 'x', item.x)
this.$set(this.imgList[index], 'y', item.y)
this.$set(this.imgList[index], 'w', item.w)
this.$set(this.imgList[index], 'h', item.h)
}
console.log()
},
getOneMmsPx() {
// 创建一个1mm宽的元素插入到页面,然后坐等出结果
let div = document.createElement("div");
div.id = "mm";
div.style.width = "1mm";
document.querySelector("body").appendChild(div);
// 原生方法获取浏览器对元素的计算值
let mm1 = document.getElementById("mm").getBoundingClientRect();
return mm1.width;
},
selectImg(it, i) {
this.selectImgIndex = i
this.imgList = []
this.getBackFile([it])
},
ev(type) {
if (!this.imgList[this.selectIndex]) return
let w = document.getElementById('line').clientWidth / 2
let h = document.getElementById('line').clientHeight / 2
switch (type) {
case 'center':
this.$set(this.imgList[this.selectIndex], 'x', Number(w) - (Number(this.imgList[this.selectIndex].w) / 2))
this.$set(this.imgList[this.selectIndex], 'y', Number(h) - (Number(this.imgList[this.selectIndex].h) / 2))
break;
case 'x_center':
this.$set(this.imgList[this.selectIndex], 'x', Number(w) - (Number(this.imgList[this.selectIndex].w) / 2))
break;
case 'y_center':
this.$set(this.imgList[this.selectIndex], 'y', Number(h) - (Number(this.imgList[this.selectIndex].h) / 2))
break;
case 'add_index':
this.$set(this.imgList[this.selectIndex], 'zIndex', this.imgList[this.selectIndex].zIndex + 1)
break;
case 'reduce_index':
this.$set(this.imgList[this.selectIndex], 'zIndex', this.imgList[this.selectIndex].zIndex - 1)
break;
default:
break
}
},
formChange(form) {
if (!this.imgList[this.selectIndex]) return
let f = JSON.parse(JSON.stringify(form))
let x_mm = f.x / 0.84
let w_mm = f.w / 0.84
let h_mm = f.h / 0.84
let y_mm = f.y / 0.84
this.$set(this.imgList[this.selectIndex], 'x', x_mm)
this.$set(this.imgList[this.selectIndex], 'y', y_mm)
this.$set(this.imgList[this.selectIndex], 'w', w_mm)
this.$set(this.imgList[this.selectIndex], 'h', h_mm)
this.$set(this.imgList[this.selectIndex], 'r', f.r)
},
close() {
this.showImgSetting = false
},
onDrag(left, top) {
if (!this.imgList[this.selectIndex]) return
this.$set(this.imgList[this.selectIndex], 'x', left)
this.$set(this.imgList[this.selectIndex], 'y', top)
},
dragStop(left, top) {
if (!this.imgList[this.selectIndex]) return
this.actionList.unshift({
t: 'drag',
x: left,
y: top
})
this.$store.commit('changeActionList', this.actionList)
},
resizeStop(x, y, w, h) {
if (!this.imgList[this.selectIndex]) return
this.actionList.unshift({
t: 'resize',
x,
y,
w,
h
})
this.$store.commit('changeActionList', this.actionList)
},
resizing(x, y, w, h) {
if (!this.imgList[this.selectIndex]) return
this.$set(this.imgList[this.selectIndex], 'x', x)
this.$set(this.imgList[this.selectIndex], 'y', y)
this.$set(this.imgList[this.selectIndex], 'w', w)
this.$set(this.imgList[this.selectIndex], 'h', h)
},
rotating(angle) {
if (!this.imgList[this.selectIndex]) return
this.$set(this.imgList[this.selectIndex], 'r', angle)
},
outsideClick(e) {
if (!e.target.className.includes('sucaitu-img')) {
this.selectIndex = -1
this.close()
}
},
selectItem(index) {
this.selectIndex = index
},
delImg(index) {
this.imgList.splice(index, 1)
},
fileToBase64(file) {
return new Promise((resolve, reject) => {
// 创建一个新的 FileReader 对象
const reader = new FileReader();
// 读取 File 对象
reader.readAsDataURL(file);
// 加载完成后
reader.onload = function () {
// 将读取的数据转换为 base64 编码的字符串
const base64String = reader.result;
// 解析为 Promise 对象,并返回 base64 编码的字符串
resolve(base64String);
};
// 加载失败时
reader.onerror = function () {
reject(new Error("Failed to load file"));
};
});
},
getBackFile(files) {
console.log(files)
let that = this
for (let i = 0; i < files.length; i++) {
files[i].url = files[i].productionFile
that.$nextTick(() => {
that.getImageSize(files[i].url).then(data => {
let w = document.getElementById('line').clientWidth / 2
let rate = data.height / data.width
that.imgList.push({
url: files[i].url,
fileName: files[i].fileName,
w: 100,
zIndex: that.imgList.length,
x: w - 75,
y: 0,
h: 100 * rate,
r: 0,
});
that.selectIndex = that.imgList.length - 1
that.showImgSetting = true
})
})
}
},
addFile(file) {
let that = this
that.$nextTick(() => {
that.getImageSize(file.url).then(data => {
let w = document.getElementById('line').clientWidth / 2
let rate = data.height / data.width
that.imgList.push({
url: file.url,
w: 100,
fileName: file.fileName,
zIndex: that.imgList.length,
x: w - 75,
y: 0,
h: 100 * rate,
r: 0,
});
that.selectIndex = that.imgList.length - 1
that.showImgSetting = true
})
})
},
listenUpload() {
let that = this
that.parentWidth = document.getElementById('img').clientWidth
that.parentHeight = document.getElementById('img').clientHeight
let dp = document.getElementsByClassName('container')[0]
dp.addEventListener('dragover', function (e) {
e.stopPropagation();
//阻止浏览器默认打开文件的操作
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
dp.addEventListener("drop", async function (e) {
e.stopPropagation();
//阻止浏览器默认打开文件的操作
e.preventDefault();
const files = e.dataTransfer.files;
let formData = new FormData()
console.log(files)
formData.append('file', files[0])
let {data} = await that.$api.post('/uploadImage', formData, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
})
that.imgList = []
that.selectImgList = []
that.selectImgIndex = 0
that.addFile(data)
});
},
getImageSize(url) {
return new Promise(function (resolve, reject) {
let image = new Image();
image.onload = function () {
resolve({
width: image.width,
height: image.height
});
};
image.onerror = function () {
reject(new Error('error'));
};
image.src = url;
});
},
/**
* 根据主题颜色修改图片颜色
* @param {[type]} imgUrl 图片url
* @param {[type]} color 主题颜色
* @param {Function} callback 返回值 返回base64
*/
changeImageColor(color = "#00ff7f", callback) {
let threshold = 114; //默认颜色阀值 为 114 ->和默认图相关
let img = new Image()
img.src = require('../../../assets/bg_tshirt_shadow.png');
let newR = parseInt("0x" + color.substr(1, 2));
let newG = parseInt("0x" + color.substr(3, 2));
let newB = parseInt("0x" + color.substr(5, 2));
//图片加载后进行处理
img.onload = function () {
let width = img.width, height = img.height, canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
// 将源图片复制到画布上
ctx.drawImage(img, 0, 0, width, height);
// 获取画布的像素信息
let imageData = ctx.getImageData(0, 0, width, height), data = imageData.data;
// 对像素集合中的单个像素进行循环,每个像素是由4个通道组成,所以要注意
let i = 0;
while (i < data.length) {
let r = data[i++],
g = data[i++],
b = data[i++],
a = data[i++];
//计算透明度
let alp = (255 - r) / (255 - threshold);
//判断是否透明
let isTransparent = r == 255 && g == 255 && b == 255 && a == 255;
if (isTransparent) {
data[i - 1] = 0;
} else {
data[i - 4] = newR;
data[i - 3] = newG;
data[i - 2] = newB;
data[i - 1] = a !== 255 ? 255 - a : alp * 255; //处理透明的图片和不透明的图片
}
}
// 将修改后的代码复制回画布中
ctx.putImageData(imageData, 0, 0);
// 图片导出为 png 格式
let imgType = "png";
let imgData = canvas.toDataURL(imgType);
callback && callback(imgData);
};
return img;
}
},
mounted() {
this.imgHeight = window.screen.height + 'px'
bus.$on('busEmit', (v) => {
let {type, value} = v
switch (type) {
case 'grid':
this.row = value.row
this.col = value.col
break;
case 'index':
this.indexChange(value)
break;
case 'bg':
this.changeImageColor(value, (res) => {
this.img = res
})
break;
case 'uploadImage':
this.imgList = []
this.selectImgList = []
this.selectImgIndex = 0
this.addFile(value)
break;
case 'sendFile':
this.imgList = []
this.selectIndex = -1
if (value.length > 0) {
console.log(value)
this.selectImgList = value
this.selectImgIndex = 0
this.getBackFile([value[0]])
// this.addFile([value[0].file])
}
break;
default:
break;
}
})
document.addEventListener('keyup', (e) => {
if (e.keyCode === 8) {
if (this.selectIndex >= 0) {
this.imgList.splice(this.selectIndex, 1)
}
}
})
this.listenUpload()
document.getElementById('img').onmousedown = function (e) {
e.preventDefault()
}
}
}
</script>
<template>
<div class="page-main">
<div @click="outsideClick" class="main-bg">
<div class="container">
<div class="select-img" v-if="selectImgList.length>1">
<div @click="selectImg(it,i)" :class="{'select-img-index':selectImgIndex===i}" v-for="(it,i) in selectImgList"
:key="i"
class="img-item">
<el-image style="width: 100px;height: auto;" :src="it.productionFile"
></el-image>
<span>{{ it.designId }}</span>
</div>
</div>
<div :style="{height:imgHeight}" id="img">
<div id="line" v-if="sysSetting.gridShow">
<div class="sucaitu">
<vue-drag-resize-rotate
:w="item.w"
class-name="my-drag-resize-rotate"
:prevent-deactivation="true"
:h="item.h"
:parent="false"
:x="item.x"
:z="item.zIndex"
:lock-aspect-ratio="true"
:active="index===selectIndex"
:y="item.y"
v-for="(item,index) in imgList"
:key="index"
:draggable="true"
:resizable="true"
:rotatable="true"
@dragging="onDrag"
@dragstop="dragStop"
@resizing="resizing"
@resizestop="resizeStop"
@rotating="rotating"
:r="item.r"
>
<div ref="sucaitu-img" @click="selectItem(index)"
:class="{'active': index === selectIndex}"
class="sucaitu-img img element">
<img :src="item.url" alt="" class="sucaitu-img">
<i @click="delImg(index)" v-if="index===selectIndex" class=" close el-icon-close"></i>
<div v-if="index===selectIndex" class="control-point control-rotator"></div>
</div>
</vue-drag-resize-rotate>
</div>
<div v-if="$store.getters.systemSetting.gridShow" style="width:482px;height:542px" class="grid">
<div :style="{height:gridSpacing,lineHeight:gridSpacing}" class="grid-row" v-for="it in 100" :key="it">
<div :style="{width:gridSpacing,height:gridSpacing}"
class="grid-col" v-for="it in 100" :key="it">
</div>
</div>
</div>
<!-- <table v-if="$store.getters.systemSetting.gridShow" border="none">-->
<!-- <tr :style="{height:computedGridSize}" style="width: 482px;overflow: hidden;"-->
<!-- v-for="it in Number((543/Number($store.getters.systemSetting.gridSpacing.slice(0,2))).toFixed(0))">-->
<!-- <td v-for="it in Number((482/Number($store.getters.systemSetting.gridSpacing.slice(0,2))).toFixed(0))"-->
<!-- :style="{width:computedGridSize ,height:computedGridSize}">-->
<!-- </td>-->
<!-- </tr>-->
<!-- </table>-->
</div>
<img class="template-img" draggable="false" :src="img" alt="">
</div>
<print-dialog :byPlatenSize="$store.getters.systemSetting.gridValue"/>
</div>
</div>
<img-setting @ev="ev" @change="formChange" :item="returnItem" @close="close"
:visible="showImgSetting"></img-setting>
</div>
</template>
<style scoped>
.page-main {
flex: 1;
flex-shrink: 0;
overflow: hidden;
box-sizing: border-box;
}
.main-bg {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#line {
position: absolute;
z-index: 2;
}
.grid {
border: 1px solid gray;
overflow: hidden;
}
.grid-row {
width: 100%;
overflow: hidden;
height: 10px;
line-height: 10px;
white-space: nowrap;
}
.grid-row:nth-of-type(1) .grid-col {
border-top: none !important;
}
.grid-row:nth-last-of-type(1) .grid-col {
border-bottom: none !important;;
}
.grid-row .grid-col:nth-of-type(1) {
border-left: none !important;
}
.grid .grid-row .grid-col {
display: inline-block;
border: 1px solid gray;
border-right: none;
}
#img {
position: absolute;
z-index: 1;
left: 130px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
width: auto;
}
.container {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
position: relative;
}
.sucaitu {
position: absolute;
z-index: 3;
width: 100%;
//display: flex;
//justify-content: center;
//align-items: center;
height: 100%;
}
.sucaitu img {
width: 100%;
height: 100%;
}
.select-img {
border-color: #409eff;
}
.sucaitu .close {
position: absolute;
top: -11px;
right: -12px;
color: white;
width: 20px;
cursor: pointer;
height: 20px;
background: red;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.sucaitu .img {
height: fit-content;
width: 100%;
height: 100%;
cursor: move;
border: 1px dashed transparent;
}
#img img {
pointer-events: none;
width: auto;
height: 100%;
}
img {
-webkit-user-drag: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
img {
-webkit-user-drag: none;
-moz-user-drag: none;
-ms-user-drag: none;
user-drag: none;
}
::v-deep .active, .active {
border-color: #66b1ff !important;
}
.select-img-index {
box-sizing: border-box;
border-color: #409EFF !important;
}
.img-item span {
margin: 5px 0;
color: white;
}
.img-item {
padding: 5px;
display: flex;
cursor: pointer;
margin-bottom: 20px;
border: 2px dashed transparent;
flex-direction: column;
align-items: center;
justify-content: center;
}
.select-img {
width: 120px;
z-index: 5;
overflow: auto;
background-color: #333;
height: calc(100% - 72px);
padding: 10px;
position: fixed;
box-sizing: border-box;
left: 0;
bottom: 0;
border-right: 1px solid #ececec;
}
</style>
<style>
.el-upload {
height: 100%;
}
.my-drag-resize-rotate {
border-color: transparent;
}
.handle-rot::before, .handle-rot::after {
display: none;
}
</style>
\ No newline at end of file
<script>
export default {
data() {
return {
rules: {
company: [{validator: this.validatePass, required: true, trigger: 'blur'}],
loginName: [{message: '请输入用户名', required: true, trigger: 'blur'}],
password: [{message: '请输入密码', required: true, trigger: 'blur'}],
},
companyList: [],
company: null,
authorityVisible: false,
authorityForm: {},
form: {
loginName: '',
company: '',
password: ''
}
}
},
created() {
let user = this.$dataStore.get('user')
this.company = this.$dataStore.get('company')
if (this.company) {
this.form.company = this.company.domain
}
if (user) {
return this.$router.push('/design')
}
this.getCompanyList()
},
mounted() {
document.addEventListener('keyup', this.keyUp)
},
beforeDestroy() {
document.removeEventListener('keyup', this.keyUp)
},
methods: {
keyUp(e) {
console.log(e)
if (e.keyCode === 13) {
this.login()
}
},
submitAuthority() {
this.$refs.authorityForm.validate((v) => {
if (v) {
this.$api.post('/commitApply', {
...this.authorityForm,
...this.form
}, {
headers: {
'company': this.form.company + '.jomalls.com'
}
}).then((res) => {
if (res.code === 200) {
this.authorityVisible = false
this.$message.success('申请提交成功')
this.$dataStore.set('deviceId' + this.form.loginName, res.data.id)
}
})
}
})
},
validatePass(rule, value, callback) {
if (!value || value === '') {
return callback(new Error('请输入客户(例如demo)'));
}
let item = this.companyList.find(item => item.domain.replace('.jomalls.com', '') === value)
if (item) {
return callback()
} else {
return callback(new Error('未查找到该客户'))
}
},
async login() {
this.$refs.formRef.validate(async valid => {
if (valid) {
let f = JSON.parse(JSON.stringify(this.form))
f.company = f.company.includes('.jomalls.com') ? f.company : f.company + '.jomalls.com'
f.deviceId = this.$dataStore.get('deviceId' + this.form.loginName)
if (!f.deviceId) delete f.deviceId
let {data, code, message} = await this.$api.post('/login', f)
if (code === 411) {
this.authorityVisible = true
this.authorityForm = {}
return this.$message.error(message)
}
data = {
...data.sysUser,
...{
token: data.token
}
}
delete data.sysMenus
delete data.sysUser
let item = this.companyList.find(item => item.domain === f.company)
console.log(item, 111222)
this.$dataStore.set('user', data)
this.$dataStore.set('company', item)
this.$message.success('登录成功')
await this.$router.push('/design')
}
})
},
async getCompanyList() {
let {data} = await this.$api.get('/getCompanyList')
this.companyList = data.records
}
}
}
</script>
<template>
<div class="login">
<div class="form-content">
<el-form ref="formRef" :rules="rules" size="small" :model="form">
<el-form-item v-if="!company" prop="company">
<el-input placeholder="客户(例如demo)" prefix-icon="el-icon-monitor" v-model="form.company"
clearable></el-input>
<!-- <el-select filterable style="width: 100%;" placeholder="系统" v-model="form.company"-->
<!-- clearable>-->
<!-- <template slot="prefix">-->
<!-- <i class="el-icon-monitor"></i>-->
<!-- </template>-->
<!-- <el-option v-for="(it,i) in companyList" :key="i" :label="it.companyName" :value="it.domain"></el-option>-->
<!-- </el-select>-->
</el-form-item>
<el-form-item prop="loginName">
<el-input placeholder="用户名" prefix-icon="el-icon-user" v-model="form.loginName" clearable></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input placeholder="密码" show-password prefix-icon="el-icon-lock" type="password" v-model="form.password"
clearable></el-input>
</el-form-item>
<el-form-item>
<div style="display: flex;justify-content: center">
<el-button @keyup.enter.native="login" @click="login" type="primary" style="width: 60%;">登录</el-button>
</div>
</el-form-item>
</el-form>
</div>
<el-dialog
:close-on-click-modal="false"
title="权限申请"
:visible.sync="authorityVisible"
top="15%"
width="400px">
<el-form
:model="authorityForm"
size="mini"
ref="authorityForm"
label-width="80px">
<el-form-item label="设备名称" prop="deviceName" required>
<el-input v-model="authorityForm.deviceName"></el-input>
</el-form-item>
<el-form-item label="授权类型" prop="authType" required>
<el-radio v-model="authorityForm.authType" label="1">
一次授权
</el-radio>
<el-radio v-model="authorityForm.authType" label="2">
永久授权
</el-radio>
</el-form-item>
</el-form>
<span slot="footer">
<el-button size="mini" @click="authorityVisible = false">
取 消
</el-button>
<el-button size="mini" type="primary" @click="submitAuthority">
提交
</el-button>
</span>
</el-dialog>
</div>
</template>
<style scoped>
.login {
width: 100%;
height: 100%;
background-image: url("../../assets/logo-bg.jpg");
background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: center;
}
.form-content {
width: 300px;
padding-top: 50px;
}
.el-icon-monitor {
position: relative;
left: 6px;
}
</style>
\ No newline at end of file
const path = require("path");
module.exports = {
// 基本路径
publicPath: process.env.NODE_ENV === "production" ? "" : "/",
// 输出文件目录
outputDir: process.env.NODE_ENV === "production" ? "dist" : "devdist",
// eslint-loader 是否在保存的时候检查
lintOnSave: false,
/** vue3.0内置了webpack所有东西,
* webpack配置,see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
**/
chainWebpack: config => {
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]",
include: ["./src/icons"]
});
config.module
.rule("pug")
.test(/\.pug$/)
.use("pug-html-loader")
.loader("pug-html-loader")
.end();
},
configureWebpack: config => {
config.resolve = {
// 配置解析别名
extensions: [".js", ".json", ".vue"], // 自动添加文件名后缀
alias: {
vue: "vue/dist/vue.js",
"@": path.resolve(__dirname, "./src"),
"@c": path.resolve(__dirname, "./src/components")
}
};
},
// 生产环境是否生成 sourceMap 文件
productionSourceMap: false,
// css相关配置
css: {
// 是否使用css分离插件 ExtractTextPlugin
extract: true,
// 开启 CSS source maps?
sourceMap: false,
// css预设器配置项
loaderOptions: {
sass: {
prependData: `@import "./src/styles/main.css";`
}
},
// 启用 CSS modules for all css / pre-processor files.
requireModuleExtension: true // 是否开启支持‘foo.module.css’样式
},
// use thread-loader for babel & TS in production build
// enabled by default if the machine has more than 1 cores
parallel: require("os").cpus().length > 1,
/**
* PWA 插件相关配置,see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
*/
pwa: {},
// webpack-dev-server 相关配置
devServer: {
open: false, // 编译完成是否打开网页
host: "0.0.0.0", // 指定使用地址,默认localhost,0.0.0.0代表可以被外界访问
port: 8090, // 访问端口
https: false, // 编译失败时刷新页面
hot: true, // 开启热加载
hotOnly: false,
proxy: {
// 配置跨域
"/devApi": {
//要访问的跨域的api的域名
target: "http://localhost:3000",
ws: true,
changOrigin: true,
pathRewrite: {
"^/devApi": ""
}
}
},
overlay: {
// 全屏模式下是否显示脚本错误
warnings: true,
errors: true
},
before: app => {}
},
/**
* 第三方插件配置
*/
pluginOptions: {
electronBuilder:{
builderOptions: {
"extraResources": [
{
"from": "./print/",
"to": "../print"
}
]
}
}
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment