chore: baseline before dashboard update
3
.env.development
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ENV = 'development'
|
||||||
|
VITE_BASE_URL = 'http://localhost:10081'
|
||||||
|
VITE_CONFIG_URL = './public/config.js'
|
||||||
3
.env.production
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ENV = 'production'
|
||||||
|
VITE_BASE_URL = 'http://127.0.0.1:10081'
|
||||||
|
VITE_CONFIG_URL = './config.js'
|
||||||
70
.eslintrc.cjs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
const { defineConfig } = require('eslint-define-config')
|
||||||
|
module.exports = defineConfig({
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es6: true
|
||||||
|
},
|
||||||
|
parser: 'vue-eslint-parser',
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
sourceType: 'module',
|
||||||
|
jsxPragma: 'React',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'prettier',
|
||||||
|
'plugin:prettier/recommended'
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'vue/no-setup-props-destructure': 'off',
|
||||||
|
'vue/script-setup-uses-vars': 'error',
|
||||||
|
'vue/no-reserved-component-names': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'vue/custom-event-name-casing': 'off',
|
||||||
|
'no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/ban-types': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'space-before-function-paren': 'off',
|
||||||
|
|
||||||
|
'vue/attributes-order': 'off',
|
||||||
|
'vue/one-component-per-file': 'off',
|
||||||
|
'vue/html-closing-bracket-newline': 'off',
|
||||||
|
'vue/max-attributes-per-line': 'off',
|
||||||
|
'vue/multiline-html-element-content-newline': 'off',
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
|
'vue/attribute-hyphenation': 'off',
|
||||||
|
'vue/require-default-prop': 'off',
|
||||||
|
'vue/require-explicit-emits': 'off',
|
||||||
|
'vue/html-self-closing': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
html: {
|
||||||
|
void: 'always',
|
||||||
|
normal: 'never',
|
||||||
|
component: 'always'
|
||||||
|
},
|
||||||
|
svg: 'always',
|
||||||
|
math: 'always'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/no-v-html': 'off',
|
||||||
|
'vue/require-toggle-inside-transition': 'off'
|
||||||
|
}
|
||||||
|
})
|
||||||
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
BIN
doc/装配中心任务看板修改方案.docx
Normal file
20
index.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link href="/vite.svg" rel="icon" type="image/svg+xml" />
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||||
|
<title>元利生产看板</title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.write("<script src='/public/config.js?v=" + new Date().getTime() + "'><\/script>");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="/src/main.js" type="module"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
5024
package-lock.json
generated
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "vue3-view",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
|
"@iamzzg/data-view": "^2.10.0",
|
||||||
|
"@jiaminghi/data-view": "^2.10.0",
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"datav-vue3": "^1.0.0",
|
||||||
|
"echarts": "^6.0.0",
|
||||||
|
"element-plus": "^2.8.6",
|
||||||
|
"js-cookie": "^3.0.5",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"pinia": "^2.2.5",
|
||||||
|
"rollup-plugin-copy": "^3.5.0",
|
||||||
|
"tdesign-vue-next": "^1.10.3",
|
||||||
|
"vue": "^3.5.12",
|
||||||
|
"vue-cookies": "^1.8.4",
|
||||||
|
"vue-router": "^4.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"patch-package": "^8.0.1",
|
||||||
|
"sass": "^1.80.5",
|
||||||
|
"vite": "^5.4.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
19
prettier.config.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export default {
|
||||||
|
printWidth: 100, // 一行最多多少字符
|
||||||
|
tabWidth: 2, // 缩进的宽度,2 个空格
|
||||||
|
useTabs: false, // 是否使用制表符,不使用制表符,使用空格
|
||||||
|
semi: false, // 语句结尾是否使用分号
|
||||||
|
bracketSpacing: true, // 大括号 {} 中开始和结束是否要空格,true — { foo: 1 },false — {foo: 1}
|
||||||
|
trailingComma: 'none', // 数组或对象或参数的最后一项是否尾随逗号,none — 没有尾随逗号,all — 尽可能使用尾随逗号,es5 — 在 ES5 中有效的尾随逗号(对象、数组等),TypeScript 和 Flow 类型参数中的尾随逗号。
|
||||||
|
arrowParens: 'always', // 只有一个参数的箭头函数是否带括号,always — 始终带括号,avoid — 不带括号
|
||||||
|
proseWrap: 'always', // 什么对代码进行折行,always — 如果超过 printWidth 指定的一行最多字符宽度,则进行折行;never — 将每块代码块展开成一行;preserve — 什么都不做,保持原样。
|
||||||
|
htmlWhitespaceSensitivity: 'strict', // 根据显示样式决定 html 要不要折行
|
||||||
|
endOfLine: 'auto', // 每行的结束符(回车符、换行符),取值请参考 https://www.prettier.cn/docs/options.html#end-of-line
|
||||||
|
// 每个文件格式化的范围是文件的全部内容
|
||||||
|
rangeStart: 0,
|
||||||
|
rangeEnd: Infinity,
|
||||||
|
// 不需要写文件开头的@prettier
|
||||||
|
requirePragma: false,
|
||||||
|
// 不需要自动在文件开头插入@prettier
|
||||||
|
insertPragma: false,
|
||||||
|
}
|
||||||
BIN
public/ClientLogo.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
205
public/config.js
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
window.g = {
|
||||||
|
API_WEB_URL: 'https://192.168.2.92:10101',
|
||||||
|
};
|
||||||
|
window.configData = {
|
||||||
|
MachineToolList: [
|
||||||
|
{
|
||||||
|
名称:'机床1',
|
||||||
|
编号: 'CNC-A001',
|
||||||
|
标题色:'#f2ba02',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床2',
|
||||||
|
编号: 'CNC-A002',
|
||||||
|
标题色:'#00b050',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床3',
|
||||||
|
编号: 'CNC-A003',
|
||||||
|
标题色:'#ff0000',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床4',
|
||||||
|
编号: 'CNC-A004',
|
||||||
|
标题色:'#f2ba02',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床5',
|
||||||
|
编号: 'CNC-A002',
|
||||||
|
标题色:'#00b050',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床6',
|
||||||
|
编号: 'CNC-A001',
|
||||||
|
标题色:'#f2ba02',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床7',
|
||||||
|
编号: 'CNC-A002',
|
||||||
|
标题色:'#00b050',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床8',
|
||||||
|
编号: 'CNC-A001',
|
||||||
|
标题色:'#808080',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床9',
|
||||||
|
编号: 'CNC-A001',
|
||||||
|
标题色:'#808080',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床10',
|
||||||
|
编号: 'CNC-A002',
|
||||||
|
标题色:'#808080',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床11',
|
||||||
|
编号: 'CNC-A002',
|
||||||
|
标题色:'#00b050',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
名称:'机床12',
|
||||||
|
编号: 'CNC-A001',
|
||||||
|
标题色:'#f2ba02',
|
||||||
|
主轴转速:'1560',
|
||||||
|
快进速度:'1560',
|
||||||
|
程序号:'P-A000001',
|
||||||
|
刀具号:'T01',
|
||||||
|
加工计数:'12',
|
||||||
|
循环时间:'0 H 10 M 50 S',
|
||||||
|
坐标: [
|
||||||
|
{name:'相对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'机械',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
{name:'绝对',X:100.2,Y:0,Z:500.6,B:123.2,C:10.1},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
],
|
||||||
|
RobotList: [
|
||||||
|
{J1:0,J2:50.3,J3:60.0,J4:40,J5:90,J6:153.9,E1:1200},
|
||||||
|
{J1:0,J2:50.3,J3:60.0,J4:40,J5:90,J6:153.9},
|
||||||
|
{J1:0,J2:50.3,J3:60.0,J4:40,J5:90,J6:153.9,E1:1200}
|
||||||
|
]
|
||||||
|
}
|
||||||
1
public/svg/加急件.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778308486289" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="45626" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M505.28 316.48c102.272 0 185.92 83.648 185.92 185.92v235.584H319.36V502.4c0-102.24 83.68-185.92 185.92-185.92z m136.96 227.04a22.72 22.72 0 0 0 21.184-24.192c-4.032-60.928-24.544-106.624-60.928-135.744-52.32-41.92-117.536-32.32-120.224-31.872a22.72 22.72 0 1 0 7.04 44.896c0.384-0.096 48.544-6.592 84.8 22.4 26.048 20.864 40.832 55.616 43.968 103.36a22.72 22.72 0 0 0 24.192 21.12zM721.568 217.92a20.48 20.48 0 0 1 7.488 27.904l-35.2 61.056a20.512 20.512 0 0 1-27.968 7.488 20.512 20.512 0 0 1-7.456-27.936l35.232-61.056a20.48 20.48 0 0 1 27.904-7.456m158.336 158.304a20.48 20.48 0 0 1-7.488 27.904l-61.024 35.264a20.512 20.512 0 0 1-27.936-7.488 20.512 20.512 0 0 1 7.488-27.936l61.056-35.2a20.48 20.48 0 0 1 27.904 7.456m-652.704 55.68a20.48 20.48 0 0 1-27.904 7.488L138.208 404.16a20.512 20.512 0 0 1-7.488-27.936 20.512 20.512 0 0 1 27.936-7.488l61.024 35.264a20.48 20.48 0 0 1 7.488 27.904m117.536-117.568a20.48 20.48 0 0 1-27.904-7.456L281.568 245.856a20.512 20.512 0 0 1 7.456-27.936 20.512 20.512 0 0 1 27.936 7.52l35.2 61.024a20.48 20.48 0 0 1-7.456 27.904M505.28 271.36a20.48 20.48 0 0 1-20.416-20.448V180.48A20.48 20.48 0 0 1 505.28 160a20.48 20.48 0 0 1 20.48 20.48V250.88a20.48 20.48 0 0 1-20.48 20.448m217.12 498.72H278.24l-27.008 82.88H749.44l-27.04-82.88z m-366.4 62.56H275.296l15.68-47.52h112.512l-47.488 47.52z" fill="#d81e06" p-id="45627"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
1
public/svg/加急任务数.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778308486289" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="45626" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M505.28 316.48c102.272 0 185.92 83.648 185.92 185.92v235.584H319.36V502.4c0-102.24 83.68-185.92 185.92-185.92z m136.96 227.04a22.72 22.72 0 0 0 21.184-24.192c-4.032-60.928-24.544-106.624-60.928-135.744-52.32-41.92-117.536-32.32-120.224-31.872a22.72 22.72 0 1 0 7.04 44.896c0.384-0.096 48.544-6.592 84.8 22.4 26.048 20.864 40.832 55.616 43.968 103.36a22.72 22.72 0 0 0 24.192 21.12zM721.568 217.92a20.48 20.48 0 0 1 7.488 27.904l-35.2 61.056a20.512 20.512 0 0 1-27.968 7.488 20.512 20.512 0 0 1-7.456-27.936l35.232-61.056a20.48 20.48 0 0 1 27.904-7.456m158.336 158.304a20.48 20.48 0 0 1-7.488 27.904l-61.024 35.264a20.512 20.512 0 0 1-27.936-7.488 20.512 20.512 0 0 1 7.488-27.936l61.056-35.2a20.48 20.48 0 0 1 27.904 7.456m-652.704 55.68a20.48 20.48 0 0 1-27.904 7.488L138.208 404.16a20.512 20.512 0 0 1-7.488-27.936 20.512 20.512 0 0 1 27.936-7.488l61.024 35.264a20.48 20.48 0 0 1 7.488 27.904m117.536-117.568a20.48 20.48 0 0 1-27.904-7.456L281.568 245.856a20.512 20.512 0 0 1 7.456-27.936 20.512 20.512 0 0 1 27.936 7.52l35.2 61.024a20.48 20.48 0 0 1-7.456 27.904M505.28 271.36a20.48 20.48 0 0 1-20.416-20.448V180.48A20.48 20.48 0 0 1 505.28 160a20.48 20.48 0 0 1 20.48 20.48V250.88a20.48 20.48 0 0 1-20.48 20.448m217.12 498.72H278.24l-27.008 82.88H749.44l-27.04-82.88z m-366.4 62.56H275.296l15.68-47.52h112.512l-47.488 47.52z" fill="#d81e06" p-id="45627"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
1
public/svg/及时送检数.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778309497680" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="51658" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M799.197483 1024H224.802517A130.013483 130.013483 0 0 1 94.881079 894.067056V227.374022A130.013483 130.013483 0 0 1 224.802517 97.452584h47.65627v64.960719h-47.65627a65.018247 65.018247 0 0 0-64.972225 64.960719v666.693034a65.018247 65.018247 0 0 0 64.960719 64.960719h574.406472a65.018247 65.018247 0 0 0 64.960719-64.960719V227.374022a65.018247 65.018247 0 0 0-64.960719-64.960719h-42.099056V97.452584h42.099056a130.013483 130.013483 0 0 1 129.921438 129.921438v666.693034A130.013483 130.013483 0 0 1 799.197483 1024z" fill="#1296db" p-id="51659"></path><path d="M644.308854 259.854382h-264.629214a129.932944 129.932944 0 0 1 0-259.854382h264.629214a129.932944 129.932944 0 0 1 0 259.854382z m-264.629214-194.893663a64.972225 64.972225 0 0 0 0 129.932944h264.629214a64.972225 64.972225 0 0 0 0-129.932944z" fill="#1296db" p-id="51660"></path><path d="M241.796315 374.910562m32.480359 0l475.446652 0q32.48036 0 32.480359 32.480359l0 0q0 32.48036-32.480359 32.48036l-475.446652 0q-32.48036 0-32.480359-32.48036l0 0q0-32.48036 32.480359-32.480359Z" fill="#1296db" p-id="51661"></path><path d="M241.796315 541.845573m32.480359 0l475.446652 0q32.48036 0 32.480359 32.48036l0 0q0 32.48036-32.480359 32.480359l-475.446652 0q-32.48036 0-32.480359-32.480359l0 0q0-32.48036 32.480359-32.48036Z" fill="#1296db" p-id="51662"></path><path d="M241.796315 708.780584m32.480359 0l475.446652 0q32.48036 0 32.480359 32.48036l0 0q0 32.48036-32.480359 32.480359l-475.446652 0q-32.48036 0-32.480359-32.480359l0 0q0-32.48036 32.480359-32.48036Z" fill="#1296db" p-id="51663"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
1
public/svg/工艺BOM待完成.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778308362537" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17666" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M96.4 503.7c-17.7 0-32-14.3-32-32v-100c0-23.1 11.3-45.3 31-60.9 17.8-14.1 40.9-21.8 65.1-21.8H382c24.2 0 47.4 7.7 65.1 21.8 19.7 15.6 31 37.8 31 60.9V421c0 17.7-14.3 32-32 32s-32-14.3-32-32v-49.3c0-7.6-12.5-18.7-32.1-18.7H160.5c-19.6 0-32.1 11.1-32.1 18.7v100c0 17.7-14.3 32-32 32z" fill="#1296db" p-id="17667"></path><path d="M863.9 960H382c-17.7 0-32-14.3-32-32s14.3-32 32-32h481.9c19.6 0 32.1-11.1 32.1-18.7V471.7c0-7.6-12.5-18.7-32.1-18.7H446.1c-17.7 0-32-14.3-32-32s14.3-32 32-32h417.8c24.2 0 47.4 7.7 65.1 21.8 19.7 15.6 31 37.8 31 60.9v405.5c0 23.1-11.3 45.3-31 60.9-17.8 14.1-40.9 21.9-65.1 21.9z" fill="#1296db" p-id="17668"></path><path d="M382 960H160.5c-24.2 0-47.4-7.7-65.1-21.8-19.7-15.6-31-37.8-31-60.9V471.7c0-17.7 14.3-32 32-32s32 14.3 32 32v405.5c0 7.6 12.5 18.7 32.1 18.7H382c17.7 0 32 14.3 32 32S399.6 960 382 960z" fill="#1296db" p-id="17669"></path><path d="M255.8 588.6h49c30.4 0 53.6 8.8 53.6 36.4 0 13.6-7.6 27.6-20.2 32v1c16 3.8 27.8 15 27.8 35 0 29.8-24.8 43.6-57.4 43.6h-52.8v-148z m46.8 60.2c18.8 0 27-7.6 27-19.4 0-13-8.8-18.2-26.6-18.2h-17.6v37.6h17.2z m3.4 65c20.2 0 31.2-7.2 31.2-22.6 0-14.4-10.8-20.4-31.2-20.4h-20.6v43H306zM428.6 662c0-48.2 27-76.2 66.2-76.2S561 614 561 662s-27 77.4-66.2 77.4-66.2-29.4-66.2-77.4z m102.2 0c0-31.8-14-50.8-36-50.8s-36 19-36 50.8c0 31.6 14 51.8 36 51.8s36-20.2 36-51.8zM634 588.6h32.8l25.2 69.2c3.2 9.2 6 19 9 28.6h1c3.2-9.6 5.8-19.4 9-28.6l24.6-69.2h32.8v148h-27v-61.8c0-14 2.2-34.6 3.6-48.6h-0.8l-12 34.8-22.8 62.2h-17l-23-62.2-11.8-34.8h-0.8c1.4 14 3.8 34.6 3.8 48.6v61.8H634v-148zM801.1 429.2h-64V224.8H191.9v94.4h-64V204.8c0-24.3 19.7-44 44-44h585.2c24.3 0 44 19.7 44 44v224.4z" fill="#1296db" p-id="17670"></path><path d="M896.5 422.7h-64V128.1H289v74.4h-64v-94.4c0-24.3 19.7-44 44-44h583.5c24.3 0 44 19.7 44 44v314.6z" fill="#1296db" p-id="17671"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
1
public/svg/异常未关闭消息数.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778309580786" class="icon" viewBox="0 0 1104 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="69935" xmlns:xlink="http://www.w3.org/1999/xlink" width="276" height="256"><path d="M961.369 1020.986H141.063c-55.01 0-99.202-21.701-121.272-59.552-22.076-37.85-19.39-87.293 7.396-135.606l413.89-746.48c26.941-48.666 67.004-76.561 109.866-76.561 42.867 0 82.89 27.86 109.907 76.483l414.323 746.635c26.78 48.314 29.55 97.72 7.433 135.565-22.035 37.85-66.263 59.516-121.237 59.516zM550.984 81.148c-13.628 0-29.239 13.595-41.894 36.362L95.202 864.067c-12.968 23.43-16.004 44.475-8.258 57.752 7.753 13.244 27.488 20.85 54.119 20.85h820.306c26.673 0 46.372-7.565 54.119-20.85 7.704-13.242 4.71-34.28-8.293-57.71l-414.318-746.6c-12.618-22.766-28.306-36.361-41.893-36.361z m0.233 586.348c-21.488 0-38.93-17.516-38.93-39.182v-352.63c0-21.63 17.442-39.183 38.93-39.183 21.492 0 38.934 17.554 38.934 39.183v352.63c0 21.666-17.442 39.182-38.934 39.182z m-0.279 191.523c33.503 0 60.663-27.382 60.663-61.16 0-33.777-27.16-61.159-60.663-61.159s-60.663 27.382-60.663 61.16c0 33.777 27.16 61.16 60.663 61.16z" p-id="69936" fill="#d81e06"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
1
public/svg/待处理销售订单.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778308309283" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11996" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M327.68 408.959908l369.280092 0 0 41.600092L327.68 450.56 327.68 408.959908zM327.68 593.279908l225.920092 0 0 41.600092L327.68 634.88 327.68 593.279908zM778.239754 860.829567 245.76 860.829567c-11.053993 0-21.788431-2.169583-31.904637-6.448326-9.761036-4.128668-18.52288-10.03466-26.042366-17.554145-7.519486-7.51924-13.425478-16.28133-17.554145-26.04212-4.278743-10.116452-6.448326-20.85089-6.448326-31.904883L163.810525 224.639908c0-11.053993 2.169583-21.788431 6.448326-31.904883 4.128422-9.760791 10.03466-18.52288 17.554145-26.04212 7.519486-7.519486 16.28133-13.425723 26.04212-17.554145 10.116452-4.278989 20.85089-6.448326 31.904883-6.448326L326.432238 142.690433l0 41.018949-80.672238 0c-22.569265 0-40.930525 18.361261-40.930525 40.930525l0 554.240184c0 22.569265 18.361261 40.930525 40.930525 40.930525l532.479754 0c22.569265 0 40.930771-18.361261 40.930771-40.930525L819.170525 224.639908c0-22.569265-18.361506-40.930525-40.930771-40.930525L697.813385 183.709383l0-41.018949 80.42637 0c11.054238 0 21.788431 2.169583 31.904883 6.448326 9.760791 4.128422 18.52288 10.03466 26.042366 17.554145 7.51924 7.519486 13.425232 16.28133 17.554145 26.04212 4.278743 10.116452 6.448326 20.85089 6.448326 31.904883l0 554.240184c0 11.053993-2.169583 21.788431-6.448326 31.904883-4.128913 9.760791-10.03466 18.52288-17.554145 26.04212-7.519731 7.519486-16.281575 13.425478-26.042366 17.554145C800.028185 858.659984 789.293993 860.829567 778.239754 860.829567zM716.829475 265.629383 307.170525 265.629383l0-133.149475c0-28.248056 22.981419-51.229475 51.229475-51.229475l52.897497 0c18.91342-37.660554 57.71096-62.079846 100.702503-62.079846 42.991789 0 81.789083 24.419293 100.702258 62.079846l52.897497 0c28.248056 0 51.229475 22.981419 51.229475 51.229475L716.829229 265.629383zM348.189475 224.610433l327.621051 0 0-92.130525c0-5.630158-4.580367-10.210525-10.210525-10.210525l-81.080708 0-4.769251-13.818228c-9.962938-28.866779-37.189695-48.261619-67.750041-48.261619-30.560345 0-57.787103 19.39484-67.750286 48.261619l-4.769251 13.818228L358.4 122.269383c-5.630158 0-10.210525 4.580367-10.210525 10.210525L348.189475 224.610433z" fill="#1296db" p-id="11997"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
1
public/svg/待排产任务.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778307683946" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6432" width="256" height="256" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M109.714286 73.142857c-21.942857 0-36.571429 14.628571-36.571429 36.571429v804.571428c0 21.942857 14.628571 36.571429 36.571429 36.571429h804.571428c21.942857 0 36.571429-14.628571 36.571429-36.571429v-804.571428c0-21.942857-14.628571-36.571429-36.571429-36.571429h-804.571428z m0-73.142857h804.571428c58.514286 0 109.714286 51.2 109.714286 109.714286v804.571428c0 58.514286-51.2 109.714286-109.714286 109.714286h-804.571428C51.2 1024 0 972.8 0 914.285714v-804.571428C0 51.2 51.2 0 109.714286 0z m438.857143 292.571429h219.428571c21.942857 0 36.571429 14.628571 36.571429 36.571428s-14.628571 36.571429-36.571429 36.571429h-219.428571c-21.942857 0-36.571429-14.628571-36.571429-36.571429s14.628571-36.571429 36.571429-36.571428z m-219.428572 438.857142c21.942857 0 36.571429-14.628571 36.571429-36.571428S351.085714 658.285714 329.142857 658.285714s-36.571429 14.628571-36.571428 36.571429 14.628571 36.571429 36.571428 36.571428z m0 73.142858C270.628571 804.571429 219.428571 753.371429 219.428571 694.857143S270.628571 585.142857 329.142857 585.142857 438.857143 636.342857 438.857143 694.857143 387.657143 804.571429 329.142857 804.571429z m-7.314286-446.171429L241.371429 277.942857c-14.628571-14.628571-36.571429-14.628571-51.2 0-14.628571 14.628571-14.628571 36.571429 0 51.2L292.571429 431.542857c7.314286 7.314286 21.942857 14.628571 29.257142 14.628572s21.942857 0 29.257143-7.314286l153.6-153.6c14.628571-14.628571 14.628571-36.571429 0-51.2-14.628571-14.628571-36.571429-14.628571-51.2 0L321.828571 358.4zM548.571429 658.285714h219.428571c21.942857 0 36.571429 14.628571 36.571429 36.571429s-14.628571 36.571429-36.571429 36.571428h-219.428571c-21.942857 0-36.571429-14.628571-36.571429-36.571428s14.628571-36.571429 36.571429-36.571429z" p-id="6433" fill="#1296db"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
1
public/svg/未及时入库.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778308450061" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="35157" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M787.7 895.6H618c-8.5 0-15.4-6.9-15.4-15.4V579.5H417.5v300.8c0 8.5-6.9 15.4-15.4 15.4H232.4c-8.5 0-15.4-6.9-15.4-15.4v-347H70.9c-6.2 0-11.9-3.8-14.2-9.5-2.4-5.8-1.1-12.4 3.3-16.8L499.1 67.7c2.9-2.9 6.8-4.5 10.9-4.5 4.1 0 8 1.6 10.9 4.5L960 506.8c4.4 4.4 5.7 11 3.3 16.8-2.4 5.8-8 9.5-14.2 9.5h-146v347c0 8.6-6.9 15.5-15.4 15.5z m-154.3-30.8h138.8v-347c0-8.5 6.9-15.4 15.4-15.4h124.3L522.8 113.3 510 100.5 108.2 502.3h124.3c8.5 0 15.4 6.9 15.4 15.4v347h138.8V564c0-8.5 6.9-15.4 15.4-15.4H618c8.5 0 15.4 6.9 15.4 15.4v300.8z" fill="#1296db" p-id="35158"></path></svg>
|
||||||
|
After Width: | Height: | Size: 899 B |
1
public/svg/机加未及时开工.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778308428111" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="29260" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M579.008 963.754667h-152.234667a16.277333 16.277333 0 0 1-16.192-14.549334l-5.994666-55.786666a391.509333 391.509333 0 0 1-102.272-43.605334l-42.176 33.984a16.234667 16.234667 0 0 1-21.738667-1.173333L130.773333 774.997333a16.298667 16.298667 0 0 1-1.173333-21.738666l35.221333-43.733334a391.061333 391.061333 0 0 1-41.536-103.146666l-53.845333-5.781334a16.298667 16.298667 0 0 1-14.549333-16.192v-152.234666c0-8.32 6.272-15.317333 14.549333-16.192l55.786667-5.994667a392.234667 392.234667 0 0 1 43.605333-102.272l-33.984-42.197333a16.277333 16.277333 0 0 1 1.173333-21.738667l107.626667-107.626667a16.298667 16.298667 0 0 1 21.738667-1.173333l43.733333 35.242667a390.186667 390.186667 0 0 1 103.146667-41.536l5.781333-53.845334a16.298667 16.298667 0 0 1 16.192-14.549333h152.234667c8.32 0 15.317333 6.272 16.192 14.549333l5.994666 55.786667a391.957333 391.957333 0 0 1 102.293334 43.605333l42.176-33.984a16.277333 16.277333 0 0 1 21.738666 1.173334l107.626667 107.626666a16.298667 16.298667 0 0 1 1.173333 21.738667l-35.221333 43.712a391.637333 391.637333 0 0 1 41.536 103.146667l53.845333 5.802666a16.298667 16.298667 0 0 1 14.549334 16.192v152.234667c0 8.32-6.272 15.317333-14.549334 16.192l-55.786666 5.994667a391.210667 391.210667 0 0 1-43.605334 102.272l33.984 42.176a16.277333 16.277333 0 0 1-1.173333 21.738666l-107.626667 107.626667a16.277333 16.277333 0 0 1-21.738666 1.173333l-43.712-35.221333a390.784 390.784 0 0 1-103.146667 41.536l-5.802667 53.845333a16.32 16.32 0 0 1-16.213333 14.549334z m-137.578667-32.597334h122.944l5.482667-50.816a16.256 16.256 0 0 1 12.8-14.186666 359.168 359.168 0 0 0 114.304-45.994667 16.277333 16.277333 0 0 1 18.794667 1.173333l41.152 33.173334 86.933333-86.933334-32.085333-39.829333a16.298667 16.298667 0 0 1-0.981334-19.093333 359.466667 359.466667 0 0 0 48.362667-113.322667 16.32 16.32 0 0 1 14.122667-12.458667l52.544-5.653333v-122.944l-50.816-5.482667a16.277333 16.277333 0 0 1-14.186667-12.8 359.253333 359.253333 0 0 0-45.994667-114.304 16.256 16.256 0 0 1 1.173334-18.794666l33.173333-41.152-86.933333-86.933334-39.829334 32.085334a16.298667 16.298667 0 0 1-19.093333 0.981333 358.485333 358.485333 0 0 0-113.322667-48.362667 16.32 16.32 0 0 1-12.458666-14.122666l-5.653334-52.544h-122.944l-5.461333 50.816a16.277333 16.277333 0 0 1-12.8 14.186666 359.253333 359.253333 0 0 0-114.304 45.994667 16.256 16.256 0 0 1-18.816-1.173333l-41.173333-33.194667-86.933334 86.933333 32.085334 39.829334a16.298667 16.298667 0 0 1 0.981333 19.093333 359.466667 359.466667 0 0 0-48.362667 113.322667 16.32 16.32 0 0 1-14.122666 12.458666l-52.544 5.696v122.944l50.816 5.461334a16.256 16.256 0 0 1 14.186666 12.8 358.869333 358.869333 0 0 0 45.994667 114.304 16.256 16.256 0 0 1-1.173333 18.794666l-33.173334 41.173334 86.933334 86.933333 39.829333-32.085333a16.298667 16.298667 0 0 1 19.093333-0.981334 358.890667 358.890667 0 0 0 113.322667 48.362667c6.72 1.578667 11.712 7.253333 12.458667 14.122667l5.674666 52.522666z m65.194667-206.293333c-117.376 0-212.864-95.488-212.864-212.864 0-117.354667 95.488-212.842667 212.864-212.842667 117.354667 0 212.864 95.488 212.864 212.842667 0 117.376-95.488 212.864-212.864 212.864z m0-393.130667c-99.413333 0-180.266667 80.874667-180.266667 180.266667 0 99.413333 80.874667 180.266667 180.266667 180.266667 99.392 0 180.266667-80.874667 180.266667-180.266667 0-99.392-80.874667-180.266667-180.266667-180.266667z" p-id="29261" fill="#1296db"></path></svg>
|
||||||
|
After Width: | Height: | Size: 3.7 KiB |
1
public/svg/缺勤人员.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778309606904" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="75594" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M 634.061 483.328 c 58.1632 -37.0688 97.0752 -102.195 97.0752 -176.128 c 0 -115.098 -93.7984 -208.896 -208.896 -208.896 S 313.344 192.102 313.344 307.2 c 0 74.1376 38.912 139.059 97.0752 176.128 C 248.013 531.661 129.024 682.189 129.024 860.16 c 0 13.5168 11.0592 24.576 24.576 24.576 s 24.576 -11.0592 24.576 -24.576 c 0 -189.645 154.419 -344.064 344.064 -344.064 S 866.304 670.515 866.304 860.16 c 0 13.5168 11.0592 24.576 24.576 24.576 s 24.576 -11.0592 24.576 -24.576 c 0 -177.971 -118.989 -328.499 -281.395 -376.832 Z M 362.496 307.2 c 0 -88.064 71.68 -159.744 159.744 -159.744 s 159.744 71.68 159.744 159.744 s -71.68 159.744 -159.744 159.744 s -159.744 -71.68 -159.744 -159.744 Z" p-id="75595" fill="#e98f36"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
1
public/svg/装配合格率.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778309554953" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="57629" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M160 32h576l192 192v704a64 64 0 0 1-64 64H160a64 64 0 0 1-64-64V96a64 64 0 0 1 64-64z m544 32v192h192l-192-192z m-96 288l-288 512h64l288-512h-64z m-256 256c53.024 0 96-57.28 96-128s-42.976-128-96-128-96 57.28-96 128 42.976 128 96 128z m0-64c-17.664 0-32-28.64-32-64s14.336-64 32-64 32 28.64 32 64-14.336 64-32 64z m288 320c53.024 0 96-57.28 96-128s-42.976-128-96-128-96 57.28-96 128 42.976 128 96 128z m0-64c-17.664 0-32-28.64-32-64s14.336-64 32-64 32 28.64 32 64-14.336 64-32 64z" fill="#1296db" p-id="57630"></path></svg>
|
||||||
|
After Width: | Height: | Size: 857 B |
1
public/svg/采购未及时到货.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1778308396421" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23623" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M956.16 465.92c-1.92-4.48-3.84-8.32-7.68-12.16l-188.16-188.16C753.28 257.92 743.68 255.36 734.08 256L576 256c0-70.4-57.6-128-128-128L128 128C57.6 128 0 185.6 0 256l0 480C0 753.92 14.08 768 32 768L64 768c0 70.4 57.6 128 128 128 70.4 0 128-57.6 128-128l320 0c0 70.4 57.6 128 128 128 70.4 0 128-57.6 128-128l32 0c17.92 0 32-14.08 32-32l0-256C960 474.88 958.72 469.76 956.16 465.92zM192 832c-35.2 0-64-28.8-64-64 0-35.2 28.8-64 64-64 35.2 0 64 28.8 64 64C256 803.2 227.2 832 192 832zM512 704 302.08 704C280.32 665.6 239.36 640 192 640c-47.36 0-88.32 25.6-110.08 64L64 704 64 256c0-35.2 28.8-64 64-64l320 0c35.2 0 64 28.8 64 64L512 704zM768 832c-35.2 0-64-28.8-64-64 0-35.2 28.8-64 64-64 35.2 0 64 28.8 64 64C832 803.2 803.2 832 768 832zM896 704l-17.92 0c-22.4-38.4-62.72-64-110.08-64-47.36 0-88.32 25.6-110.08 64L576 704 576 320l144.64 0L896 495.36 896 704z" p-id="23624" fill="#1296db"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
56
src/App.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<t-loading :loading="isLoading" size="large" text="加载中(大)...">
|
||||||
|
<router-view/>
|
||||||
|
</t-loading>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useAppStore} from "@/store/app.js";
|
||||||
|
import {storeToRefs} from "pinia";
|
||||||
|
|
||||||
|
const store = useAppStore();
|
||||||
|
// storeToRefs 响应式解构赋值 pinia
|
||||||
|
const {isLoading} = storeToRefs(store);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
// 定义的全局变量
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*修改提示框样式 更醒目*/
|
||||||
|
.el-message-box {
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
.el-message-box__title {
|
||||||
|
font-size: 35px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgb(255, 143, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提示文本
|
||||||
|
.el-message-box__message p {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// icon图标
|
||||||
|
.el-message-box__status {
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-message-box__btns {
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
width: 48%;
|
||||||
|
height: 6vh;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
BIN
src/assets/404_images/404.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
src/assets/404_images/404_cloud.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
src/assets/dashboard/category-panel-frame.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/dashboard/title-frame.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/font/DS-DIGI.TTF
Normal file
BIN
src/assets/font/DS-DIGIB.TTF
Normal file
BIN
src/assets/font/DS-DIGII.TTF
Normal file
BIN
src/assets/font/DS-DIGIT.TTF
Normal file
4
src/assets/font/font.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@font-face{
|
||||||
|
font-family:electronicFont;
|
||||||
|
src:url(./DS-DIGIT.TTF)
|
||||||
|
}
|
||||||
BIN
src/assets/font/font.zip
Normal file
BIN
src/assets/image/MachineTool.png
Normal file
|
After Width: | Height: | Size: 231 KiB |
BIN
src/assets/image/Robot.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
src/assets/image/image4.png
Normal file
|
After Width: | Height: | Size: 329 B |
50
src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<h1>{{ msg }}</h1>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<button type="button" @click="count++">count is {{ count }}</button>
|
||||||
|
<p>
|
||||||
|
Edit
|
||||||
|
<code>components/HelloWorld.vue</code> to test HMR
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Check out
|
||||||
|
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||||
|
>create-vue</a
|
||||||
|
>, the official Vue + Vite starter
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Learn more about IDE Support for Vue in the
|
||||||
|
<a
|
||||||
|
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
|
||||||
|
target="_blank"
|
||||||
|
>Vue Docs Scaling up Guide</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||||
|
<el-button type="primary">21321312</el-button>
|
||||||
|
<t-button disabled loading>确定</t-button>
|
||||||
|
<t-button variant="dashed" ghost>幽灵按钮</t-button>
|
||||||
|
<icon name="cps-icon-home-sheep" url="https://tdesign.gtimg.com/icon/default-demo/index.js" size="2em" />
|
||||||
|
|
||||||
|
123
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Icon } from 'tdesign-icons-vue-next';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
msg: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.read-the-docs {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
50
src/main.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import {createApp} from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
//ElementPlus
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
// TDesign
|
||||||
|
import TDesign from 'tdesign-vue-next'
|
||||||
|
// 引入组件库的少量全局样式变量
|
||||||
|
import 'tdesign-vue-next/es/style/index.css'
|
||||||
|
// datav
|
||||||
|
import DataVVue3 from 'datav-vue3'
|
||||||
|
// pinia
|
||||||
|
import {createPinia} from 'pinia'
|
||||||
|
//router
|
||||||
|
import router from './router/index.js'
|
||||||
|
//crud
|
||||||
|
import curdVue3 from './utils/curdVue3'
|
||||||
|
// el-icon
|
||||||
|
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||||
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||||
|
app.component(key, component)
|
||||||
|
}
|
||||||
|
// 提供全局方法
|
||||||
|
|
||||||
|
|
||||||
|
app.use(ElementPlus, {
|
||||||
|
locale: zhCn,
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(TDesign)
|
||||||
|
app.use(DataVVue3)
|
||||||
|
const pinia = createPinia() // 创建 Pinia 实例
|
||||||
|
app.use(pinia)
|
||||||
|
app.use(curdVue3)
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
app.provide('CreateData', app.config.globalProperties.CreateData)
|
||||||
|
app.provide('ExecDatabase', app.config.globalProperties.ExecDatabase)
|
||||||
|
app.provide('ExecDatabaseByParam', app.config.globalProperties.ExecDatabaseByParam)
|
||||||
|
// app.provide('ExecWritePLC', app.config.globalProperties.ExecWritePLC)
|
||||||
|
// app.provide('ExecReadPLC', app.config.globalProperties.ExecReadPLC)
|
||||||
|
// app.provide('ExecIMESAPI', app.config.globalProperties.ExecIMESAPI)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
|
||||||
|
// useRouteGuard(); // 使用路由守卫
|
||||||
|
|
||||||
33
src/router/index.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import {createRouter, createWebHashHistory} from "vue-router";
|
||||||
|
|
||||||
|
// cookies
|
||||||
|
|
||||||
|
// 创建routes
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
// 重定向
|
||||||
|
path: "/home",
|
||||||
|
redirect: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//前台首页
|
||||||
|
path: "/",
|
||||||
|
name: "root",
|
||||||
|
component: () => import("@/views/index.vue"),
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 测试任务看板
|
||||||
|
path: "/TestTaskDashboard",
|
||||||
|
name: "TestTaskDashboard",
|
||||||
|
component: () => import("@/views/TestTaskDashboard.vue"),
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//History路由模型
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
76
src/router/useRouteGuard.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { inject } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import Cookies from "vue-cookies";
|
||||||
|
|
||||||
|
|
||||||
|
export function useRouteGuard() {
|
||||||
|
const router = useRouter();
|
||||||
|
const CreateData = inject('CreateData');
|
||||||
|
const ExecDatabase = inject('ExecDatabase');
|
||||||
|
let userId = Cookies.get('userId')
|
||||||
|
// 防止首次或者刷新界面路由失效
|
||||||
|
let registerRouteFresh = true
|
||||||
|
router.beforeEach(async (to, from, next) => {
|
||||||
|
if (registerRouteFresh) {
|
||||||
|
if(userId !== ''){
|
||||||
|
// 获取权限列表
|
||||||
|
var param = []
|
||||||
|
param[0] = ['userId', userId]
|
||||||
|
var Data = CreateData('11', '基础表_权限模块_查询', param)
|
||||||
|
ExecDatabase(Data).then(response => {
|
||||||
|
if(response.data.length > 0){
|
||||||
|
routerData.value.userRouterList = response.data
|
||||||
|
// 创建动态路由列表
|
||||||
|
const dynamicRoutes = dynamicRouter(response.data);
|
||||||
|
|
||||||
|
dynamicRoutes.forEach((route) => {
|
||||||
|
// 检查是否已存在该路由,避免重复添加
|
||||||
|
if (!router.hasRoute(route.name)) {
|
||||||
|
router.addRoute('root', route);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
ElMessage.warning('该角色未分配角色模块,请先进行分配!')
|
||||||
|
}
|
||||||
|
next({ ...to, replace: true })
|
||||||
|
registerRouteFresh = false
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
next({ ...to, replace: true })
|
||||||
|
registerRouteFresh = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (to.meta.title) {
|
||||||
|
window.document.title = to.meta.title;
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 递归替换引入component
|
||||||
|
function dynamicRouter(routerList) {
|
||||||
|
const list = [];
|
||||||
|
routerList.forEach((route) => {
|
||||||
|
const routeItem = {
|
||||||
|
path: '/' + route.path,
|
||||||
|
name:route.path,
|
||||||
|
meta: {
|
||||||
|
title: route.权限模块,
|
||||||
|
},
|
||||||
|
component: async () => {
|
||||||
|
try {
|
||||||
|
const component = await import(`@/views/${route.path}/index.vue`);
|
||||||
|
console.log(component)
|
||||||
|
return component.default || component;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('引入路由组件失败,请确认地址是否配置正确:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
list.push(routeItem);
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/store/app.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import {defineStore} from 'pinia'
|
||||||
|
|
||||||
|
|
||||||
|
// 第一个参数是应用程序中 store 的唯一 id
|
||||||
|
export const useAppStore = defineStore('AppData', {
|
||||||
|
// 其它配置项
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
AID: '',
|
||||||
|
name: '',
|
||||||
|
classes: '',
|
||||||
|
teamGroup: '',
|
||||||
|
role: '',
|
||||||
|
userName: '',
|
||||||
|
password: '',
|
||||||
|
opName: '',
|
||||||
|
},
|
||||||
|
isLoading: false,
|
||||||
|
routerData: {
|
||||||
|
userRouterList: [],
|
||||||
|
nowRouterPath: '',
|
||||||
|
nowRouterTitle: '',
|
||||||
|
selected: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
})
|
||||||
15
src/utils/auth.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
|
const TokenKey = 'Admin-Token'
|
||||||
|
|
||||||
|
export function getToken() {
|
||||||
|
return Cookies.get(TokenKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setToken(token) {
|
||||||
|
return Cookies.set(TokenKey, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeToken() {
|
||||||
|
return Cookies.remove(TokenKey)
|
||||||
|
}
|
||||||
435
src/utils/curd.js
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
import { ExcelDownLoad } from '@/utils/request'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import Cookie from 'js-cookie'
|
||||||
|
import { type } from './tool' // type判断传入值的类型,比typeof更详细
|
||||||
|
export default {
|
||||||
|
exportExcel_NPOI(param) {
|
||||||
|
console.log(param, 1)
|
||||||
|
download(`${ExcelDownLoad}?param=${param}`)
|
||||||
|
},
|
||||||
|
// 用于生成传通讯服务器参数。
|
||||||
|
// 传入参数
|
||||||
|
// 1)type:11查询;12增删改,必须
|
||||||
|
// 2)name:存储过程名,必须
|
||||||
|
// 3)data:存储过程参数名和对应值,例如:
|
||||||
|
// param[0] = ['工艺计划流水号', this.工艺计划流水号] 输入参数
|
||||||
|
// param[1] = ['PageCurrent', '1111', 'int', '1'] output参数
|
||||||
|
// 数组里四个分别对应:存储过程参数名(必须);存储过程参数值(必须);参数类型(若是output必须);参数是否为output(0否1是)
|
||||||
|
// 4)pageSize,pageList:分页使用,可选
|
||||||
|
CreateData(type, name, data, pageSize, pageList) {
|
||||||
|
var param_Str = ''
|
||||||
|
var param_array = []
|
||||||
|
if (type === '7') {
|
||||||
|
data.map(v => {
|
||||||
|
pageSize.map(h => {
|
||||||
|
const val = h.toString()
|
||||||
|
if (!v[val]) {
|
||||||
|
this.$set(v, val, '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
param_Str = data
|
||||||
|
} else {
|
||||||
|
if (data !== undefined) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
param_array.push({
|
||||||
|
name: data[i][0],
|
||||||
|
value: data[i][1],
|
||||||
|
type: data[i][2],
|
||||||
|
output: data[i][3]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
param_Str = JSON.stringify(param_array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var obj = []
|
||||||
|
obj[0] = {}
|
||||||
|
obj[0].type = type
|
||||||
|
obj[0].name = name
|
||||||
|
obj[0].param = param_Str
|
||||||
|
obj[0].pageSize = pageSize
|
||||||
|
obj[0].pageList = pageList
|
||||||
|
obj[0].UserID = Cookie.get('username')
|
||||||
|
obj[0].ModularID = ''//this.$router.currentRoute.path
|
||||||
|
console.log(obj[0], 'obj[0]')
|
||||||
|
var numn = JSON.stringify(obj[0])
|
||||||
|
return numn
|
||||||
|
},
|
||||||
|
// 新通讯11/12---<
|
||||||
|
ExecDatabase(data) {
|
||||||
|
return request({
|
||||||
|
url: '',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 高精原CreateData
|
||||||
|
// Vue.prototype.CreateData = function(type, name, data, pageSize, pageList) {
|
||||||
|
// var paramStr = ''
|
||||||
|
// var paramarray = []
|
||||||
|
// if (data !== undefined) {
|
||||||
|
// for (let i = 0; i < data.length; i++) {
|
||||||
|
// paramarray.push({
|
||||||
|
// name: data[i][0],
|
||||||
|
// value: data[i][1],
|
||||||
|
// type: data[i][2],
|
||||||
|
// output: data[i][3]
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// paramStr = JSON.stringify(paramarray)
|
||||||
|
// }
|
||||||
|
// var obj = []
|
||||||
|
// obj[0] = {}
|
||||||
|
// obj[0].type = type
|
||||||
|
// obj[0].name = name
|
||||||
|
// obj[0].param = paramStr
|
||||||
|
// obj[0].pageSize = pageSize
|
||||||
|
// obj[0].pageList = pageList
|
||||||
|
// var numn = JSON.stringify(obj[0])
|
||||||
|
// return numn
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 用于打开增加框,方法能重置表单,并使表单内容回到初始化状态。
|
||||||
|
// 传入参数
|
||||||
|
// 1)表单名称,字符串形式如'form',必须
|
||||||
|
// 2) 打开表单后需要做什么,可以传数组[this.dialogvisiale = false, fetchData],也可以传回调函数function() { this.dialogvisiale = false } 可选
|
||||||
|
addForm(form, callback) {
|
||||||
|
if (type(form) === 'string') {
|
||||||
|
if (this.$refs[form]) { // 判断是否需要重置表单,如果需要重置表单
|
||||||
|
this.$refs[form].resetFields()
|
||||||
|
}
|
||||||
|
if (type(callback) === 'array') { // 判断传入是否为数组,如果是遍历数组,遇到函数将函数体变成函数执行,其余正常执行
|
||||||
|
if (callback && callback.length !== 0) {
|
||||||
|
for (let i = 0, len = callback.length; i < len; i++) {
|
||||||
|
typeof callback[i] === 'function' ? callback[i]() : callback[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type(callback) === 'function') { // 判断传入是否为回调函数
|
||||||
|
callback.call(this) // 回调函数执行,需将this改为该组件
|
||||||
|
}
|
||||||
|
this[form] = Object.assign(this.$data[form], this.$options.data()[form]) // 将表单中的数据变为初始状态。其中,this.$options.data是所有初始化的数据,this.$data是目前的数据
|
||||||
|
} else {
|
||||||
|
console.error('传入参数错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 用于打开编辑框,方法能自动传入行的数据到对应的表单中,并将表单中所有纯数字转成int。前提是行的名称需跟表单名称一样。
|
||||||
|
// 传入参数
|
||||||
|
// 1) 编辑这一行的row
|
||||||
|
// 2)表单名称,字符串形式如'form'
|
||||||
|
// 3) 打开编辑需要做什么,同样是[] 和 function
|
||||||
|
editForm(row, form, callback) {
|
||||||
|
if (type(row) === 'object' && type(form) === 'string') {
|
||||||
|
if (this.$refs[form]) { // 判断是否需要重置表单
|
||||||
|
this.$refs[form].resetFields()
|
||||||
|
}
|
||||||
|
const isNum = /^[0-9]+$/ // 正则表达式,是否全都为纯数字
|
||||||
|
for (const prop in this[form]) { // 遍历表单,当表单和row中属性名一样时,表单row中的属性赋值给form
|
||||||
|
isNum.lastIndex = 0 // 正则每一次匹配后的索引归0,否则循环会造成问题
|
||||||
|
isNum.exec(row[prop]) ? this[form][prop] = parseInt(row[prop]) : this[form][prop] = row[prop] // 通过正则匹配数字,当匹配成功后,将字符串转为int。当然你也不用担心空转成NaN,因为''匹配通不过!
|
||||||
|
}
|
||||||
|
if (type(callback) === 'array') { // 这部分处理与打开表单相同
|
||||||
|
if (callback && callback.length !== 0) {
|
||||||
|
for (let i = 0, len = callback.length; i < len; i++) {
|
||||||
|
typeof callback[i] === 'function' ? callback[i]() : callback[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type(callback) === 'function') {
|
||||||
|
callback.call(this)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('传入参数错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 用于获取下拉框中的数据
|
||||||
|
// 传入参数
|
||||||
|
// 1)请求数据函数引用如getXXX,必须
|
||||||
|
// 2)数组的实行名称['XXXX名称', 'XXXX代码'],必须
|
||||||
|
// 3)需要用哪个数组接收这个结果,字符串形式。如在组件也需要用this.select,传入值就为'select',必须
|
||||||
|
// 4) 请求函数传入的参数,可选
|
||||||
|
getSelect(requestData, select, carrier, param) {
|
||||||
|
if (type(requestData) === 'function' && type(select) === 'array' && type(carrier) === 'string') {
|
||||||
|
if (type(param) === 'array') {
|
||||||
|
requestData(...param).then(response => { // 这个方法没什么难度,不注释了
|
||||||
|
this[carrier] = []
|
||||||
|
for (let i = 0, len = response.data.length; i < len; i++) {
|
||||||
|
if (select.length === 2) {
|
||||||
|
this[carrier].push({
|
||||||
|
label: response.data[i][select[0]],
|
||||||
|
value: response.data[i][select[1]]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this[carrier].push({
|
||||||
|
value: response.data[i][select[0]]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
requestData(param).then(response => { // 这个方法没什么难度,不注释了
|
||||||
|
this[carrier] = []
|
||||||
|
for (let i = 0, len = response.data.length; i < len; i++) {
|
||||||
|
if (select.length === 2) {
|
||||||
|
this[carrier].push({
|
||||||
|
label: response.data[i][select[0]],
|
||||||
|
value: response.data[i][select[1]]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this[carrier].push({
|
||||||
|
value: response.data[i][select[0]]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('参数传递错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 普通获取数据
|
||||||
|
// 传入参数
|
||||||
|
// 1) 请求数据函数引用如getXXX,必须
|
||||||
|
// 2) 需要用哪个数组接收这个结果,字符串形式。如在组件也需要用this.select,传入值就为'select',必须
|
||||||
|
// 3) 请求函数传入的参数,可选
|
||||||
|
getData(requestData, carrier, param) {
|
||||||
|
if (type(requestData) === 'function' && type(carrier) === 'string') {
|
||||||
|
if (type(param) === 'undefined') {
|
||||||
|
requestData(param).then(response => {
|
||||||
|
const data = response.data
|
||||||
|
this[carrier] = data
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let Param = []
|
||||||
|
if (type(param) !== 'array') {
|
||||||
|
Param.push(param)
|
||||||
|
} else {
|
||||||
|
Param = param
|
||||||
|
}
|
||||||
|
requestData(...Param).then(response => {
|
||||||
|
const data = response.data
|
||||||
|
this[carrier] = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('传入参数错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取表格数据,将表格中的年月日时分秒,转换成年月日
|
||||||
|
// 传入参数
|
||||||
|
// 1)请求数据函数引用如getXXX,必须
|
||||||
|
// 2)请求的参数,可选
|
||||||
|
// 3)接受值,数组,一般是['table','total','loading']顺序不能变
|
||||||
|
getTable(requestData, param, e) {
|
||||||
|
requestData(...param).then(response => {
|
||||||
|
const data = response.data.rows
|
||||||
|
this[e[0]] = []
|
||||||
|
this[e[1]] = 0
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
const isTime = /^\d{4}([-\/.])\d{1,2}\1\d{1,2}/ // 判断是否是时间的正则表达式
|
||||||
|
for (let i = 0, len = data.length; i < len; i++) { // 循环得到每一条数据
|
||||||
|
for (const prop in data[i]) { // 循环每一条的每一项
|
||||||
|
isTime.lastIndex = 0 // 正则每一次指针复位
|
||||||
|
if (isTime.test(data[i][prop])) { // 判断是否是时间,如果是切割
|
||||||
|
data[i][prop] = data[i][prop].split(' ')[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this[e[0]] = data
|
||||||
|
this[e[1]] = parseInt(response.data.total)
|
||||||
|
} else {
|
||||||
|
this[e[0]] = []
|
||||||
|
// console.log('暂无数据')
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
this[e[2]] = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 添加表格数据
|
||||||
|
// 传入参数
|
||||||
|
// 1) 请求数据函数引用如getXXX,必须
|
||||||
|
// 2) 请求的参数,可选
|
||||||
|
// 3) 验证的表单名称,必须
|
||||||
|
// 4) 回调函数,必须
|
||||||
|
addTable(requestData, param, form, callback) {
|
||||||
|
if (type(callback) === 'function' && type(requestData) === 'function' && type(form) === 'string') {
|
||||||
|
console.log(this.$refs[form])
|
||||||
|
this.$refs[form].validate((valid) => {
|
||||||
|
console.log(valid)
|
||||||
|
if (valid) {
|
||||||
|
requestData(...param).then(response => {
|
||||||
|
if (response.data[0].result === '1') {
|
||||||
|
this.$message.success('增加成功')
|
||||||
|
callback.call(this)
|
||||||
|
} else { this.$message.error('增加失败') }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error('error submit!!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error('参数传递错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 编辑表格数据
|
||||||
|
// 传入参数
|
||||||
|
// 1) 请求数据函数引用如getXXX,必须
|
||||||
|
// 2) 请求的参数,可选
|
||||||
|
// 3) 验证的表单名称,必须
|
||||||
|
// 4) 回调函数,必须
|
||||||
|
editTable(requestData, param, form, callback) {
|
||||||
|
if (type(callback) === 'function' && type(requestData) === 'function' && type(form) === 'string') {
|
||||||
|
this.$refs[form].validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
requestData(...param).then(response => {
|
||||||
|
if (response.data[0].result === '1') {
|
||||||
|
this.$message.success('修改成功')
|
||||||
|
callback.call(this) // 回调函数修正this指向组件本身
|
||||||
|
} else { this.$message.error('修改失败') }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error('error submit!!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error('参数传递错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 删除表格数据
|
||||||
|
// 传入参数
|
||||||
|
// 1) 请求数据函数引用如getXXX,必须
|
||||||
|
// 2) id传入删除条件,必须
|
||||||
|
// 3) 回调函数,必须
|
||||||
|
deleteRow(requestData, id, callback) {
|
||||||
|
if (type(callback) === 'function' && type(requestData) === 'function') {
|
||||||
|
if (this.tableData && this.tableData.length === 1 && this.pageCurrent > 1) { // 判断删除的是否是最后一页最后一行,如果是且不是第一页,页数减一
|
||||||
|
this.pageCurrent--
|
||||||
|
}
|
||||||
|
this.$confirm('此操作将永久删除该行, 是否继续?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
requestData(id).then(response => {
|
||||||
|
if (response.data[0].result === '1') {
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
callback.call(this) // 回调函数修正this指向组件本身
|
||||||
|
} else { this.$message.error('数据占用') }
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
this.$message({
|
||||||
|
type: 'info',
|
||||||
|
message: '已取消删除'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error('参数传递错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 设置字段宽度
|
||||||
|
setColumnWidth(str) {
|
||||||
|
let columnWidth = 0
|
||||||
|
if (str === '日期') {
|
||||||
|
columnWidth = 90
|
||||||
|
} else if (str === '订单编号' || str === '领料单号' || str === '调拨单号' || str === '订单号' || str === '机床名称') {
|
||||||
|
columnWidth = 140
|
||||||
|
} else if (str === '合同编号' || str === '补货单号') {
|
||||||
|
columnWidth = 140
|
||||||
|
} else if (str === '产品名称' || str === '部件名称') {
|
||||||
|
columnWidth = 110
|
||||||
|
} else if (str === '序号') {
|
||||||
|
columnWidth = 60
|
||||||
|
} else if (str === '单位') {
|
||||||
|
columnWidth = 60
|
||||||
|
} else if (str === '数量' || str === '库存') {
|
||||||
|
columnWidth = 80
|
||||||
|
} else if (str === '单价') {
|
||||||
|
columnWidth = 90
|
||||||
|
} else if (str === '金额') {
|
||||||
|
columnWidth = 120
|
||||||
|
} else if (str === '状态' || str === '类型' || str === '选入' || str === '选出') {
|
||||||
|
columnWidth = 80
|
||||||
|
} else if (str === '付款方式') {
|
||||||
|
columnWidth = 100
|
||||||
|
} else if (str === '买方' || str === '买方名称') {
|
||||||
|
columnWidth = 150
|
||||||
|
} else if (str === '外协厂家' || str === '调入仓库' || str === '调出仓库') {
|
||||||
|
columnWidth = 150
|
||||||
|
} else if (str === '姓名') {
|
||||||
|
columnWidth = 80
|
||||||
|
} else if (str === '日期时间') {
|
||||||
|
columnWidth = 140
|
||||||
|
} else if (str === '工序') {
|
||||||
|
columnWidth = 80
|
||||||
|
} else if (str === '物料名称' || str === '工件名称' || str === '零件名称') {
|
||||||
|
columnWidth = 140
|
||||||
|
} else if (str === '图号或型号' || str === '规格' || str === '零件图号' || str === '图号' || str === '图号/型号') {
|
||||||
|
columnWidth = 120
|
||||||
|
} else if (str === '物料编码' || str === '物料编号') {
|
||||||
|
columnWidth = 120
|
||||||
|
} else if (str === '材料') {
|
||||||
|
columnWidth = 80
|
||||||
|
} else if (str === '仓位' || str === '库位') {
|
||||||
|
columnWidth = 90
|
||||||
|
} else if (str === '品名/规格/型号') {
|
||||||
|
columnWidth = 140
|
||||||
|
} else if (str === '备注') {
|
||||||
|
columnWidth = 120
|
||||||
|
} else if (str === '工序名称') {
|
||||||
|
columnWidth = 150
|
||||||
|
} else if (str === '工序说明') {
|
||||||
|
columnWidth = 300
|
||||||
|
} else if (str === '加工工时M' || str === '加工时长H' || str === '加工时长M') {
|
||||||
|
columnWidth = 90
|
||||||
|
} else if (str === '加工设备') {
|
||||||
|
columnWidth = 130
|
||||||
|
} else if (str === '产品详情' || str === '详情') {
|
||||||
|
columnWidth = 340
|
||||||
|
} else if (str === '最低库存') {
|
||||||
|
columnWidth = 110
|
||||||
|
} else if (str === '本次到货数' || str === '本次入库' || str === '补货数') {
|
||||||
|
columnWidth = 100
|
||||||
|
} else {
|
||||||
|
columnWidth = 110
|
||||||
|
}
|
||||||
|
return columnWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function download(url) {
|
||||||
|
getBlob(url, function(blob, filename) {
|
||||||
|
console.log(blob, filename, 2)
|
||||||
|
saveAs(blob, filename)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBlob(url, cb) {
|
||||||
|
var xhr = new XMLHttpRequest()
|
||||||
|
xhr.open('GET', url, true)
|
||||||
|
xhr.responseType = 'blob'
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const name = xhr.getResponseHeader('content-disposition').split('=')[1]
|
||||||
|
var filename = decodeURIComponent(name)
|
||||||
|
cb(xhr.response, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAs(blob, filename) {
|
||||||
|
if (window.navigator.msSaveOrOpenBlob) {
|
||||||
|
navigator.msSaveBlob(blob, filename)
|
||||||
|
} else {
|
||||||
|
var link = document.createElement('a')
|
||||||
|
var body = document.querySelector('body')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = filename
|
||||||
|
// fix Firefox
|
||||||
|
link.style.display = 'none'
|
||||||
|
body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
135
src/utils/curdVue3.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
import Cookie from 'js-cookie'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install(app) {
|
||||||
|
app.config.globalProperties.CreateData = function (type, name, data, pageSize, pageList) {
|
||||||
|
var param_Str = ''
|
||||||
|
var param_array = []
|
||||||
|
if (type === '7') {
|
||||||
|
data.map(v => {
|
||||||
|
pageSize.map(h => {
|
||||||
|
const val = h.toString()
|
||||||
|
if (!v[val]) {
|
||||||
|
this.$set(v, val, '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
param_Str = data
|
||||||
|
} else {
|
||||||
|
if (data !== undefined) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
param_array.push({
|
||||||
|
name: data[i][0],
|
||||||
|
value: data[i][1],
|
||||||
|
type: data[i][2],
|
||||||
|
output: data[i][3]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
param_Str = JSON.stringify(param_array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var obj = []
|
||||||
|
obj[0] = {}
|
||||||
|
obj[0].type = type
|
||||||
|
obj[0].name = name
|
||||||
|
obj[0].param = param_Str
|
||||||
|
obj[0].pageSize = pageSize
|
||||||
|
obj[0].pageList = pageList
|
||||||
|
obj[0].UserID = Cookie.get('username')
|
||||||
|
obj[0].ModularID = ''//this.$router.currentRoute.path
|
||||||
|
console.log(obj[0], 'obj[0]')
|
||||||
|
var numn = JSON.stringify(obj[0])
|
||||||
|
return numn
|
||||||
|
}
|
||||||
|
app.config.globalProperties.ExecDatabase = function (data) {
|
||||||
|
return request({
|
||||||
|
url: '',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
app.config.globalProperties.ExecDatabaseByParam = function (type, name, data, pageSize, pageList) {
|
||||||
|
var param_Str = ''
|
||||||
|
var param_array = []
|
||||||
|
if (type === '7') {
|
||||||
|
data.map(v => {
|
||||||
|
pageSize.map(h => {
|
||||||
|
const val = h.toString()
|
||||||
|
if (!v[val]) {
|
||||||
|
this.$set(v, val, '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
param_Str = data
|
||||||
|
} else {
|
||||||
|
if (data !== undefined) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
param_array.push({
|
||||||
|
name: data[i][0],
|
||||||
|
value: data[i][1],
|
||||||
|
type: data[i][2],
|
||||||
|
output: data[i][3]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
param_Str = JSON.stringify(param_array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var obj = []
|
||||||
|
obj[0] = {}
|
||||||
|
obj[0].type = type
|
||||||
|
obj[0].name = name
|
||||||
|
obj[0].param = param_Str
|
||||||
|
obj[0].pageSize = pageSize
|
||||||
|
obj[0].pageList = pageList
|
||||||
|
obj[0].UserID = Cookie.get('username')
|
||||||
|
obj[0].ModularID = ''//this.$router.currentRoute.path
|
||||||
|
console.log(obj[0], 'obj[0]')
|
||||||
|
var numn = JSON.stringify(obj[0])
|
||||||
|
return request({
|
||||||
|
url: '',
|
||||||
|
method: 'post',
|
||||||
|
data: numn
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// app.config.globalProperties.ExecWritePLC = function (opName, tagTypeCodeID, value) {
|
||||||
|
// return request({
|
||||||
|
// url: window.g.API_MES_URL + '/api/imes/Web_WritePLC',
|
||||||
|
// method: 'post',
|
||||||
|
// data: {
|
||||||
|
// tagTypeCodeID: tagTypeCodeID,
|
||||||
|
// OPName: opName,
|
||||||
|
// tagValue: value,
|
||||||
|
// }
|
||||||
|
// }).catch(function (error) {
|
||||||
|
// // 如果接口调用失败,弹出错误弹窗
|
||||||
|
// ElMessage.error('写入PLC数据接口失败: ' + error.message)
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// app.config.globalProperties.ExecReadPLC = function (opName, tagTypeCodeID) {
|
||||||
|
// return request({
|
||||||
|
// url: window.g.API_MES_URL + '/api/imes/Web_ReadPLC',
|
||||||
|
// method: 'post',
|
||||||
|
// data: {
|
||||||
|
// tagTypeCodeID: tagTypeCodeID,
|
||||||
|
// OPName: opName,
|
||||||
|
// }
|
||||||
|
// }).catch(function (error) {
|
||||||
|
// // 如果接口调用失败,弹出错误弹窗
|
||||||
|
// ElMessage.error('读取PLC数据接口失败: ' + error.message)
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// app.config.globalProperties.ExecIMESAPI = function (apiName, param) {
|
||||||
|
// return request({
|
||||||
|
// url: window.g.API_MES_URL + '/api/imes/' + apiName,
|
||||||
|
// method: 'post',
|
||||||
|
// data: param
|
||||||
|
// }).catch(function (error) {
|
||||||
|
// // 如果接口调用失败,弹出错误弹窗
|
||||||
|
// ElMessage.error('调用MES' + apiName + '接口失败: ' + error.message)
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/utils/index.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* Created by jiachenpan on 16/11/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function parseTime(time, cFormat) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||||
|
let date
|
||||||
|
if (typeof time === 'object') {
|
||||||
|
date = time
|
||||||
|
} else {
|
||||||
|
if (('' + time).length === 10) time = parseInt(time) * 1000
|
||||||
|
date = new Date(time)
|
||||||
|
}
|
||||||
|
const formatObj = {
|
||||||
|
y: date.getFullYear(),
|
||||||
|
m: date.getMonth() + 1,
|
||||||
|
d: date.getDate(),
|
||||||
|
h: date.getHours(),
|
||||||
|
i: date.getMinutes(),
|
||||||
|
s: date.getSeconds(),
|
||||||
|
a: date.getDay()
|
||||||
|
}
|
||||||
|
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||||
|
let value = formatObj[key]
|
||||||
|
// Note: getDay() returns 0 on Sunday
|
||||||
|
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
|
||||||
|
if (result.length > 0 && value < 10) {
|
||||||
|
value = '0' + value
|
||||||
|
}
|
||||||
|
return value || 0
|
||||||
|
})
|
||||||
|
return time_str
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTime(time, option) {
|
||||||
|
time = +time * 1000
|
||||||
|
const d = new Date(time)
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
const diff = (now - d) / 1000
|
||||||
|
|
||||||
|
if (diff < 30) {
|
||||||
|
return '刚刚'
|
||||||
|
} else if (diff < 3600) {
|
||||||
|
// less 1 hour
|
||||||
|
return Math.ceil(diff / 60) + '分钟前'
|
||||||
|
} else if (diff < 3600 * 24) {
|
||||||
|
return Math.ceil(diff / 3600) + '小时前'
|
||||||
|
} else if (diff < 3600 * 24 * 2) {
|
||||||
|
return '1天前'
|
||||||
|
}
|
||||||
|
if (option) {
|
||||||
|
return parseTime(time, option)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
d.getMonth() +
|
||||||
|
1 +
|
||||||
|
'月' +
|
||||||
|
d.getDate() +
|
||||||
|
'日' +
|
||||||
|
d.getHours() +
|
||||||
|
'时' +
|
||||||
|
d.getMinutes() +
|
||||||
|
'分'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeroFill(i) {
|
||||||
|
if (i >= 0 && i <= 9) {
|
||||||
|
return '0' + i
|
||||||
|
} else {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNowDAY() { // 默认显示今天
|
||||||
|
// 获取当前时间
|
||||||
|
const date = new Date()
|
||||||
|
const month = zeroFill(date.getMonth() + 1)
|
||||||
|
const day = zeroFill(date.getDate())
|
||||||
|
const time = date.getFullYear() + '-' + month + '-' + day
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get90DAY() { // 3个月显示今天
|
||||||
|
var Data = new Date()
|
||||||
|
Data.setTime(Data.getTime() - 3600 * 1000 * 24 * 90)
|
||||||
|
const month = zeroFill(Data.getMonth() + 1)
|
||||||
|
const day = zeroFill(Data.getDate())
|
||||||
|
const time = Data.getFullYear() + '-' + month + '-' + day
|
||||||
|
return time
|
||||||
|
}
|
||||||
104
src/utils/mqtt.js
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { v4 as uuid } from 'uuid'
|
||||||
|
import mqtt from 'mqtt'
|
||||||
|
export { Mqtt }
|
||||||
|
class Mqtt {
|
||||||
|
constructor(config) {
|
||||||
|
this.connection = {
|
||||||
|
host: config.host,
|
||||||
|
port: config.port,
|
||||||
|
endpoint: config.endpoint || '/mqtt',
|
||||||
|
clean: config.clean || true,
|
||||||
|
connectTimeout: config.connectTimeout || 4000,
|
||||||
|
reconnectPeriod: config.reconnectPeriod || 4000,
|
||||||
|
clientId: config.clientId || uuid(),
|
||||||
|
username: config.username || '',
|
||||||
|
password: config.password || ''
|
||||||
|
}
|
||||||
|
this.client = {
|
||||||
|
connected: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 创建连接
|
||||||
|
createConnection() {
|
||||||
|
// 连接字符串, 通过协议指定使用的连接方式
|
||||||
|
// ws 未加密 WebSocket 连接
|
||||||
|
// wss 加密 WebSocket 连接
|
||||||
|
// mqtt 未加密 TCP 连接
|
||||||
|
// mqtts 加密 TCP 连接
|
||||||
|
// wxs 微信小程序连接
|
||||||
|
// alis 支付宝小程序连接
|
||||||
|
const { host, port, endpoint, ...options } = this.connection
|
||||||
|
const connectUrl = `ws://${host}:${port}${endpoint}`
|
||||||
|
try {
|
||||||
|
this.client = mqtt.connect(connectUrl, options)
|
||||||
|
} catch (error) {
|
||||||
|
// 连接错位、
|
||||||
|
this.connectError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectError(error) {
|
||||||
|
console.log('mqtt.connect error', error)
|
||||||
|
}
|
||||||
|
// 订阅主题
|
||||||
|
doSubscribe(subscription) {
|
||||||
|
const { topic, qos } = subscription
|
||||||
|
let subscribeSuccess = false
|
||||||
|
this.client.subscribe(topic, { qos }, (error, res) => {
|
||||||
|
if (error) {
|
||||||
|
subscribeSuccess = false
|
||||||
|
console.log('Subscribe to topics error', error)
|
||||||
|
} else {
|
||||||
|
subscribeSuccess = true
|
||||||
|
console.log('Subscribe to topics res', res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return subscribeSuccess
|
||||||
|
}
|
||||||
|
// 取消订阅
|
||||||
|
doUnSubscribe(subscription) {
|
||||||
|
const { topic } = subscription
|
||||||
|
let unSubscribeSuccess = false
|
||||||
|
this.client.unsubscribe(topic, error => {
|
||||||
|
if (error) {
|
||||||
|
unSubscribeSuccess = false
|
||||||
|
console.log('Unsubscribe error', error)
|
||||||
|
} else {
|
||||||
|
unSubscribeSuccess = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return unSubscribeSuccess
|
||||||
|
}
|
||||||
|
// 发送消息
|
||||||
|
doPublish(publish) {
|
||||||
|
const { topic, qos, payload } = publish
|
||||||
|
let doPublishSuccess = false
|
||||||
|
this.client.publish(topic, payload, qos, error => {
|
||||||
|
if (error) {
|
||||||
|
doPublishSuccess = false
|
||||||
|
console.log('Publish error', error)
|
||||||
|
} else {
|
||||||
|
doPublishSuccess = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return doPublishSuccess
|
||||||
|
}
|
||||||
|
// 断开连接
|
||||||
|
destroyConnection() {
|
||||||
|
let destroyConnectionSuccess = false
|
||||||
|
if (this.client.connected) {
|
||||||
|
try {
|
||||||
|
this.client.end()
|
||||||
|
this.client = {
|
||||||
|
connected: false
|
||||||
|
}
|
||||||
|
destroyConnectionSuccess = true
|
||||||
|
console.log('Successfully disconnected!')
|
||||||
|
} catch (error) {
|
||||||
|
destroyConnectionSuccess = false
|
||||||
|
console.log('Disconnect failed', error.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return destroyConnectionSuccess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
35
src/utils/request.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
// import store from '@/store'
|
||||||
|
import {ElMessage} from 'element-plus' // 引入Element Plus的消息提示组件
|
||||||
|
const name_config = `OP8888`
|
||||||
|
// import.meta.env.VITE_BASE_URL
|
||||||
|
console.log(window.g)
|
||||||
|
const request_config = window.g.API_WEB_URL + `/submit/MESCommonBase.ashx`
|
||||||
|
export const name = name_config
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: request_config,
|
||||||
|
timeout: 1000 * 5 // 请求超时时间
|
||||||
|
})
|
||||||
|
|
||||||
|
// && config.data.name !== '菜单系统模块_通知信息_查询数据' && config.data.name !== '菜单系统模块_首页信息_查询数据1' && config.data.name !== '菜单系统模块_首页信息_查询数据2'
|
||||||
|
service.interceptors.request.use(config => {
|
||||||
|
|
||||||
|
return config
|
||||||
|
}, error => {
|
||||||
|
if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
|
||||||
|
ElMessage({
|
||||||
|
message: '请求超时,请稍后再试。',
|
||||||
|
type: 'error',
|
||||||
|
duration: 5000
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: '请求失败,请稍后再试。',
|
||||||
|
type: 'error',
|
||||||
|
duration: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default service
|
||||||
90
src/utils/setMethods.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the time to string
|
||||||
|
* @param {(Object|string|number)} time
|
||||||
|
* @param {string} cFormat
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function parseTime(time, cFormat) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||||
|
let date
|
||||||
|
if (typeof time === 'object') {
|
||||||
|
date = time
|
||||||
|
} else {
|
||||||
|
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
|
||||||
|
time = parseInt(time)
|
||||||
|
}
|
||||||
|
if (typeof time === 'number' && time.toString().length === 10) {
|
||||||
|
time = time * 1000
|
||||||
|
}
|
||||||
|
date = new Date(time)
|
||||||
|
}
|
||||||
|
const formatObj = {
|
||||||
|
y: date.getFullYear(),
|
||||||
|
m: date.getMonth() + 1,
|
||||||
|
d: date.getDate(),
|
||||||
|
h: date.getHours(),
|
||||||
|
i: date.getMinutes(),
|
||||||
|
s: date.getSeconds(),
|
||||||
|
a: date.getDay()
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||||
|
let value = formatObj[key]
|
||||||
|
// Note: getDay() returns 0 on Sunday
|
||||||
|
if (key === 'a') {
|
||||||
|
return ['日', '一', '二', '三', '四', '五', '六'][value]
|
||||||
|
}
|
||||||
|
if (result.length > 0 && value < 10) {
|
||||||
|
value = '0' + value
|
||||||
|
}
|
||||||
|
return value || 0
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line
|
||||||
|
return time_str
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the json to excel
|
||||||
|
* tableJson 导出数据 ; filenames导出表的名字; autowidth表格宽度自动 true or false; bookTypes xlsx & csv & txt
|
||||||
|
* @param {(Object)} tableJson
|
||||||
|
* @param {string} filenames
|
||||||
|
* @param {boolean} autowidth
|
||||||
|
* @param {string} bookTypes
|
||||||
|
*/
|
||||||
|
export function json2excel(tableJson, filenames, autowidth, bookTypes) {
|
||||||
|
import('@/vendor/Export2Excel').then(excel => {
|
||||||
|
var tHeader = []
|
||||||
|
var dataArr = []
|
||||||
|
var sheetnames = []
|
||||||
|
for (var i in tableJson) {
|
||||||
|
tHeader.push(tableJson[i].tHeader)
|
||||||
|
dataArr.push(formatJson(tableJson[i].filterVal, tableJson[i].tableDatas))
|
||||||
|
sheetnames.push(tableJson[i].sheetName)
|
||||||
|
}
|
||||||
|
console.log(dataArr, 9191)
|
||||||
|
excel.export_json_to_excel2({
|
||||||
|
header: tHeader,
|
||||||
|
data: dataArr,
|
||||||
|
sheetname: sheetnames,
|
||||||
|
filename: filenames,
|
||||||
|
autoWidth: autowidth,
|
||||||
|
bookType: bookTypes
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 数据过滤,时间过滤
|
||||||
|
function formatJson(filterVal, jsonData) {
|
||||||
|
return jsonData.map(v =>
|
||||||
|
filterVal.map(j => {
|
||||||
|
if (j === 'timestamp') {
|
||||||
|
return parseTime(v[j])
|
||||||
|
} else {
|
||||||
|
return v[j]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
361
src/utils/tool.js
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
// 数组去重
|
||||||
|
export function arrayUnique(arr) {
|
||||||
|
arr.filter((element, index, arr) => {
|
||||||
|
return arr.indexOf(element) === index
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数组去重,数组中值是对象
|
||||||
|
export function arrayUnique2(arr, name) {
|
||||||
|
const hash = {}
|
||||||
|
return arr.reduce(function (item, next) {
|
||||||
|
hash[next[name]] ? '' : hash[next[name]] = true && item.push(next)
|
||||||
|
return item
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间转时间戳
|
||||||
|
export function timeToStamp(time) {
|
||||||
|
const date = new Date(time)
|
||||||
|
return date.getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数组的深度拷贝
|
||||||
|
export function arrDeepCopy(arr) {
|
||||||
|
const newArr = []
|
||||||
|
for (const prop in arr) newArr[prop] = typeof arr[prop] === 'object' ? arrDeepCopy(arr[prop]) : arr[prop]
|
||||||
|
return newArr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对象的深度拷贝
|
||||||
|
export function objDeepCopy(obj) {
|
||||||
|
const newObj = {}
|
||||||
|
for (const prop in obj) newObj[prop] = typeof obj[prop] === 'object' ? objDeepCopy(obj[prop]) : obj[prop]
|
||||||
|
return newObj
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeroFill(i) {
|
||||||
|
if (i >= 0 && i <= 9) {
|
||||||
|
return '0' + i
|
||||||
|
} else {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前时间(yyyy-mm-dd hh:mm:ss)
|
||||||
|
export function getNowTime() {
|
||||||
|
const date = new Date()
|
||||||
|
const month = zeroFill(date.getMonth() + 1)
|
||||||
|
const day = zeroFill(date.getDate())
|
||||||
|
const hour = zeroFill(date.getHours())
|
||||||
|
const minute = zeroFill(date.getMinutes())
|
||||||
|
const second = zeroFill(date.getSeconds())
|
||||||
|
const time = date.getFullYear() + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNowTime2() {
|
||||||
|
const date66 = new Date()
|
||||||
|
const month66 = zeroFill(date66.getMonth() + 1)
|
||||||
|
const day66 = zeroFill(date66.getDate())
|
||||||
|
const date = date66.getFullYear() + '-' + month66 + '-' + day66
|
||||||
|
// var date = '2019-3-31'
|
||||||
|
var arr = date.split('-')
|
||||||
|
var year = arr[0] // 获取当前日期的年份
|
||||||
|
var month = arr[1] // 获取当前日期的月份
|
||||||
|
var day = arr[2] // 获取当前日期的日
|
||||||
|
// var days = new Date(year, month, 0)
|
||||||
|
// days = days.getDate() // 获取当前日期中月的天数
|
||||||
|
var year2 = year
|
||||||
|
var month2 = parseInt(month) - 1
|
||||||
|
if (month2 === 0) {
|
||||||
|
year2 = parseInt(year2) - 1
|
||||||
|
month2 = 12
|
||||||
|
}
|
||||||
|
var day2 = day
|
||||||
|
var days2 = new Date(year2, month2, 0)
|
||||||
|
days2 = days2.getDate()
|
||||||
|
if (day2 > days2) {
|
||||||
|
day2 = days2
|
||||||
|
}
|
||||||
|
if (month2 < 10) {
|
||||||
|
month2 = '0' + month2
|
||||||
|
}
|
||||||
|
var t2 = year2 + '-' + month2 + '-' + day2
|
||||||
|
return t2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前时间(yyyy-mm-dd)
|
||||||
|
export function getNowShotTime() {
|
||||||
|
const date = new Date()
|
||||||
|
const month = zeroFill(date.getMonth() + 1)
|
||||||
|
const day = zeroFill(date.getDate())
|
||||||
|
const time = date.getFullYear() + '-' + month + '-' + day
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前时间(yyyy/mm/dd)
|
||||||
|
export function getNowShotTime1() {
|
||||||
|
const date = new Date()
|
||||||
|
const month = zeroFill(date.getMonth() + 1)
|
||||||
|
const day = zeroFill(date.getDate())
|
||||||
|
const time = date.getFullYear() + '/' + month + '/' + day
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前0晨
|
||||||
|
export function getNowTime1() {
|
||||||
|
const date = new Date()
|
||||||
|
const month = zeroFill(date.getMonth() + 1)
|
||||||
|
const year = zeroFill(date.getFullYear())
|
||||||
|
const day = zeroFill(date.getDate())
|
||||||
|
let hour = zeroFill(date.getHours())
|
||||||
|
hour = hour < 10 ? ('0' + hour) : hour
|
||||||
|
let minute = zeroFill(date.getMinutes())
|
||||||
|
minute = minute < 10 ? ('0' + minute) : minute
|
||||||
|
let second = zeroFill(date.getSeconds())
|
||||||
|
second = second < 10 ? ('0' + second) : second
|
||||||
|
// const hour = '00'
|
||||||
|
// const minute = '00'
|
||||||
|
// const second = '00'
|
||||||
|
const time = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNowTime3(date) {
|
||||||
|
const month = zeroFill2(date.getMonth() + 1)
|
||||||
|
const day = zeroFill(date.getDate())
|
||||||
|
return date.getFullYear() + '-' + month + '-' + day
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeroFill2(i) {
|
||||||
|
if (i >= 0 && i <= 9) {
|
||||||
|
if (i === 0) {
|
||||||
|
return '01'
|
||||||
|
} else {
|
||||||
|
return '0' + i
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工时统计上个月26-本月25
|
||||||
|
export function getTime26() {
|
||||||
|
const date = new Date()
|
||||||
|
const day = date.getDate()
|
||||||
|
if (day >= 25) {
|
||||||
|
var month = zeroFill(date.getMonth() + 1)
|
||||||
|
if (month === 0) {
|
||||||
|
month = 12
|
||||||
|
const day = 26
|
||||||
|
const time = date.getFullYear() - 1 + '-' + month + '-' + day
|
||||||
|
return time
|
||||||
|
} else {
|
||||||
|
const day = 26
|
||||||
|
const time = date.getFullYear() + '-' + month + '-' + day
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var month2 = zeroFill(date.getMonth() + 1) - 1
|
||||||
|
if (month2 === 0) {
|
||||||
|
month2 = 12
|
||||||
|
const day = 26
|
||||||
|
const time = date.getFullYear() - 1 + '-' + month2 + '-' + day
|
||||||
|
return time
|
||||||
|
} else {
|
||||||
|
const day = 26
|
||||||
|
const time = date.getFullYear() + '-' + month2 + '-' + day
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTime25() {
|
||||||
|
const date = new Date()
|
||||||
|
const day = date.getDate()
|
||||||
|
if (day >= 25) {
|
||||||
|
const month = zeroFill(date.getMonth() + 2)
|
||||||
|
const day2 = 25
|
||||||
|
const time = date.getFullYear() + '-' + month + '-' + day2
|
||||||
|
return time
|
||||||
|
} else {
|
||||||
|
const month = zeroFill(date.getMonth() + 1)
|
||||||
|
const day2 = 25
|
||||||
|
const time = date.getFullYear() + '-' + month + '-' + day2
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断类型
|
||||||
|
export function type(target) {
|
||||||
|
const ret = typeof (target)
|
||||||
|
const template = {
|
||||||
|
'[object Array]': 'array',
|
||||||
|
'[object Object]': 'object',
|
||||||
|
'[object String]': 'string - object',
|
||||||
|
'[object Number]': 'Number - object',
|
||||||
|
'[object Boolean]': 'Boolean - object'
|
||||||
|
}
|
||||||
|
if (target === null) {
|
||||||
|
return 'null'
|
||||||
|
}
|
||||||
|
if (ret === 'object') {
|
||||||
|
const str = Object.prototype.toString.call(target)
|
||||||
|
return template[str]
|
||||||
|
} else {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SectionToChinese(section) {
|
||||||
|
const chnNumChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
|
||||||
|
const chnUnitChar = ['', '十', '百', '千']
|
||||||
|
let strIns = ''
|
||||||
|
let chnStr = ''
|
||||||
|
let unitPos = 0
|
||||||
|
let zero = true
|
||||||
|
while (section > 0) {
|
||||||
|
const v = section % 10
|
||||||
|
if (v === 0) {
|
||||||
|
if (!zero) {
|
||||||
|
zero = true
|
||||||
|
chnStr = chnNumChar[v] + chnStr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
zero = false
|
||||||
|
strIns = chnNumChar[v]
|
||||||
|
strIns += chnUnitChar[unitPos]
|
||||||
|
chnStr = strIns + chnStr
|
||||||
|
}
|
||||||
|
unitPos++
|
||||||
|
section = Math.floor(section / 10)
|
||||||
|
}
|
||||||
|
return chnStr
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sleep(time) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(resolve, time)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function groupBy(datas, keys) {
|
||||||
|
const list = datas || []
|
||||||
|
const groups = []
|
||||||
|
const result = []
|
||||||
|
list.forEach(v => {
|
||||||
|
const key = {}
|
||||||
|
keys.forEach(k => {
|
||||||
|
key[k] = v[k]
|
||||||
|
})
|
||||||
|
let group = groups.find(v => {
|
||||||
|
return v._key === JSON.stringify(key)
|
||||||
|
})
|
||||||
|
if (!group) {
|
||||||
|
group = {
|
||||||
|
_key: JSON.stringify(key),
|
||||||
|
key: key
|
||||||
|
}
|
||||||
|
groups.push(group)
|
||||||
|
}
|
||||||
|
group.data1 = group.data1 || 0
|
||||||
|
group.key.数量 = group.data1 += v.数量
|
||||||
|
group.data2 = group.data2 || ''
|
||||||
|
group.key.机床图号_组号 = group.data2 += (v.机床图号 || '') + '-' + (v.组号 ? v.组号.split('组')[0] : '') + '<br/>'
|
||||||
|
group.data3 = group.data3 || 0
|
||||||
|
group.key.合计 = group.data3 += v.合计
|
||||||
|
})
|
||||||
|
groups.map(v => {
|
||||||
|
result.push(v.key)
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除js浮点加法bug
|
||||||
|
export function numAdd(arg1, arg2) {
|
||||||
|
var r1, r2, m
|
||||||
|
try {
|
||||||
|
r1 = arg1.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
r1 = 0
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
r2 = arg2.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
r2 = 0
|
||||||
|
}
|
||||||
|
m = Math.pow(10, Math.max(r1, r2))
|
||||||
|
return (arg1 * m + arg2 * m) / m
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除js浮点减法bug
|
||||||
|
export function numSubtract(arg1, arg2) {
|
||||||
|
var r1, r2, m, n
|
||||||
|
try {
|
||||||
|
r1 = arg1.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
r1 = 0
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
r2 = arg2.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
r2 = 0
|
||||||
|
}
|
||||||
|
m = Math.pow(10, Math.max(r1, r2))
|
||||||
|
n = (r1 >= r2) ? r1 : r2
|
||||||
|
return ((arg1 * m - arg2 * m) / m).toFixed(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除js浮点乘法bug
|
||||||
|
export function numMultiply(arg1, arg2) {
|
||||||
|
var m = 0
|
||||||
|
var s1 = arg1.toString()
|
||||||
|
var s2 = arg2.toString()
|
||||||
|
try {
|
||||||
|
m += s1.split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
m += s2.split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除js浮点除法bug
|
||||||
|
export function numDivide(arg1, arg2) {
|
||||||
|
var t1 = 0
|
||||||
|
var t2 = 0
|
||||||
|
var r1, r2
|
||||||
|
try {
|
||||||
|
t1 = arg1.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
t2 = arg2.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
r1 = Math.Number(arg1.toString().replace('.', ''))
|
||||||
|
r2 = Math.Number(arg2.toString().replace('.', ''))
|
||||||
|
return (r1 / r2) * Math.pow(10, t2 - t1)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function base64ImgtoFile(dataurl, filename = 'file') {
|
||||||
|
const arr = dataurl.split(',')
|
||||||
|
const mime = arr[0].match(/:(.*?);/)[1]
|
||||||
|
const suffix = mime.split('/')[1]
|
||||||
|
const bstr = atob(arr[1])
|
||||||
|
let n = bstr.length
|
||||||
|
const u8arr = new Uint8Array(n)
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n)
|
||||||
|
}
|
||||||
|
return new File([u8arr], `${filename}.${suffix}`, {
|
||||||
|
type: mime
|
||||||
|
})
|
||||||
|
}
|
||||||
32
src/utils/validate.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Created by jiachenpan on 16/11/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function isvalidUsername(str) {
|
||||||
|
const user = /^[0-9]{5,7}$/
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 合法uri*/
|
||||||
|
export function validateURL(textval) {
|
||||||
|
const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
|
||||||
|
return urlregex.test(textval)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 小写字母*/
|
||||||
|
export function validateLowerCase(str) {
|
||||||
|
const reg = /^[a-z]+$/
|
||||||
|
return reg.test(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大写字母*/
|
||||||
|
export function validateUpperCase(str) {
|
||||||
|
const reg = /^[A-Z]+$/
|
||||||
|
return reg.test(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大小写字母*/
|
||||||
|
export function validatAlphabets(str) {
|
||||||
|
const reg = /^[A-Za-z]+$/
|
||||||
|
return reg.test(str)
|
||||||
|
}
|
||||||
248
src/views/404.vue
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wscn-http404-container">
|
||||||
|
<div class="wscn-http404">
|
||||||
|
<div class="pic-404">
|
||||||
|
<img
|
||||||
|
class="pic-404__parent"
|
||||||
|
src="@/assets/404_images/404.png"
|
||||||
|
alt="404"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="pic-404__child left"
|
||||||
|
src="@/assets/404_images/404_cloud.png"
|
||||||
|
alt="404"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="pic-404__child mid"
|
||||||
|
src="@/assets/404_images/404_cloud.png"
|
||||||
|
alt="404"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
class="pic-404__child right"
|
||||||
|
src="@/assets/404_images/404_cloud.png"
|
||||||
|
alt="404"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="bullshit">
|
||||||
|
<div class="bullshit__oops">OOPS!</div>
|
||||||
|
<div class="bullshit__info">
|
||||||
|
版权所有
|
||||||
|
<a class="link-type" href="https://wallstreetcn.com" target="_blank"
|
||||||
|
>华尔街见闻</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="bullshit__headline">{{ message }}</div>
|
||||||
|
<div class="bullshit__info">
|
||||||
|
请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告
|
||||||
|
</div>
|
||||||
|
<a href="/" class="bullshit__return-home">返回首页</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Page404",
|
||||||
|
computed: {
|
||||||
|
message() {
|
||||||
|
return "网管说这个页面你不能进......";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.wscn-http404-container {
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
.wscn-http404 {
|
||||||
|
position: relative;
|
||||||
|
width: 1200px;
|
||||||
|
padding: 0 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
.pic-404 {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
width: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
&__parent {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&__child {
|
||||||
|
position: absolute;
|
||||||
|
&.left {
|
||||||
|
width: 80px;
|
||||||
|
top: 17px;
|
||||||
|
left: 220px;
|
||||||
|
opacity: 0;
|
||||||
|
animation-name: cloudLeft;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
&.mid {
|
||||||
|
width: 46px;
|
||||||
|
top: 10px;
|
||||||
|
left: 420px;
|
||||||
|
opacity: 0;
|
||||||
|
animation-name: cloudMid;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-delay: 1.2s;
|
||||||
|
}
|
||||||
|
&.right {
|
||||||
|
width: 62px;
|
||||||
|
top: 100px;
|
||||||
|
left: 500px;
|
||||||
|
opacity: 0;
|
||||||
|
animation-name: cloudRight;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
@keyframes cloudLeft {
|
||||||
|
0% {
|
||||||
|
top: 17px;
|
||||||
|
left: 220px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
top: 33px;
|
||||||
|
left: 188px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
top: 81px;
|
||||||
|
left: 92px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
top: 97px;
|
||||||
|
left: 60px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes cloudMid {
|
||||||
|
0% {
|
||||||
|
top: 10px;
|
||||||
|
left: 420px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
top: 40px;
|
||||||
|
left: 360px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
top: 130px;
|
||||||
|
left: 180px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
top: 160px;
|
||||||
|
left: 120px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes cloudRight {
|
||||||
|
0% {
|
||||||
|
top: 100px;
|
||||||
|
left: 500px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
top: 120px;
|
||||||
|
left: 460px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
top: 180px;
|
||||||
|
left: 340px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
top: 200px;
|
||||||
|
left: 300px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bullshit {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
width: 300px;
|
||||||
|
padding: 30px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
&__oops {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 40px;
|
||||||
|
color: #1482f0;
|
||||||
|
opacity: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
&__headline {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #222;
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-delay: 0.1s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
&__info {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 21px;
|
||||||
|
color: grey;
|
||||||
|
opacity: 0;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
&__return-home {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
width: 110px;
|
||||||
|
height: 36px;
|
||||||
|
background: #1482f0;
|
||||||
|
border-radius: 100px;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
opacity: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
@keyframes slideUp {
|
||||||
|
0% {
|
||||||
|
transform: translateY(60px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
686
src/views/ProductionTaskDashboard.vue
Normal file
@@ -0,0 +1,686 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="production-dashboard"
|
||||||
|
:style="{ width: `${style.width}px`, height: `${style.height}px`, transform: style.transform }"
|
||||||
|
>
|
||||||
|
<div class="dashboard-shell">
|
||||||
|
<header class="dashboard-header">
|
||||||
|
<div class="brand">
|
||||||
|
<img class="brand-logo" :src="clientLogoUrl" alt="RENLY" />
|
||||||
|
</div>
|
||||||
|
<h1>生产任务综合看板</h1>
|
||||||
|
<div class="current-time">{{ currentTime }}</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="dashboard-content">
|
||||||
|
<section class="summary-grid first-row">
|
||||||
|
<article class="panel kpi-card rate-card">
|
||||||
|
<div class="ring-wrap">
|
||||||
|
<div class="progress-ring">{{complete.completeset}}%</div>
|
||||||
|
</div>
|
||||||
|
<div class="rate-info">
|
||||||
|
<div class="card-title">按期齐套率</div>
|
||||||
|
<div class="mini-row"><span>未齐套数</span><b>{{ complete.untasknum }}</b></div>
|
||||||
|
<div class="mini-row"><span>应齐套数</span><b>{{ complete.tasknum }}</b></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article v-for="item in topStats" :key="item.title" class="panel kpi-card top-stat-card">
|
||||||
|
<div class="icon-slot">
|
||||||
|
<img :src="item.icon" :alt="item.title" />
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="card-title">{{ item.title }}</div>
|
||||||
|
<div class="stat-number">{{ item.value }}</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="summary-grid second-row">
|
||||||
|
<article class="panel status-card split-number-card">
|
||||||
|
<div class="icon-slot wide">
|
||||||
|
<img :src="svgIcons.purchaseDelay" alt="采购未及时到货" />
|
||||||
|
</div>
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="card-title">采购未及时到货</div>
|
||||||
|
<div class="split-number"><em>8</em><span>/46</span></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="panel status-card">
|
||||||
|
<div class="icon-slot gear">
|
||||||
|
<img :src="svgIcons.machineDelay" alt="机加工未及时开工" />
|
||||||
|
</div>
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="card-title">机加未及时开工</div>
|
||||||
|
<div class="orange-number">{{ notintimenum }}</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="panel status-card rate-small-card">
|
||||||
|
<div class="small-ring">87.5%</div>
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="card-title">质检及时率</div>
|
||||||
|
<div class="mini-row"><span>已检数</span><b>84</b></div>
|
||||||
|
<div class="mini-row"><span>未检数</span><b>12</b></div>
|
||||||
|
<div class="mini-row"><span>全部待检数</span><b>96</b></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="panel status-card">
|
||||||
|
<div class="icon-slot house">
|
||||||
|
<img :src="svgIcons.stockDelay" alt="未及时入库" />
|
||||||
|
</div>
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="card-title">未及时入库</div>
|
||||||
|
<div class="orange-number">11</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="panel status-card urgent-status-card">
|
||||||
|
<div class="icon-slot alarm">
|
||||||
|
<img :src="svgIcons.urgent" alt="加急件" />
|
||||||
|
</div>
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="card-title">加急件</div>
|
||||||
|
<div class="red-number">{{ Urgentnum }}</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="table-area">
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">未按期齐套任务</div>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th v-for="column in leftColumns" :key="column.key">{{ column.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, index) in kitRows" :key="`${row.salesOrder}-${index}`">
|
||||||
|
<td v-for="column in leftColumns" :key="column.key">{{ row[column.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">加急件</div>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th v-for="column in urgentColumns" :key="column.key">{{ column.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, index) in urgentRows" :key="`${row.salesOrder}-${index}`">
|
||||||
|
<td v-for="column in urgentColumns" :key="column.key">{{ row[column.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||||
|
const CreateData = inject('CreateData')
|
||||||
|
const ExecDatabase = inject('ExecDatabase')
|
||||||
|
const tableData = ref([])
|
||||||
|
const now = ref(new Date('2026-05-06 10:10:10'))
|
||||||
|
let timer = null
|
||||||
|
let resizeHandler = null
|
||||||
|
let Urgentnum = null
|
||||||
|
let notintimenum = null
|
||||||
|
let complete = reactive({
|
||||||
|
completeset:100,
|
||||||
|
tasknum:0,
|
||||||
|
untasknum:0
|
||||||
|
})
|
||||||
|
const style = reactive({
|
||||||
|
width: 1920,
|
||||||
|
height: 1080,
|
||||||
|
transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)'
|
||||||
|
})
|
||||||
|
const setScale = () => {
|
||||||
|
const scale = getScale()
|
||||||
|
style.transform = 'scaleY(' + scale.y + ') scaleX(' + scale.x + ') translate(-50%, -50%)'
|
||||||
|
}
|
||||||
|
const getScale = () => {
|
||||||
|
const w = window.innerWidth / style.width
|
||||||
|
const h = window.innerHeight / style.height
|
||||||
|
return { x: w, y: h }
|
||||||
|
}
|
||||||
|
const pad = (value) => String(value).padStart(2, '0')
|
||||||
|
|
||||||
|
const currentTime = computed(() => {
|
||||||
|
const date = now.value
|
||||||
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const clientLogoUrl = './public/ClientLogo.png'
|
||||||
|
const svgIcons = {
|
||||||
|
pendingSchedule: './public/svg/待排产任务.svg',
|
||||||
|
salesOrder: './public/svg/待处理销售订单.svg',
|
||||||
|
bom: './public/svg/工艺BOM待完成.svg',
|
||||||
|
purchaseDelay: './public/svg/采购未及时到货.svg',
|
||||||
|
machineDelay: './public/svg/机加未及时开工.svg',
|
||||||
|
stockDelay: './public/svg/未及时入库.svg',
|
||||||
|
urgent: './public/svg/加急件.svg',
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
startClock()
|
||||||
|
setScale()
|
||||||
|
resizeHandler = () => {
|
||||||
|
setScale()
|
||||||
|
}
|
||||||
|
window.addEventListener('resize', resizeHandler)
|
||||||
|
searchDashboardData()
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
destroyDashboard()
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchDashboardData = () => {
|
||||||
|
searchCompleteSetData()
|
||||||
|
searchNoProcessData()
|
||||||
|
searchUrgentData()
|
||||||
|
searchUnCompleteData()
|
||||||
|
searchNotInTimeData()
|
||||||
|
|
||||||
|
}
|
||||||
|
const searchCompleteSetData = () => {
|
||||||
|
queryDashboardData('生产任务综合看板_按期齐套率', applyCompleteSetData)
|
||||||
|
}
|
||||||
|
const searchNoProcessData = () => {
|
||||||
|
queryDashboardData('生产任务综合看板_未排产无工艺查询', applyNoProcessData)
|
||||||
|
}
|
||||||
|
const searchUrgentData = () => {
|
||||||
|
queryDashboardData('生产任务综合看板_加急任务查询', applyUrgentData)
|
||||||
|
}
|
||||||
|
const searchNotInTimeData = () => {
|
||||||
|
queryDashboardData('生产任务综合看板_未及时开工查询', applyNotInTimeData)
|
||||||
|
}
|
||||||
|
const searchUnCompleteData = () => {
|
||||||
|
queryDashboardData('生产任务综合看板_未按期齐套任务', applyUnCompleteData)
|
||||||
|
}
|
||||||
|
const applyCompleteSetData = rows => {
|
||||||
|
complete.completeset = rows[0].齐套率
|
||||||
|
complete.untasknum = rows[0].未齐套任务数
|
||||||
|
complete.tasknum = rows[0].总任务数
|
||||||
|
}
|
||||||
|
const applyNoProcessData = rows => {
|
||||||
|
topStats[0].value = rows[0].未排产任务数
|
||||||
|
topStats[2].value = rows[0].无生产工艺计划数
|
||||||
|
}
|
||||||
|
const applyNotInTimeData = rows => {
|
||||||
|
notintimenum = rows[0].未及时开工总数
|
||||||
|
}
|
||||||
|
const applyUrgentData = rows => {
|
||||||
|
urgentRows = rows.map( item => ({
|
||||||
|
salesOrder: item.合同号,
|
||||||
|
productionOrder: item.订单编号,
|
||||||
|
productName: item.物料描述,
|
||||||
|
productCode: item.物料编号,
|
||||||
|
property: item.自制件属性,
|
||||||
|
planQty: item.计划数量,
|
||||||
|
releaseDate: item.下达日期,
|
||||||
|
startDate: item.计划开始,
|
||||||
|
finishDate: item.计划完成,
|
||||||
|
}))
|
||||||
|
Urgentnum = rows[0].加急订单总数
|
||||||
|
}
|
||||||
|
const applyUnCompleteData = rows => {
|
||||||
|
kitRows = rows.map( item => ({
|
||||||
|
productionOrder: item.订单编号,
|
||||||
|
productName: item.物料描述,
|
||||||
|
productCode: item.物料编号,
|
||||||
|
property: item.自制件属性,
|
||||||
|
planQty: item.计划数量,
|
||||||
|
releaseDate: item.下达日期,
|
||||||
|
startDate: item.计划开始,
|
||||||
|
finishDate: item.计划完成,
|
||||||
|
planNo: item.合同号,
|
||||||
|
stockQty: '0',
|
||||||
|
kitDate: item.计划开始,
|
||||||
|
pickStatus: item.拣配状态 == 1 ?'已拣配':'未拣配',
|
||||||
|
issueStatus: item.发料状态 == 2 ?'发料缺件':'未发料',
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
const queryDashboardData = (procedureName, applyData) => {
|
||||||
|
let data
|
||||||
|
data = CreateData('11', procedureName, [])
|
||||||
|
ExecDatabase(data).then(response => {
|
||||||
|
const rows = getResponseRows(response)
|
||||||
|
tableData.value = rows
|
||||||
|
applyData(rows)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getResponseRows = response => {
|
||||||
|
if (response && Array.isArray(response.data)) {
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
if (response && response.data && Array.isArray(response.data.result)) {
|
||||||
|
return response.data.result
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const startClock = () => {
|
||||||
|
timer = window.setInterval(() => {
|
||||||
|
now.value = new Date(now.value.getTime() + 1000)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const destroyDashboard = () => {
|
||||||
|
if (timer) {
|
||||||
|
window.clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
if (resizeHandler) {
|
||||||
|
window.removeEventListener('resize', resizeHandler)
|
||||||
|
resizeHandler = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const topStats = [
|
||||||
|
{ title: '待排产任务', value: 18, icon: svgIcons.pendingSchedule },
|
||||||
|
{ title: '待处理销售订单', value: 12, icon: svgIcons.salesOrder },
|
||||||
|
{ title: '工艺BOM待完成', value: 9, icon: svgIcons.bom },
|
||||||
|
]
|
||||||
|
|
||||||
|
const leftColumns = [
|
||||||
|
{ label: '销售订单', key: 'salesOrder' },
|
||||||
|
{ label: '计划号', key: 'planNo' },
|
||||||
|
{ label: '生产订单', key: 'productionOrder' },
|
||||||
|
{ label: '产品名称', key: 'productName' },
|
||||||
|
{ label: '产品编码', key: 'productCode' },
|
||||||
|
{ label: '计划数', key: 'planQty' },
|
||||||
|
{ label: '入库数', key: 'stockQty' },
|
||||||
|
{ label: '预计齐套', key: 'kitDate' },
|
||||||
|
{ label: '拣配状态', key: 'pickStatus' },
|
||||||
|
{ label: '发料状态', key: 'issueStatus' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const urgentColumns = [
|
||||||
|
{ label: '销售订单', key: 'salesOrder' },
|
||||||
|
{ label: '生产订单', key: 'productionOrder' },
|
||||||
|
{ label: '产品名称', key: 'productName' },
|
||||||
|
{ label: '产品编码', key: 'productCode' },
|
||||||
|
{ label: '属性', key: 'property' },
|
||||||
|
{ label: '计划数', key: 'planQty' },
|
||||||
|
{ label: '下达日期', key: 'releaseDate' },
|
||||||
|
{ label: '计划开始', key: 'startDate' },
|
||||||
|
{ label: '计划完成', key: 'finishDate' },
|
||||||
|
]
|
||||||
|
|
||||||
|
let kitRows = Array.from({ length: 9 }, () => ({
|
||||||
|
salesOrder: 'SH260014',
|
||||||
|
planNo: '54023',
|
||||||
|
productionOrder: '1075',
|
||||||
|
productName: '垫铁+行程开关部装',
|
||||||
|
productCode: '213-YGLQ-DF150V1.2.18',
|
||||||
|
planQty: '1',
|
||||||
|
stockQty: '0',
|
||||||
|
kitDate: '2026-01-21',
|
||||||
|
pickStatus: '已拣配',
|
||||||
|
issueStatus: '未发料',
|
||||||
|
}))
|
||||||
|
|
||||||
|
let urgentRows = Array.from({ length: 10 }, () => ({
|
||||||
|
salesOrder: 'SH260014',
|
||||||
|
productionOrder: '1075',
|
||||||
|
productName: '垫铁+行程开关部装',
|
||||||
|
productCode: '213-YGLQ-DF150V1.2.18',
|
||||||
|
property: '机加',
|
||||||
|
planQty: '1',
|
||||||
|
releaseDate: '2026-01-21',
|
||||||
|
startDate: '2026-01-21',
|
||||||
|
finishDate: '2026-01-21',
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.production-dashboard {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 1920px;
|
||||||
|
height: 1080px;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transition: 0.3s;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #f3f9ff;
|
||||||
|
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||||||
|
background-color: #07001f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-shell {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0px 15px 15px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-header {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 336px 1fr 442px;
|
||||||
|
align-items: center;
|
||||||
|
height: 84px;
|
||||||
|
border-bottom: 1px solid rgba(21, 162, 240, 0.62);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-slot {
|
||||||
|
width: 230px;
|
||||||
|
height: 45px;
|
||||||
|
margin-left: 8px;
|
||||||
|
border: 1px solid rgba(100, 196, 255, 0.48);
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
box-shadow: inset 0 0 14px rgba(83, 181, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 55px;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-time {
|
||||||
|
padding-right: 38px;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #eef7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 145px 145px 1fr;
|
||||||
|
gap: 17px;
|
||||||
|
height: calc(100% - 84px);
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first-row {
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.second-row {
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid rgba(29, 153, 229, 0.78);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 0 18px rgba(27, 130, 214, 0.24),
|
||||||
|
0 0 10px rgba(1, 132, 220, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: -1px;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
background:
|
||||||
|
linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left top / 14px 2px no-repeat,
|
||||||
|
linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) left top / 2px 14px no-repeat,
|
||||||
|
linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right top / 14px 2px no-repeat,
|
||||||
|
linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) right top / 2px 14px no-repeat,
|
||||||
|
linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left bottom / 14px 2px no-repeat,
|
||||||
|
linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) left bottom / 2px 14px no-repeat,
|
||||||
|
linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right bottom / 14px 2px no-repeat,
|
||||||
|
linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) right bottom / 2px 14px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel::after {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: inset 0 0 12px rgba(43, 216, 255, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card,
|
||||||
|
.status-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 70px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title,
|
||||||
|
.panel-caption {
|
||||||
|
color: #8cd3ff;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate-card {
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding-left: 42px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ring-wrap,
|
||||||
|
.progress-ring,
|
||||||
|
.small-ring {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-ring {
|
||||||
|
width: 110px;
|
||||||
|
height: 110px;
|
||||||
|
min-width: 58px;
|
||||||
|
min-height: 58px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 900;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle, #08234f 53%, transparent 55%),
|
||||||
|
conic-gradient(#1ce6ff 0deg 333deg, rgba(47, 84, 131, 0.8) 333deg 360deg);
|
||||||
|
box-shadow: 0 0 15px rgba(0, 208, 255, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate-info {
|
||||||
|
width: 154px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #e7f6ff;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-row b {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-slot {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 110px;
|
||||||
|
height: 110px;
|
||||||
|
min-width: 60px;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-slot img {
|
||||||
|
width: 76%;
|
||||||
|
height: 76%;
|
||||||
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-slot.wide {
|
||||||
|
width: 108px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-slot.gear,
|
||||||
|
.icon-slot.house,
|
||||||
|
.icon-slot.alarm {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.stat-info,
|
||||||
|
.status-info {
|
||||||
|
min-width: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
margin-top: 5px;
|
||||||
|
font-size: 58px;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 0 8px rgba(255, 255, 255, 0.36);
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-number,
|
||||||
|
.orange-number,
|
||||||
|
.red-number {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 52px;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-number em {
|
||||||
|
color: #ff2026;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-number span {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange-number {
|
||||||
|
color: #ff9e14;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red-number {
|
||||||
|
color: #ff242a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-ring {
|
||||||
|
width: 110px;
|
||||||
|
height: 110px;
|
||||||
|
min-width: 56px;
|
||||||
|
min-height: 56px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 900;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle, #08234f 52%, transparent 54%),
|
||||||
|
conic-gradient(#19d4ff 0deg 315deg, rgba(47, 84, 131, 0.85) 315deg 360deg);
|
||||||
|
box-shadow: 0 0 13px rgba(0, 208, 255, 0.54);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-area {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 17px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-caption {
|
||||||
|
height: 38px;
|
||||||
|
line-height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 9px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid rgba(53, 149, 217, 0.48);
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: #f1f8ff;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.18;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
height: 38px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 200;
|
||||||
|
background: rgba(12, 68, 119, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
width: 300px;
|
||||||
|
height: 72px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-logo {
|
||||||
|
width: 224px;
|
||||||
|
height: 58px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: left center;
|
||||||
|
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.28));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
898
src/views/TestTaskDashboard.vue
Normal file
@@ -0,0 +1,898 @@
|
|||||||
|
<template>
|
||||||
|
<div class="assembly-dashboard"
|
||||||
|
:style="{ width: `${style.width}px`, height: `${style.height}px`, transform: style.transform }">
|
||||||
|
<div class="dashboard-shell">
|
||||||
|
<header class="dashboard-header">
|
||||||
|
<div class="brand"><img class="brand-logo" :src="clientLogoUrl" alt="RENLY" /></div>
|
||||||
|
<h1>质检测试任务看板</h1>
|
||||||
|
<div class="current-time">{{ currentTime }}</div>
|
||||||
|
</header>
|
||||||
|
<main class="dashboard-content">
|
||||||
|
<section class="summary-row">
|
||||||
|
<article v-for="item in summaryCards" :key="item.title" class="panel summary-card" :class="item.className">
|
||||||
|
<div>
|
||||||
|
<div class="summary-title">{{ item.title }}</div>
|
||||||
|
<div v-if="item.metrics" class="summary-metrics">
|
||||||
|
<div v-for="metric in item.metrics" :key="metric.label" class="summary-metric">
|
||||||
|
<span>{{ metric.label }}</span>
|
||||||
|
<b>{{ metric.value }}</b>
|
||||||
|
<small>{{ item.unit }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="summary-body">
|
||||||
|
<img class="summary-icon" :src="item.icon" :alt="item.title" />
|
||||||
|
<div class="summary-value" :class="item.valueClass"><span>{{ item.value }}</span><small
|
||||||
|
v-if="item.unit">{{ item.unit }}</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.people" class="people-list">
|
||||||
|
<div v-for="person in item.people" :key="person.name"><span>{{ person.name }}</span><b>{{ person.group
|
||||||
|
}}</b></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
<section class="main-grid">
|
||||||
|
<div class="left-column">
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">发货状态列表</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in taskColumns2" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="deliveryScrollStyle">
|
||||||
|
<tr v-for="(r, i) in deliveryDisplayRows" :key="i">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in taskColumns2" :key="c.key">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="panel table-panel task-panel">
|
||||||
|
<div class="panel-caption">测试装配任务列表</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in taskColumns" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="taskScrollStyle">
|
||||||
|
<tr v-for="(r, i) in taskDisplayRows" :key="i" :class="{ warning: r.urgentQty > 0 }">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in taskColumns" :key="c.key">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<aside class="right-column">
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">在制任务缺件明细</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in materialColumns" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="materialScrollStyle">
|
||||||
|
<tr v-for="(r, i) in materialDisplayRows" :key="i">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in materialColumns" :key="c.key">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">已完工任务</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in doneColumns" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="doneScrollStyle">
|
||||||
|
<tr v-for="(r, i) in doneDisplayRows" :key="i">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in doneColumns" :key="c.key" :class="c.key === 'passRate' ? r.rateClass : ''">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||||
|
const CreateData = inject('CreateData')
|
||||||
|
const ExecDatabase = inject('ExecDatabase')
|
||||||
|
defineOptions({ name: 'AssemblyCenterTaskDashboard' })
|
||||||
|
|
||||||
|
const now = ref(new Date('2026-05-06 10:10:10'))
|
||||||
|
let timer = null
|
||||||
|
let resizeHandler = null
|
||||||
|
let scrollTimers = []
|
||||||
|
const style = reactive({ width: 1920, height: 1080, transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)' })
|
||||||
|
const setScale = () => { const s = getScale(); style.transform = `scaleY(${s.y}) scaleX(${s.x}) translate(-50%, -50%)` }
|
||||||
|
const getScale = () => ({ x: window.innerWidth / style.width, y: window.innerHeight / style.height })
|
||||||
|
const pad = v => String(v).padStart(2, '0')
|
||||||
|
const currentTime = computed(() => `${now.value.getFullYear()}-${pad(now.value.getMonth() + 1)}-${pad(now.value.getDate())} ${pad(now.value.getHours())}:${pad(now.value.getMinutes())}:${pad(now.value.getSeconds())}`)
|
||||||
|
const clientLogoUrl = './public/ClientLogo.png'
|
||||||
|
const icon = name => `./public/svg/${name}.svg`
|
||||||
|
const tableData = ref([])
|
||||||
|
const getFirstRow = rows => {
|
||||||
|
if (Array.isArray(rows) && rows.length > 0) {
|
||||||
|
return rows[0]
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
startClock();
|
||||||
|
setScale();
|
||||||
|
resizeHandler = setScale;
|
||||||
|
window.addEventListener('resize', resizeHandler)
|
||||||
|
searchDashboardData()
|
||||||
|
startAllScroll()
|
||||||
|
setInterval(function(){
|
||||||
|
searchDashboardData()
|
||||||
|
},600000)
|
||||||
|
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) window.clearInterval(timer);
|
||||||
|
if (resizeHandler) window.removeEventListener('resize', resizeHandler)
|
||||||
|
stopAllScroll()
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============ 滚动数据 ============
|
||||||
|
const deliveryRows = ref([])
|
||||||
|
const taskRows = ref([])
|
||||||
|
const materialRows = ref([])
|
||||||
|
const doneRows = ref([])
|
||||||
|
|
||||||
|
// 每个列表的滚动偏移
|
||||||
|
const deliveryOffset = ref(0)
|
||||||
|
const taskOffset = ref(0)
|
||||||
|
const materialOffset = ref(0)
|
||||||
|
const doneOffset = ref(0)
|
||||||
|
|
||||||
|
// 可见行数
|
||||||
|
const VISIBLE_ROWS = {
|
||||||
|
delivery: 8,
|
||||||
|
task: 7,
|
||||||
|
material: 8,
|
||||||
|
done: 7
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成带序号和循环滚动的显示行
|
||||||
|
const makeDisplayRows = (rows, offset, visibleCount) => {
|
||||||
|
if (!rows || rows.length === 0) return []
|
||||||
|
const len = rows.length
|
||||||
|
const result = []
|
||||||
|
for (let i = 0; i < visibleCount; i++) {
|
||||||
|
const idx = (offset + i) % len
|
||||||
|
result.push({ ...rows[idx], _idx: idx + 1 })
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const deliveryDisplayRows = computed(() => makeDisplayRows(deliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery))
|
||||||
|
const taskDisplayRows = computed(() => makeDisplayRows(taskRows.value, taskOffset.value, VISIBLE_ROWS.task))
|
||||||
|
const materialDisplayRows = computed(() => makeDisplayRows(materialRows.value, materialOffset.value, VISIBLE_ROWS.material))
|
||||||
|
const doneDisplayRows = computed(() => makeDisplayRows(doneRows.value, doneOffset.value, VISIBLE_ROWS.done))
|
||||||
|
|
||||||
|
// 滚动样式(平滑过渡)
|
||||||
|
const deliveryScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
const taskScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
const materialScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
const doneScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
|
||||||
|
// 启动单个列表滚动
|
||||||
|
const startScroll = (rowsRef, offsetRef) => {
|
||||||
|
const id = window.setInterval(() => {
|
||||||
|
if (rowsRef.value.length > 0) {
|
||||||
|
offsetRef.value = (offsetRef.value + 1) % rowsRef.value.length
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
scrollTimers.push(id)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
const startAllScroll = () => {
|
||||||
|
startScroll(deliveryRows, deliveryOffset)
|
||||||
|
startScroll(taskRows, taskOffset)
|
||||||
|
startScroll(materialRows, materialOffset)
|
||||||
|
startScroll(doneRows, doneOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopAllScroll = () => {
|
||||||
|
scrollTimers.forEach(id => window.clearInterval(id))
|
||||||
|
scrollTimers = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 数据查询 ============
|
||||||
|
const searchDashboardData = () => {
|
||||||
|
searchTestAssemblyTaskData()
|
||||||
|
searchQualityTimelinessData()
|
||||||
|
searchDoneTaskData()
|
||||||
|
searchMaterialshortageData()
|
||||||
|
searchDeliveryData()
|
||||||
|
}
|
||||||
|
const searchTestAssemblyTaskData = () => {
|
||||||
|
const param = [
|
||||||
|
['任务类型', '全部'],
|
||||||
|
['生产订单', ''],
|
||||||
|
['销售订单', ''],
|
||||||
|
['物料名称', ''],
|
||||||
|
['物料编码', '']
|
||||||
|
]
|
||||||
|
queryDashboardData('质量管理_测试装配任务_查询', applyTestAssemblyTaskData, param)
|
||||||
|
}
|
||||||
|
const searchQualityTimelinessData = () => {
|
||||||
|
const param = [
|
||||||
|
['任务类型', '全部'],
|
||||||
|
['生产订单', ''],
|
||||||
|
['销售订单', ''],
|
||||||
|
['物料名称', ''],
|
||||||
|
['物料编码', '']
|
||||||
|
]
|
||||||
|
queryDashboardData('质量管理_测试装配任务_质检及时率', applyQualityTimelinessData, param)
|
||||||
|
}
|
||||||
|
const searchDoneTaskData = () => {
|
||||||
|
const param = [
|
||||||
|
['任务类型', '全部'],
|
||||||
|
['生产订单', ''],
|
||||||
|
['销售订单', ''],
|
||||||
|
['物料名称', ''],
|
||||||
|
['物料编码', '']
|
||||||
|
]
|
||||||
|
queryDashboardData('质量管理_测试装配任务_完成查询', applyDoneTaskData, param)
|
||||||
|
}
|
||||||
|
const searchDeliveryData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_质检发货状态查询', applyDeliveryData)
|
||||||
|
}
|
||||||
|
const searchMaterialshortageData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_在制品缺件查询', applyMaterialshortageData)
|
||||||
|
}
|
||||||
|
const applyTestAssemblyTaskData = rows => {
|
||||||
|
const sortedRows = sortTestAssemblyTaskRows(rows)
|
||||||
|
const executableRows = sortedRows.filter(isInspectionUnfinished)
|
||||||
|
const valueRows = executableRows.filter(item => getTaskType(item) === '阀类')
|
||||||
|
const systemRows = executableRows.filter(item => getTaskType(item) === '系统类')
|
||||||
|
|
||||||
|
summaryCards[0].metrics[0].value = valueRows.length
|
||||||
|
summaryCards[0].metrics[1].value = systemRows.length
|
||||||
|
summaryCards[1].value = executableRows.filter(isReworkTask).length
|
||||||
|
summaryCards[3].value = executableRows.filter(item => getNumber(item.加急总数 || item.加急数量) > 0).length
|
||||||
|
summaryCards[4].value = sortedRows.reduce((sum, item) => sum + getNumber(item.异常未关闭数 || item.测试装配异常未关闭总数), 0)
|
||||||
|
|
||||||
|
applyTaskData(sortedRows)
|
||||||
|
}
|
||||||
|
const applyQualityTimelinessData = rows => {
|
||||||
|
const row = getFirstRow(rows)
|
||||||
|
summaryCards[2].value = `${getNumber(row.质检及时率).toFixed(2)}`
|
||||||
|
}
|
||||||
|
const applyDeliveryData = rows => {
|
||||||
|
deliveryRows.value = rows.map(item => {
|
||||||
|
let requiredDate = ''
|
||||||
|
if (item.要求发货日期) {
|
||||||
|
// 如果是字符串
|
||||||
|
if (typeof item.要求发货日期 === 'string') {
|
||||||
|
requiredDate = item.要求发货日期.split(' ')[0]
|
||||||
|
}
|
||||||
|
// 如果是 Date 对象
|
||||||
|
else if (item.要求发货日期 instanceof Date) {
|
||||||
|
requiredDate = item.要求发货日期.toISOString().split('T')[0]
|
||||||
|
}
|
||||||
|
// 如果是其他格式
|
||||||
|
else {
|
||||||
|
const dateStr = String(item.要求发货日期)
|
||||||
|
requiredDate = dateStr.split(' ')[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
productionOrder: item.订单编号,
|
||||||
|
salesOrder: item.合同号 || '',
|
||||||
|
materialName: item.物料描述 || '',
|
||||||
|
materialCode: item.物料编号 || '',
|
||||||
|
completeSet: item.齐套 === 1 ? '是' : '否',
|
||||||
|
issueStatus: item.发料状态 === '1' ? '发料齐套' : (item.发料状态 === '2' ? '发料缺件' : ''),
|
||||||
|
urgentQty: item.操作人,
|
||||||
|
assignQty: item.交货数量,
|
||||||
|
taskdate: requiredDate,
|
||||||
|
operator: item.任务状态 === 0 ? '待执行' : (item.发料状态 === 1 ? '可执行' : '已开工'),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const applyDoneTaskData = rows => {
|
||||||
|
doneRows.value = rows.map(item => ({
|
||||||
|
productionOrder: item.生产订单?.toString() || item.计划号?.toString() || '',
|
||||||
|
materialName: item.物料名称 || item.零件名称 || '',
|
||||||
|
materialCode: item.物料编码 || item.零件编码 || '',
|
||||||
|
passRate: `${getPassRate(item)}%`,
|
||||||
|
rateClass: getPassRate(item) <= 70 ? 'orange-cell' : 'green-cell'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyTaskData = rows => {
|
||||||
|
taskRows.value = rows.map(item => ({
|
||||||
|
productionOrder: item.生产订单?.toString() || item.订单号?.toString() || item.计划号?.toString() || '',
|
||||||
|
salesOrder: item.销售订单 || item.合同号 || '',
|
||||||
|
materialName: item.物料名称 || item.零件名称 || '',
|
||||||
|
materialCode: item.物料编码 || item.零件编码 || '',
|
||||||
|
taskType: item.任务类型 || (String(item.自制件属性 || item.物料名称 || item.零件名称 || '').includes('系统') ? '系统类' : '阀类'),
|
||||||
|
urgentQty: getNumber(item.加急总数 || item.加急数量),
|
||||||
|
assignQty: getNumber(item.计划数量 || item.计划生产数 || item.分配数),
|
||||||
|
completeQty: getNumber(item.完成数量),
|
||||||
|
inspectQty: getNumber(item.检验数量),
|
||||||
|
expectedInspectionDate: formatDate(item.预计送检日期)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
const applyMaterialshortageData = rows => {
|
||||||
|
console.log(rows)
|
||||||
|
materialRows.value = rows.map(item => ({
|
||||||
|
wipOrder: item.DocEntry,
|
||||||
|
shortageStatus: item.U_Name,
|
||||||
|
finishTime: item.ItemCode,
|
||||||
|
owner: item.加急数量
|
||||||
|
}))
|
||||||
|
console.log(materialRows.value)
|
||||||
|
}
|
||||||
|
const queryDashboardData = (procedureName, applyData, param = []) => {
|
||||||
|
let data
|
||||||
|
data = CreateData('11', procedureName, param)
|
||||||
|
ExecDatabase(data).then(response => {
|
||||||
|
const rows = getResponseRows(response)
|
||||||
|
tableData.value = rows
|
||||||
|
applyData(rows)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getResponseRows = response => {
|
||||||
|
if (response && Array.isArray(response.data)) {
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
if (response && response.data && Array.isArray(response.data.result)) {
|
||||||
|
return response.data.result
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNumber = value => {
|
||||||
|
if (value === null || value === undefined || value === '') return 0
|
||||||
|
const number = Number(String(value).replace(/,/g, ''))
|
||||||
|
return Number.isNaN(number) ? 0 : number
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDate = value => {
|
||||||
|
if (!value) return ''
|
||||||
|
return String(value).split(' ')[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDateTime = value => {
|
||||||
|
if (!value) return Infinity
|
||||||
|
const time = new Date(String(value).replace(/-/g, '/')).getTime()
|
||||||
|
return Number.isNaN(time) ? Infinity : time
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTaskType = item => item.任务类型 || (String(item.自制件属性 || item.物料名称 || item.零件名称 || '').includes('系统') ? '系统类' : '阀类')
|
||||||
|
|
||||||
|
const isInspectionUnfinished = item => getNumber(item.完成数量) > getNumber(item.检验数量)
|
||||||
|
|
||||||
|
const isReworkTask = item => String(item.检测说明 || '').includes('返工')
|
||||||
|
|
||||||
|
const sortTestAssemblyTaskRows = rows => rows.slice().sort((a, b) => {
|
||||||
|
const unfinishedA = isInspectionUnfinished(a)
|
||||||
|
const unfinishedB = isInspectionUnfinished(b)
|
||||||
|
if (unfinishedA !== unfinishedB) return unfinishedA ? -1 : 1
|
||||||
|
return getDateTime(a.预计送检日期) - getDateTime(b.预计送检日期)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getPassRate = item => {
|
||||||
|
const inspectQty = getNumber(item.检验数量)
|
||||||
|
if (inspectQty <= 0) return 100
|
||||||
|
return Number(((getNumber(item.检验合格数量) * 100) / inspectQty).toFixed(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
const startClock = () => { timer = window.setInterval(() => { now.value = new Date(now.value.getTime() + 1000) }, 1000) }
|
||||||
|
const svgData = svg => 'data:image/svg+xml;utf8,' + encodeURIComponent(svg)
|
||||||
|
const svgIcon = (from, to, body) => svgData(
|
||||||
|
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" fill="none">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="${from}"/>
|
||||||
|
<stop offset="1" stop-color="${to}"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="shine" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fff" stop-opacity=".35"/>
|
||||||
|
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="glow" x="-55%" y="-55%" width="210%" height="210%"><feGaussianBlur stdDeviation="4" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
|
||||||
|
</defs>
|
||||||
|
<rect x="7" y="7" width="58" height="58" rx="11" fill="url(#bg)" filter="url(#glow)"/>
|
||||||
|
<path d="M14 14h44v20c-10-4-29-4-44 1z" fill="url(#shine)"/>
|
||||||
|
<g transform="translate(36 36) scale(1.12) translate(-36 -36)" stroke="#fff" stroke-width="4.1" stroke-linecap="round" stroke-linejoin="round" opacity=".96">${body}</g>
|
||||||
|
</svg>`
|
||||||
|
)
|
||||||
|
const icons = {
|
||||||
|
pending: svgIcon('#37a9ff', '#0862f3', '<rect x="23" y="20" width="26" height="36" rx="4"/><path d="M29 20h14l-3-6h-8l-3 6zM29 34h14M29 43h14"/>'),
|
||||||
|
urgent: svgIcon('#ff7469', '#dc2524', '<path d="M21 55h30M25 50V34a11 11 0 0 1 22 0v16M36 13v8M18 21l6 6M54 21l-6 6M15 38h8M49 38h8"/>'),
|
||||||
|
cart: svgIcon('#28a8ff', '#0a58ff', '<path d="M16 22h7l5 25h27l5-17H28"/><circle cx="32" cy="56" r="2.8"/><circle cx="52" cy="56" r="2.8"/>'),
|
||||||
|
checklist: svgIcon('#49e89e', '#08a86e', '<rect x="21" y="17" width="32" height="42" rx="5"/><path d="M28 35l5 5 12-13M28 49h19"/>'),
|
||||||
|
returnDown: svgIcon('#ffb23f', '#fb7418', '<rect x="21" y="23" width="31" height="32" rx="4"/><path d="M36 14v24M27 31l9 9 9-9"/>'),
|
||||||
|
returnSales: svgIcon('#9867ff', '#6232e6', '<path d="M51 22H28a10 10 0 0 0 0 20h26M28 14l-11 9 11 9"/><rect x="35" y="37" width="18" height="18" rx="2"/>'),
|
||||||
|
finalCheck: svgIcon('#3a95ff', '#0a61e8', '<path d="M36 14l16 7v13c0 11-7 18-16 24-9-6-16-13-16-24V21z"/><path d="M27 37l6 6 13-15"/>'),
|
||||||
|
other: svgIcon('#4bd8ff', '#0a88cc', '<path d="M19 26l17-10 17 10-17 10zM19 26v19l17 10 17-10V26M36 36v19"/>'),
|
||||||
|
shield: svgIcon('#3a9aff', '#0861e8', '<path d="M36 14l16 7v13c0 11-7 18-16 24-9-6-16-13-16-24V21z"/><path d="M28 37l6 6 12-14"/>'),
|
||||||
|
done: svgIcon('#47eaa0', '#0da76b', '<rect x="19" y="19" width="34" height="34" rx="8"/><path d="M27 37l7 7 15-18"/>')
|
||||||
|
}
|
||||||
|
const summaryCards = [
|
||||||
|
{ title: '可执行任务数', unit: '条', className: 'executable-card', metrics: [{ label: '阀类', value: 0 }, { label: '系统类', value: 0 }] },
|
||||||
|
{ title: '待协助任务数', value: 0, unit: '条', icon: icons.pending , valueClass: 'red-text'},
|
||||||
|
{ title: '质检及时率', value: '100.00', icon: icons.finalCheck, valueClass: 'green-text full-value' },
|
||||||
|
{ title: '加急任务数', value: 125, unit: '条', icon: icons.urgent, valueClass: 'red-text' },
|
||||||
|
{ title: '异常未关闭消息数', value: 5, unit: '条', icon: icons.urgent, valueClass: 'red-text', className: 'exception-card' },
|
||||||
|
]
|
||||||
|
const taskColumns = [
|
||||||
|
{ label: '生产订单', key: 'productionOrder', width: '72px' },
|
||||||
|
{ label: '销售订单', key: 'salesOrder', width: '92px' },
|
||||||
|
{ label: '物料名称', key: 'materialName', width: '190px' },
|
||||||
|
{ label: '物料编码', key: 'materialCode', width: '176px' },
|
||||||
|
{ label: '任务类型', key: 'taskType', width: '62px' },
|
||||||
|
{ label: '加急数', key: 'urgentQty', width: '62px' },
|
||||||
|
{ label: '计划数', key: 'assignQty', width: '70px' },
|
||||||
|
{ label: '完成数', key: 'completeQty', width: '70px' },
|
||||||
|
{ label: '检验数', key: 'inspectQty', width: '70px' },
|
||||||
|
{ label: '预计送检', key: 'expectedInspectionDate', width: '88px' },
|
||||||
|
|
||||||
|
]
|
||||||
|
const taskColumns2 = [
|
||||||
|
{ label: '生产订单', key: 'productionOrder', width: '72px' },
|
||||||
|
{ label: '销售订单', key: 'salesOrder', width: '92px' },
|
||||||
|
{ label: '物料名称', key: 'materialName', width: '190px' },
|
||||||
|
{ label: '物料编码', key: 'materialCode', width: '176px' },
|
||||||
|
{ label: '数量', key: 'assignQty', width: '176px' },
|
||||||
|
{ label: '要求发货日期', key: 'taskdate', width: '176px' },
|
||||||
|
{ label: '齐套', key: 'completeSet', width: '52px' },
|
||||||
|
{ label: '发料状态', key: 'issueStatus', width: '88px' },
|
||||||
|
{ label: '操作人', key: 'urgentQty', width: '62px' },
|
||||||
|
{ label: '装配开工', key: 'operator', width: '82px' },
|
||||||
|
]
|
||||||
|
const materialColumns = [
|
||||||
|
{ label: '生产订单', key: 'wipOrder', width: '76px' },
|
||||||
|
{ label: '物料名称', key: 'shortageStatus', width: '105px' },
|
||||||
|
{ label: '物料编码', key: 'finishTime', width: '160px' },
|
||||||
|
{ label: '加急数量', key: 'owner', width: '90px' },
|
||||||
|
]
|
||||||
|
const doneColumns = [
|
||||||
|
{ label: '生产订单', key: 'productionOrder', width: '88px' },
|
||||||
|
{ label: '物料名称', key: 'materialName', width: '150px' },
|
||||||
|
{ label: '物料编码', key: 'materialCode', width: '165px' },
|
||||||
|
{ label: '合格率', key: 'passRate', width: '86px' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assembly-dashboard {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 1920px;
|
||||||
|
height: 1080px;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transition: .3s;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #f3f9ff;
|
||||||
|
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||||||
|
background: #07001f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-shell {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 15px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 336px 1fr 442px;
|
||||||
|
align-items: center;
|
||||||
|
height: 95px;
|
||||||
|
border-bottom: 1px solid rgba(21, 162, 240, .7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
width: 300px;
|
||||||
|
height: 72px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-logo {
|
||||||
|
width: 224px;
|
||||||
|
height: 58px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: left center;
|
||||||
|
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.28));
|
||||||
|
}
|
||||||
|
.dashboard-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 55px;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: .08em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-time {
|
||||||
|
padding-right: 40px;
|
||||||
|
color: #eef7ff;
|
||||||
|
font-size: 27px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 126px 1fr;
|
||||||
|
gap: 17px;
|
||||||
|
height: calc(100% - 95px);
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.28fr 1fr 1fr 1fr .82fr;
|
||||||
|
gap: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2.05fr 1fr;
|
||||||
|
gap: 17px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-column {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1.05fr .9fr;
|
||||||
|
gap: 17px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-column {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1.05fr .9fr;
|
||||||
|
gap: 17px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid rgba(29, 153, 229, .78);
|
||||||
|
background: rgba(3, 16, 48, .38);
|
||||||
|
box-shadow: inset 0 0 18px rgba(27, 130, 214, .24), 0 0 10px rgba(1, 132, 220, .18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: -1px;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left top/14px 2px no-repeat, linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) left top/2px 14px no-repeat, linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right top/14px 2px no-repeat, linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) right top/2px 14px no-repeat, linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left bottom/14px 2px no-repeat, linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) left bottom/2px 14px no-repeat, linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right bottom/14px 2px no-repeat, linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) right bottom/2px 14px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel::after {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: inset 0 0 12px rgba(43, 216, 255, .12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 8px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card > div:first-child {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card > div:first-child {
|
||||||
|
width: auto;
|
||||||
|
padding-right: 22px;
|
||||||
|
border-right: 3px solid rgba(238, 247, 255, 0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-title {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-body {
|
||||||
|
display: flex;
|
||||||
|
gap: 26px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-value {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-value span {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.executable-card {
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.executable-card .summary-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 21px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metrics {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 14px;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
grid-template-rows: 22px 48px;
|
||||||
|
align-items: baseline;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 6px 10px 4px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric span {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
color: #89d4ff;
|
||||||
|
font-size: 25px;
|
||||||
|
line-height: 22px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric b {
|
||||||
|
color: #18c65b;
|
||||||
|
font-size: 57px;
|
||||||
|
line-height: 1;
|
||||||
|
text-align: right;
|
||||||
|
text-shadow: 0 0 10px rgba(24, 198, 91, .32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric small {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card {
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-title {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-body {
|
||||||
|
grid-template-columns: 54px 150px;
|
||||||
|
column-gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-icon {
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-value {
|
||||||
|
grid-template-columns: 105px 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-value span {
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-value small {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-title {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #89d4ff;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 66px 214px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
column-gap: 26px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon {
|
||||||
|
width: 66px;
|
||||||
|
height: 66px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 32px;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value span {
|
||||||
|
font-size: 57px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value small {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-value span {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red-text span {
|
||||||
|
color: #ff1e2e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-text span {
|
||||||
|
color: #18c65b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange-text span {
|
||||||
|
color: #ff9c12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-value::before {
|
||||||
|
content: '总数:';
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-list {
|
||||||
|
min-width: 130px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-list div {
|
||||||
|
display: flex;
|
||||||
|
gap: 18px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-caption {
|
||||||
|
height: 38px;
|
||||||
|
color: #8cd3ff;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 38px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 9px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
height: 48px;
|
||||||
|
border: 1px solid rgba(53, 149, 217, .54);
|
||||||
|
color: #f1f8ff;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.18;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-all;
|
||||||
|
background: rgba(4, 24, 57, .44);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
height: 44px;
|
||||||
|
color: #bde9ff;
|
||||||
|
font-weight: 500;
|
||||||
|
background: rgba(12, 68, 119, .28);
|
||||||
|
}
|
||||||
|
|
||||||
|
.idx-th {
|
||||||
|
width: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.idx-td {
|
||||||
|
width: 44px;
|
||||||
|
color: #89d4ff;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-panel td {
|
||||||
|
height: 47px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning td {
|
||||||
|
color: #ff4949;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-cell {
|
||||||
|
color: #32ff32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange-cell {
|
||||||
|
color: #ff7a1a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
857
src/views/index.vue
Normal file
@@ -0,0 +1,857 @@
|
|||||||
|
<template>
|
||||||
|
<div class="assembly-dashboard"
|
||||||
|
:style="{ width: `${style.width}px`, height: `${style.height}px`, transform: style.transform }">
|
||||||
|
<div class="dashboard-shell">
|
||||||
|
<header class="dashboard-header">
|
||||||
|
<div class="brand"><img class="brand-logo" :src="clientLogoUrl" alt="RENLY" /></div>
|
||||||
|
<h1>装配中心任务看板</h1>
|
||||||
|
<div class="current-time">{{ currentTime }}</div>
|
||||||
|
</header>
|
||||||
|
<main class="dashboard-content">
|
||||||
|
<section class="summary-row">
|
||||||
|
<article v-for="item in summaryCards" :key="item.title" class="panel summary-card" :class="item.className">
|
||||||
|
<div>
|
||||||
|
<div class="summary-title">{{ item.title }}</div>
|
||||||
|
<div v-if="item.metrics" class="summary-metrics">
|
||||||
|
<div v-for="metric in item.metrics" :key="metric.label" class="summary-metric">
|
||||||
|
<span>{{ metric.label }}</span>
|
||||||
|
<b>{{ metric.value }}</b>
|
||||||
|
<small>{{ item.unit }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="summary-body">
|
||||||
|
<img class="summary-icon" :src="item.icon" :alt="item.title" />
|
||||||
|
<div class="summary-value" :class="item.valueClass"><span>{{ item.value }}</span><small
|
||||||
|
v-if="item.unit">{{ item.unit }}</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.people" class="people-list">
|
||||||
|
<div v-for="person in item.people" :key="person.name"><span>{{ person.name }}</span><b>{{ person.group
|
||||||
|
}}</b></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
<section class="main-grid">
|
||||||
|
<div class="left-column">
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">发货状态列表</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in taskColumns2" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="deliveryScrollStyle">
|
||||||
|
<tr v-for="(r, i) in deliveryDisplayRows" :key="i">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in taskColumns2" :key="c.key">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="panel table-panel task-panel">
|
||||||
|
<div class="panel-caption">在制任务列表</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in taskColumns" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="taskScrollStyle">
|
||||||
|
<tr v-for="(r, i) in taskDisplayRows" :key="i" :class="{ warning: r.urgentQty > 0 }">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in taskColumns" :key="c.key">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<aside class="right-column">
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">在制任务缺件明细</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in materialColumns" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="materialScrollStyle">
|
||||||
|
<tr v-for="(r, i) in materialDisplayRows" :key="i">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in materialColumns" :key="c.key">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="panel table-panel">
|
||||||
|
<div class="panel-caption">已完工作务</div>
|
||||||
|
<div class="scroll-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="idx-th">序号</th>
|
||||||
|
<th v-for="c in doneColumns" :key="c.key">{{ c.label }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody :style="doneScrollStyle">
|
||||||
|
<tr v-for="(r, i) in doneDisplayRows" :key="i">
|
||||||
|
<td class="idx-td">{{ r._idx }}</td>
|
||||||
|
<td v-for="c in doneColumns" :key="c.key" :class="c.key === 'passRate' ? r.rateClass : ''">{{ r[c.key] }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||||
|
const CreateData = inject('CreateData')
|
||||||
|
const ExecDatabase = inject('ExecDatabase')
|
||||||
|
defineOptions({ name: 'AssemblyCenterTaskDashboard' })
|
||||||
|
|
||||||
|
const now = ref(new Date('2026-05-06 10:10:10'))
|
||||||
|
let timer = null
|
||||||
|
let resizeHandler = null
|
||||||
|
let scrollTimers = []
|
||||||
|
const style = reactive({ width: 1920, height: 1080, transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)' })
|
||||||
|
const setScale = () => { const s = getScale(); style.transform = `scaleY(${s.y}) scaleX(${s.x}) translate(-50%, -50%)` }
|
||||||
|
const getScale = () => ({ x: window.innerWidth / style.width, y: window.innerHeight / style.height })
|
||||||
|
const pad = v => String(v).padStart(2, '0')
|
||||||
|
const currentTime = computed(() => `${now.value.getFullYear()}-${pad(now.value.getMonth() + 1)}-${pad(now.value.getDate())} ${pad(now.value.getHours())}:${pad(now.value.getMinutes())}:${pad(now.value.getSeconds())}`)
|
||||||
|
const clientLogoUrl = './public/ClientLogo.png'
|
||||||
|
const icon = name => `./public/svg/${name}.svg`
|
||||||
|
const tableData = ref([])
|
||||||
|
const getFirstRow = rows => {
|
||||||
|
if (Array.isArray(rows) && rows.length > 0) {
|
||||||
|
return rows[0]
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
startClock();
|
||||||
|
setScale();
|
||||||
|
resizeHandler = setScale;
|
||||||
|
window.addEventListener('resize', resizeHandler)
|
||||||
|
searchDashboardData()
|
||||||
|
startAllScroll()
|
||||||
|
setInterval(function(){
|
||||||
|
searchDashboardData()
|
||||||
|
},600000)
|
||||||
|
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) window.clearInterval(timer);
|
||||||
|
if (resizeHandler) window.removeEventListener('resize', resizeHandler)
|
||||||
|
stopAllScroll()
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============ 滚动数据 ============
|
||||||
|
const deliveryRows = ref([])
|
||||||
|
const taskRows = ref([])
|
||||||
|
const materialRows = ref([])
|
||||||
|
const doneRows = ref([])
|
||||||
|
|
||||||
|
// 每个列表的滚动偏移
|
||||||
|
const deliveryOffset = ref(0)
|
||||||
|
const taskOffset = ref(0)
|
||||||
|
const materialOffset = ref(0)
|
||||||
|
const doneOffset = ref(0)
|
||||||
|
|
||||||
|
// 可见行数
|
||||||
|
const VISIBLE_ROWS = {
|
||||||
|
delivery: 8,
|
||||||
|
task: 7,
|
||||||
|
material: 8,
|
||||||
|
done: 7
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成带序号和循环滚动的显示行
|
||||||
|
const makeDisplayRows = (rows, offset, visibleCount) => {
|
||||||
|
if (!rows || rows.length === 0) return []
|
||||||
|
const len = rows.length
|
||||||
|
const result = []
|
||||||
|
for (let i = 0; i < visibleCount; i++) {
|
||||||
|
const idx = (offset + i) % len
|
||||||
|
result.push({ ...rows[idx], _idx: idx + 1 })
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const deliveryDisplayRows = computed(() => makeDisplayRows(deliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery))
|
||||||
|
const taskDisplayRows = computed(() => makeDisplayRows(taskRows.value, taskOffset.value, VISIBLE_ROWS.task))
|
||||||
|
const materialDisplayRows = computed(() => makeDisplayRows(materialRows.value, materialOffset.value, VISIBLE_ROWS.material))
|
||||||
|
const doneDisplayRows = computed(() => makeDisplayRows(doneRows.value, doneOffset.value, VISIBLE_ROWS.done))
|
||||||
|
|
||||||
|
// 滚动样式(平滑过渡)
|
||||||
|
const deliveryScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
const taskScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
const materialScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
const doneScrollStyle = computed(() => ({ transition: 'none' }))
|
||||||
|
|
||||||
|
// 启动单个列表滚动
|
||||||
|
const startScroll = (rowsRef, offsetRef) => {
|
||||||
|
const id = window.setInterval(() => {
|
||||||
|
if (rowsRef.value.length > 0) {
|
||||||
|
offsetRef.value = (offsetRef.value + 1) % rowsRef.value.length
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
scrollTimers.push(id)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
const startAllScroll = () => {
|
||||||
|
startScroll(deliveryRows, deliveryOffset)
|
||||||
|
startScroll(taskRows, taskOffset)
|
||||||
|
startScroll(materialRows, materialOffset)
|
||||||
|
startScroll(doneRows, doneOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopAllScroll = () => {
|
||||||
|
scrollTimers.forEach(id => window.clearInterval(id))
|
||||||
|
scrollTimers = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 数据查询 ============
|
||||||
|
const searchDashboardData = () => {
|
||||||
|
searchExecutableData()
|
||||||
|
searchUnclosedData()
|
||||||
|
searchMaterialshortageData()
|
||||||
|
searchUrgentData()
|
||||||
|
searchDeliveryData()
|
||||||
|
searchTaskData()
|
||||||
|
searchPassRateData()
|
||||||
|
searchDoneTaskData()
|
||||||
|
}
|
||||||
|
const searchExecutableData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_可执行任务数', applyExecutableData)
|
||||||
|
}
|
||||||
|
const searchUnclosedData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_异常未关闭总数', applyUnclosedData)
|
||||||
|
}
|
||||||
|
const searchDeliveryData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_发货状态查询', applyDeliveryData)
|
||||||
|
}
|
||||||
|
const searchMaterialshortageData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_在制品缺件查询', applyMaterialshortageData)
|
||||||
|
}
|
||||||
|
const searchUrgentData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_加急任务总数查询', applyUrgentData)
|
||||||
|
}
|
||||||
|
// const searchTaskData = () => {
|
||||||
|
// queryDashboardData('装配中心任务看板_装配任务查询', applyTaskData)
|
||||||
|
// }
|
||||||
|
const searchTaskData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_装配在制任务查询', applyTaskData)
|
||||||
|
}
|
||||||
|
const searchPassRateData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_装配合格率', applyPassRateData)
|
||||||
|
}
|
||||||
|
const searchDoneTaskData = () => {
|
||||||
|
queryDashboardData('装配中心任务看板_已完成任务', applyDoneTaskData)
|
||||||
|
}
|
||||||
|
const applyExecutableData = rows => {
|
||||||
|
const row = rows[0] || {}
|
||||||
|
summaryCards[0].metrics[0].value = row.阀类可执行任务数 || 0
|
||||||
|
summaryCards[0].metrics[1].value = row.系统类电气任务数 || 0
|
||||||
|
summaryCards[0].metrics[2].value = row.系统类机械任务数 || 0
|
||||||
|
summaryCards[1].value = row.及时送检数 || 0
|
||||||
|
}
|
||||||
|
const applyUnclosedData = rows => {
|
||||||
|
summaryCards[4].value = rows[0].装配异常未关闭总数
|
||||||
|
}
|
||||||
|
const applyDeliveryData = rows => {
|
||||||
|
deliveryRows.value = rows.map(item => {
|
||||||
|
let requiredDate = ''
|
||||||
|
if (item.要求发货日期) {
|
||||||
|
// 如果是字符串
|
||||||
|
if (typeof item.要求发货日期 === 'string') {
|
||||||
|
requiredDate = item.要求发货日期.split(' ')[0]
|
||||||
|
}
|
||||||
|
// 如果是 Date 对象
|
||||||
|
else if (item.要求发货日期 instanceof Date) {
|
||||||
|
requiredDate = item.要求发货日期.toISOString().split('T')[0]
|
||||||
|
}
|
||||||
|
// 如果是其他格式
|
||||||
|
else {
|
||||||
|
const dateStr = String(item.要求发货日期)
|
||||||
|
requiredDate = dateStr.split(' ')[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
productionOrder: item.订单编号,
|
||||||
|
salesOrder: item.合同号 || '',
|
||||||
|
materialName: item.物料描述 || '',
|
||||||
|
materialCode: item.物料编号 || '',
|
||||||
|
completeSet: item.齐套 === 1 ? '是' : '否',
|
||||||
|
issueStatus: item.发料状态 === '1' ? '发料齐套' : (item.发料状态 === '2' ? '发料缺件' : ''),
|
||||||
|
urgentQty: item.操作人,
|
||||||
|
assignQty: item.交货数量,
|
||||||
|
taskdate: requiredDate,
|
||||||
|
operator: item.任务状态 === 0 ? '待执行' : (item.发料状态 === 1 ? '可执行' : '已开工'),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const applyUrgentData = rows => {
|
||||||
|
summaryCards[3].value = rows[0].加急总数
|
||||||
|
}
|
||||||
|
const applyPassRateData = rows => {
|
||||||
|
summaryCards[2].value = rows[0].总合格率+'%'
|
||||||
|
}
|
||||||
|
const applyDoneTaskData = rows => {
|
||||||
|
doneRows.value = rows.map(item => ({
|
||||||
|
productionOrder:item.计划号?.toString() || '',
|
||||||
|
materialName: item.零件名称 || '',
|
||||||
|
materialCode: item.零件编码 || '',
|
||||||
|
passRate: item.单项合格率+'%',
|
||||||
|
rateClass:item.单项合格率 <= 70 ? 'orange-cell' : 'green-cell'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyTaskData = rows => {
|
||||||
|
taskRows.value = rows.map(item => ({
|
||||||
|
productionOrder: item.订单号?.toString() || '',
|
||||||
|
salesOrder: item.合同号 || '',
|
||||||
|
materialName: item.零件名称 || '',
|
||||||
|
materialCode: item.零件编码 || '',
|
||||||
|
processName: item.工序名称 || '',
|
||||||
|
completeSet: item.齐套 === '是' ? '是' : '否',
|
||||||
|
issueStatus: item.发料状态 === '发料齐套' ? '发料齐套' : (item.发料状态 === '发料缺件' ? '发料缺件' : ''),
|
||||||
|
urgentQty: (item.加急总数 && item.加急总数 > 0) ? item.加急总数 : (item.加急 === '加急' ? (item.分配数 || 0) : 0),
|
||||||
|
assignQty: item.分配数 || 0,
|
||||||
|
inspectQty: item.完成数量,
|
||||||
|
assignTarget: item.操作人 || '',
|
||||||
|
workHours: '',
|
||||||
|
operator: item.操作人 || ''
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
const applyMaterialshortageData = rows => {
|
||||||
|
console.log(rows)
|
||||||
|
materialRows.value = rows.map(item => ({
|
||||||
|
wipOrder: item.DocEntry,
|
||||||
|
shortageStatus: item.U_Name,
|
||||||
|
finishTime: item.ItemCode,
|
||||||
|
owner: item.加急数量
|
||||||
|
}))
|
||||||
|
console.log(materialRows.value)
|
||||||
|
}
|
||||||
|
const queryDashboardData = (procedureName, applyData) => {
|
||||||
|
let data
|
||||||
|
data = CreateData('11', procedureName, [])
|
||||||
|
ExecDatabase(data).then(response => {
|
||||||
|
const rows = getResponseRows(response)
|
||||||
|
tableData.value = rows
|
||||||
|
applyData(rows)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getResponseRows = response => {
|
||||||
|
if (response && Array.isArray(response.data)) {
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
if (response && response.data && Array.isArray(response.data.result)) {
|
||||||
|
return response.data.result
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const startClock = () => { timer = window.setInterval(() => { now.value = new Date(now.value.getTime() + 1000) }, 1000) }
|
||||||
|
const svgData = svg => 'data:image/svg+xml;utf8,' + encodeURIComponent(svg)
|
||||||
|
const svgIcon = (from, to, body) => svgData(
|
||||||
|
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" fill="none">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="${from}"/>
|
||||||
|
<stop offset="1" stop-color="${to}"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="shine" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fff" stop-opacity=".35"/>
|
||||||
|
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="glow" x="-55%" y="-55%" width="210%" height="210%"><feGaussianBlur stdDeviation="4" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
|
||||||
|
</defs>
|
||||||
|
<rect x="7" y="7" width="58" height="58" rx="11" fill="url(#bg)" filter="url(#glow)"/>
|
||||||
|
<path d="M14 14h44v20c-10-4-29-4-44 1z" fill="url(#shine)"/>
|
||||||
|
<g transform="translate(36 36) scale(1.12) translate(-36 -36)" stroke="#fff" stroke-width="4.1" stroke-linecap="round" stroke-linejoin="round" opacity=".96">${body}</g>
|
||||||
|
</svg>`
|
||||||
|
)
|
||||||
|
const icons = {
|
||||||
|
pending: svgIcon('#37a9ff', '#0862f3', '<rect x="23" y="20" width="26" height="36" rx="4"/><path d="M29 20h14l-3-6h-8l-3 6zM29 34h14M29 43h14"/>'),
|
||||||
|
urgent: svgIcon('#ff7469', '#dc2524', '<path d="M21 55h30M25 50V34a11 11 0 0 1 22 0v16M36 13v8M18 21l6 6M54 21l-6 6M15 38h8M49 38h8"/>'),
|
||||||
|
cart: svgIcon('#28a8ff', '#0a58ff', '<path d="M16 22h7l5 25h27l5-17H28"/><circle cx="32" cy="56" r="2.8"/><circle cx="52" cy="56" r="2.8"/>'),
|
||||||
|
checklist: svgIcon('#49e89e', '#08a86e', '<rect x="21" y="17" width="32" height="42" rx="5"/><path d="M28 35l5 5 12-13M28 49h19"/>'),
|
||||||
|
returnDown: svgIcon('#ffb23f', '#fb7418', '<rect x="21" y="23" width="31" height="32" rx="4"/><path d="M36 14v24M27 31l9 9 9-9"/>'),
|
||||||
|
returnSales: svgIcon('#9867ff', '#6232e6', '<path d="M51 22H28a10 10 0 0 0 0 20h26M28 14l-11 9 11 9"/><rect x="35" y="37" width="18" height="18" rx="2"/>'),
|
||||||
|
finalCheck: svgIcon('#3a95ff', '#0a61e8', '<path d="M36 14l16 7v13c0 11-7 18-16 24-9-6-16-13-16-24V21z"/><path d="M27 37l6 6 13-15"/>'),
|
||||||
|
other: svgIcon('#4bd8ff', '#0a88cc', '<path d="M19 26l17-10 17 10-17 10zM19 26v19l17 10 17-10V26M36 36v19"/>'),
|
||||||
|
shield: svgIcon('#3a9aff', '#0861e8', '<path d="M36 14l16 7v13c0 11-7 18-16 24-9-6-16-13-16-24V21z"/><path d="M28 37l6 6 12-14"/>'),
|
||||||
|
done: svgIcon('#47eaa0', '#0da76b', '<rect x="19" y="19" width="34" height="34" rx="8"/><path d="M27 37l7 7 15-18"/>')
|
||||||
|
}
|
||||||
|
const summaryCards = [
|
||||||
|
{ title: '可执行任务数', unit: '条', className: 'executable-card', metrics: [{ label: '阀类', value: 0 }, { label: '系统-电气', value: 0 },{ label: '系统-机械', value: 0 }] },
|
||||||
|
{ title: '及时送检数', value: 0, unit: '条', icon: icons.pending , valueClass: 'red-text'},
|
||||||
|
{ title: '装配合格率', value: '100%', icon: icons.finalCheck, valueClass: 'green-text full-value' },
|
||||||
|
{ title: '加急任务数', value: 125, unit: '条', icon: icons.urgent, valueClass: 'red-text' },
|
||||||
|
{ title: '异常未关闭消息数', value: 5, unit: '条', icon: icons.urgent, valueClass: 'red-text', className: 'exception-card' },
|
||||||
|
]
|
||||||
|
const taskColumns = [
|
||||||
|
{ label: '生产订单', key: 'productionOrder', width: '72px' },
|
||||||
|
{ label: '销售订单', key: 'salesOrder', width: '92px' },
|
||||||
|
{ label: '物料名称', key: 'materialName', width: '190px' },
|
||||||
|
{ label: '物料编码', key: 'materialCode', width: '176px' },
|
||||||
|
{ label: '齐套', key: 'completeSet', width: '52px' },
|
||||||
|
{ label: '发料状态', key: 'issueStatus', width: '88px' },
|
||||||
|
{ label: '加急数', key: 'urgentQty', width: '62px' },
|
||||||
|
{ label: '分配数', key: 'assignQty', width: '70px' },
|
||||||
|
{ label: '送检数', key: 'inspectQty', width: '70px' },
|
||||||
|
{ label: '指派对象', key: 'assignTarget', width: '88px' },
|
||||||
|
|
||||||
|
]
|
||||||
|
const taskColumns2 = [
|
||||||
|
{ label: '生产订单', key: 'productionOrder', width: '72px' },
|
||||||
|
{ label: '销售订单', key: 'salesOrder', width: '92px' },
|
||||||
|
{ label: '物料名称', key: 'materialName', width: '190px' },
|
||||||
|
{ label: '物料编码', key: 'materialCode', width: '176px' },
|
||||||
|
{ label: '数量', key: 'assignQty', width: '176px' },
|
||||||
|
{ label: '要求发货日期', key: 'taskdate', width: '176px' },
|
||||||
|
{ label: '齐套', key: 'completeSet', width: '52px' },
|
||||||
|
{ label: '发料状态', key: 'issueStatus', width: '88px' },
|
||||||
|
{ label: '操作人', key: 'urgentQty', width: '62px' },
|
||||||
|
{ label: '装配开工', key: 'operator', width: '82px' },
|
||||||
|
]
|
||||||
|
const materialColumns = [
|
||||||
|
{ label: '生产订单', key: 'wipOrder', width: '76px' },
|
||||||
|
{ label: '物料名称', key: 'shortageStatus', width: '105px' },
|
||||||
|
{ label: '物料编码', key: 'finishTime', width: '160px' },
|
||||||
|
{ label: '加急数量', key: 'owner', width: '90px' },
|
||||||
|
]
|
||||||
|
const doneColumns = [
|
||||||
|
{ label: '生产订单', key: 'productionOrder', width: '88px' },
|
||||||
|
{ label: '物料名称', key: 'materialName', width: '150px' },
|
||||||
|
{ label: '物料编码', key: 'materialCode', width: '165px' },
|
||||||
|
{ label: '合格率', key: 'passRate', width: '86px' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assembly-dashboard {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 1920px;
|
||||||
|
height: 1080px;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transition: .3s;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #f3f9ff;
|
||||||
|
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||||||
|
background: #07001f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-shell {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 15px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 336px 1fr 442px;
|
||||||
|
align-items: center;
|
||||||
|
height: 95px;
|
||||||
|
border-bottom: 1px solid rgba(21, 162, 240, .7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
width: 300px;
|
||||||
|
height: 72px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-logo {
|
||||||
|
width: 224px;
|
||||||
|
height: 58px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: left center;
|
||||||
|
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.28));
|
||||||
|
}
|
||||||
|
.dashboard-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 55px;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: .08em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-time {
|
||||||
|
padding-right: 40px;
|
||||||
|
color: #eef7ff;
|
||||||
|
font-size: 27px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 126px 1fr;
|
||||||
|
gap: 17px;
|
||||||
|
height: calc(100% - 95px);
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.28fr 1fr 1fr 1fr .82fr;
|
||||||
|
gap: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2.05fr 1fr;
|
||||||
|
gap: 17px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-column {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1.05fr .9fr;
|
||||||
|
gap: 17px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-column {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1.05fr .9fr;
|
||||||
|
gap: 17px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid rgba(29, 153, 229, .78);
|
||||||
|
background: rgba(3, 16, 48, .38);
|
||||||
|
box-shadow: inset 0 0 18px rgba(27, 130, 214, .24), 0 0 10px rgba(1, 132, 220, .18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: -1px;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left top/14px 2px no-repeat, linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) left top/2px 14px no-repeat, linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right top/14px 2px no-repeat, linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) right top/2px 14px no-repeat, linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left bottom/14px 2px no-repeat, linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) left bottom/2px 14px no-repeat, linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right bottom/14px 2px no-repeat, linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) right bottom/2px 14px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel::after {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: inset 0 0 12px rgba(43, 216, 255, .12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 8px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card > div:first-child {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card > div:first-child {
|
||||||
|
width: auto;
|
||||||
|
padding-right: 22px;
|
||||||
|
border-right: 3px solid rgba(238, 247, 255, 0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-title {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-body {
|
||||||
|
display: flex;
|
||||||
|
gap: 26px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-value {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-value span {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.executable-card {
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.executable-card .summary-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 21px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metrics {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
gap: 2px;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
grid-template-rows: 22px 48px;
|
||||||
|
align-items: baseline;
|
||||||
|
min-width: 0;
|
||||||
|
// padding: 6px 10px 4px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric span {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
color: #89d4ff;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric b {
|
||||||
|
color: #18c65b;
|
||||||
|
font-size: 57px;
|
||||||
|
line-height: 1;
|
||||||
|
text-align: right;
|
||||||
|
text-shadow: 0 0 10px rgba(24, 198, 91, .32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-metric small {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card {
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-title {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-body {
|
||||||
|
grid-template-columns: 54px 150px;
|
||||||
|
column-gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-icon {
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-value {
|
||||||
|
grid-template-columns: 105px 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-value span {
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exception-card .summary-value small {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-title {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #89d4ff;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 66px 214px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
column-gap: 26px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon {
|
||||||
|
width: 66px;
|
||||||
|
height: 66px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 32px;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value span {
|
||||||
|
font-size: 57px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value small {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-value span {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red-text span {
|
||||||
|
color: #ff1e2e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-text span {
|
||||||
|
color: #18c65b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange-text span {
|
||||||
|
color: #ff9c12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.absence-card .summary-value::before {
|
||||||
|
content: '总数:';
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-list {
|
||||||
|
min-width: 130px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.people-list div {
|
||||||
|
display: flex;
|
||||||
|
gap: 18px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-caption {
|
||||||
|
height: 38px;
|
||||||
|
color: #8cd3ff;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 38px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 9px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
height: 48px;
|
||||||
|
border: 1px solid rgba(53, 149, 217, .54);
|
||||||
|
color: #f1f8ff;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.18;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-all;
|
||||||
|
background: rgba(4, 24, 57, .44);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
height: 44px;
|
||||||
|
color: #bde9ff;
|
||||||
|
font-weight: 500;
|
||||||
|
background: rgba(12, 68, 119, .28);
|
||||||
|
}
|
||||||
|
|
||||||
|
.idx-th {
|
||||||
|
width: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.idx-td {
|
||||||
|
width: 44px;
|
||||||
|
color: #89d4ff;
|
||||||
|
// font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-panel td {
|
||||||
|
height: 47px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning td {
|
||||||
|
color: #ff4949;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-cell {
|
||||||
|
color: #32ff32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange-cell {
|
||||||
|
color: #ff7a1a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
48
vite.config.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
import copy from 'rollup-plugin-copy'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
base: './',
|
||||||
|
plugins: [
|
||||||
|
vue()
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginOptions: {
|
||||||
|
'style-resources-loader': {
|
||||||
|
preProcessor: 'scss',
|
||||||
|
patterns: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
'process.env': {}
|
||||||
|
},
|
||||||
|
// 完全禁用默认的 public 目录处理
|
||||||
|
publicDir: false,
|
||||||
|
build: {
|
||||||
|
// 清空输出目录时保留自定义拷贝的文件
|
||||||
|
emptyOutDir: true,
|
||||||
|
|
||||||
|
rollupOptions: {
|
||||||
|
plugins: [
|
||||||
|
copy({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
src: 'public/**/*', // 复制整个 public 目录
|
||||||
|
dest: 'dist/public' // 输出到 dist/public
|
||||||
|
}
|
||||||
|
],
|
||||||
|
hook: 'writeBundle', // 在构建完成后执行
|
||||||
|
copyOnce: true // 仅拷贝一次
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||