From e5ed471a20179fef875aed7341a5ae6d5b79a8c7 Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期一, 18 五月 2026 16:44:37 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_NEW_pro' into dev_鹤壁_强信宇_pro
---
multiple/assets/favicon/JXJHfavicon.ico | 0
src/views/productionManagement/productionProcess/index.vue | 27
src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue | 9
multiple/assets/favicon/ZQSYfavicon.ico | 0
src/views/basicData/product/index.vue | 53
src/views/reportAnalysis/productionAnalysis/index.vue | 16
src/views/equipmentManagement/measurementEquipment/components/formDia.vue | 34
src/views/financialManagement/generalLedger/index.vue | 4
src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue | 144 +
src/views/qualityManagement/rawMaterialInspection/index.vue | 26
src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue | 9
src/views/reportAnalysis/productionAnalysis/components/left-top.vue | 9
src/views/qualityManagement/finalInspection/index.vue | 21
src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue | 9
src/views/personnelManagement/contractManagement/index.vue | 22
multiple/assets/favicon/HYLQfavicon.ico | 0
src/api/inventoryManagement/stockInventory.js | 8
src/views/qualityManagement/processInspection/index.vue | 25
src/views/collaborativeApproval/sealManagement/index.vue | 82 +
src/views/productionManagement/workOrderManagement/index.vue | 5
multiple/assets/logo/ZQSYLogo.png | 0
src/views/reportAnalysis/productionAnalysis/components/right-top.vue | 9
src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue | 28
src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue | 9
src/views/productionManagement/productionTraceability/index.vue | 1
multiple/assets/logo/JHYLogo.png | 0
multiple/assets/logo/HYLQLogo.png | 0
src/views/financialManagement/voucher/generalLedger.vue | 18
src/views/equipmentManagement/upkeep/index.vue | 29
src/views/qualityManagement/processInspection/components/formDia.vue | 120 +
multiple/multiple-build.js | 192 +
src/components/AIChatSidebar/assistants/productionAssistant.js | 27
src/views/personnelManagement/employeeRecord/index.vue | 59
src/views/aiIndustrialBrain/index.vue | 1
src/views/equipmentManagement/repair/Modal/RepairModal.vue | 38
multiple/assets/favicon/JHYfavicon.ico | 0
src/views/salesManagement/salesQuotation/index.vue | 77
src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue | 9
src/views/financialManagement/assets/fixedAssets.vue | 2
src/views/reportAnalysis/productionAnalysis/components/center-top.vue | 9
src/views/procurementManagement/procurementLedger/index.vue | 1
src/views/qualityManagement/nonconformingManagement/components/formDia.vue | 22
src/views/equipmentManagement/measurementEquipment/index.vue | 48
src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue | 9
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue | 86
src/views/qualityManagement/nonconformingManagement/index.vue | 2
src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue | 357 ++--
src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue | 9
src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue | 6
multiple/assets/logo/XCDQLogo.png | 0
multiple/assets/logo/XSWHLogo.png | 0
multiple/config.json | 87 +
src/views/equipmentManagement/upkeep/Form/PlanModal.vue | 13
src/views/reportAnalysis/dataDashboard/index.vue | 16
src/views/equipmentManagement/inspectionManagement/components/formDia.vue | 19
src/views/productionManagement/processRoute/processRouteItem/index.vue | 79
src/components/ProcessParamListDialog.vue | 34
src/components/AIChatSidebar/assistants/index.js | 13
src/views/collaborativeApproval/approvalProcess/index.vue | 23
src/views/financialManagement/voucher/index.vue | 107 +
src/views/salesManagement/salesLedger/index.vue | 6
src/views/reportAnalysis/PSIDataAnalysis/index.vue | 16
src/views/reportAnalysis/productionAnalysis/components/center-center.vue | 9
src/views/aiIndustrialBrain/MAINTAIN_RULES.md | 7
multiple/assets/logo/JXJHLogo.png | 0
multiple/assets/favicon/XCDQfavicon.ico | 0
src/layout/components/Sidebar/index.vue | 15
src/views/financialManagement/assets/intangibleAssets.vue | 2
src/views/inventoryManagement/stockManagement/Record.vue | 354 +++-
src/views/financialManagement/voucher/detailLedger.vue | 2
multiple/assets/favicon/XSWHfavicon.ico | 0
multiple/assets/logo/YTJZLogo.png | 0
src/components/AIChatSidebar/assistants/salesAssistant.js | 28
src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue | 9
src/views/basicData/supplierManage/components/HomeTab.vue | 21
multiple/assets/favicon/YTJZfavicon.ico | 0
src/assets/styles/sidebar.scss | 9
src/views/qualityManagement/finalInspection/components/formDia.vue | 180 +
src/views/equipmentManagement/repair/index.vue | 36
src/components/AIChatSidebar/index.vue | 1137 ++++++++++++++
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue | 5
src/views/equipmentManagement/inspectionManagement/index.vue | 31
src/views/productionManagement/productStructure/Detail/index.vue | 118 +
src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue | 9
src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue | 9
src/api/equipmentManagement/repair.js | 13
src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue | 9
src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue | 227 +++
src/views/aiIndustrialBrain/components/AiAssistantWorkspace.vue | 16
src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue | 27
src/views/equipmentManagement/upkeep/Form/formDia.vue | 22
multiple/assets/logo/HYJCLogo.png | 0
src/views/basicData/supplierManage/components/BlacklistTab.vue | 21
src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue | 9
src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue | 44
multiple/assets/favicon/HYJCfavicon.ico | 0
96 files changed, 3,649 insertions(+), 804 deletions(-)
diff --git a/multiple/assets/favicon/HYJCfavicon.ico b/multiple/assets/favicon/HYJCfavicon.ico
new file mode 100644
index 0000000..e52f096
--- /dev/null
+++ b/multiple/assets/favicon/HYJCfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/HYLQfavicon.ico b/multiple/assets/favicon/HYLQfavicon.ico
new file mode 100644
index 0000000..9a2dbf3
--- /dev/null
+++ b/multiple/assets/favicon/HYLQfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/JHYfavicon.ico b/multiple/assets/favicon/JHYfavicon.ico
new file mode 100644
index 0000000..f818f6f
--- /dev/null
+++ b/multiple/assets/favicon/JHYfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/JXJHfavicon.ico b/multiple/assets/favicon/JXJHfavicon.ico
new file mode 100644
index 0000000..0c90589
--- /dev/null
+++ b/multiple/assets/favicon/JXJHfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/XCDQfavicon.ico b/multiple/assets/favicon/XCDQfavicon.ico
new file mode 100644
index 0000000..69a2280
--- /dev/null
+++ b/multiple/assets/favicon/XCDQfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/XSWHfavicon.ico b/multiple/assets/favicon/XSWHfavicon.ico
new file mode 100644
index 0000000..f6cd72d
--- /dev/null
+++ b/multiple/assets/favicon/XSWHfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/YTJZfavicon.ico b/multiple/assets/favicon/YTJZfavicon.ico
new file mode 100644
index 0000000..ad7b03b
--- /dev/null
+++ b/multiple/assets/favicon/YTJZfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/ZQSYfavicon.ico b/multiple/assets/favicon/ZQSYfavicon.ico
new file mode 100644
index 0000000..9e3422f
--- /dev/null
+++ b/multiple/assets/favicon/ZQSYfavicon.ico
Binary files differ
diff --git a/multiple/assets/logo/HYJCLogo.png b/multiple/assets/logo/HYJCLogo.png
new file mode 100644
index 0000000..367f64d
--- /dev/null
+++ b/multiple/assets/logo/HYJCLogo.png
Binary files differ
diff --git a/multiple/assets/logo/HYLQLogo.png b/multiple/assets/logo/HYLQLogo.png
new file mode 100644
index 0000000..8eda2f7
--- /dev/null
+++ b/multiple/assets/logo/HYLQLogo.png
Binary files differ
diff --git a/multiple/assets/logo/JHYLogo.png b/multiple/assets/logo/JHYLogo.png
new file mode 100644
index 0000000..edf5921
--- /dev/null
+++ b/multiple/assets/logo/JHYLogo.png
Binary files differ
diff --git a/multiple/assets/logo/JXJHLogo.png b/multiple/assets/logo/JXJHLogo.png
new file mode 100644
index 0000000..1d47288
--- /dev/null
+++ b/multiple/assets/logo/JXJHLogo.png
Binary files differ
diff --git a/multiple/assets/logo/XCDQLogo.png b/multiple/assets/logo/XCDQLogo.png
new file mode 100644
index 0000000..d10580a
--- /dev/null
+++ b/multiple/assets/logo/XCDQLogo.png
Binary files differ
diff --git a/multiple/assets/logo/XSWHLogo.png b/multiple/assets/logo/XSWHLogo.png
new file mode 100644
index 0000000..d0c32f5
--- /dev/null
+++ b/multiple/assets/logo/XSWHLogo.png
Binary files differ
diff --git a/multiple/assets/logo/YTJZLogo.png b/multiple/assets/logo/YTJZLogo.png
new file mode 100644
index 0000000..ea0c489
--- /dev/null
+++ b/multiple/assets/logo/YTJZLogo.png
Binary files differ
diff --git a/multiple/assets/logo/ZQSYLogo.png b/multiple/assets/logo/ZQSYLogo.png
new file mode 100644
index 0000000..fe865b6
--- /dev/null
+++ b/multiple/assets/logo/ZQSYLogo.png
Binary files differ
diff --git a/multiple/config.json b/multiple/config.json
index 51f04d6..82f5c86 100644
--- a/multiple/config.json
+++ b/multiple/config.json
@@ -44,9 +44,9 @@
},
"BTYX": {
"env": {
- "VITE_APP_TITLE": "娌冲崡甯お浼橀�夎繘鍑哄彛鏈夐檺鍏徃",
- "VITE_BASE_API": "http://127.0.0.1:9001",
- "VITE_JAVA_API": "http://127.0.0.1:9000"
+ "VITE_APP_TITLE": "娌冲崡甯お浼橀�夐鍝佹湁闄愬叕鍙�",
+ "VITE_BASE_API": "http://1.15.17.182:9056",
+ "VITE_JAVA_API": "http://1.15.17.182:9057"
},
"logo": "logo/BTYXLogo.png",
"favicon": "favicon/BTYXfavicon.ico"
@@ -59,15 +59,6 @@
},
"logo": "logo/ZXZNLogo.png",
"favicon": "favicon/ZXZNfavicon.ico"
- },
- "QXY": {
- "env": {
- "VITE_APP_TITLE": "寮轰俊瀹囩數鍣ㄧ鐞嗙郴缁�",
- "VITE_BASE_API": "http://36.134.154.10:9001",
- "VITE_JAVA_API": "http://36.134.154.10:9000"
- },
- "logo": "logo/QXYLogo.png",
- "favicon": "favicon/QXYfavicon.ico"
},
"HYZC": {
"env": {
@@ -105,6 +96,78 @@
"logo": "logo/DYKJLogo.png",
"favicon": "favicon/DYKJfavicon.ico"
},
+ "ZQSY": {
+ "env": {
+ "VITE_APP_TITLE": "娉芥穱瀹炰笟",
+ "VITE_BASE_API": "http://36.213.128.159:9000",
+ "VITE_JAVA_API": "http://36.213.128.159:9001"
+ },
+ "logo": "logo/ZQSYLogo.png",
+ "favicon": "favicon/ZQSYfavicon.ico"
+ },
+ "JXJH": {
+ "env": {
+ "VITE_APP_TITLE": "娴氬幙姹熸捣姘存偿鍒跺搧鏈夐檺鍏徃",
+ "VITE_BASE_API": "http://36.139.201.20:9000",
+ "VITE_JAVA_API": "http://36.139.201.20:9001"
+ },
+ "logo": "logo/JXJHLogo.png",
+ "favicon": "favicon/JXJHfavicon.ico"
+ },
+ "YTJZ": {
+ "env": {
+ "VITE_APP_TITLE": "璞嘲寤虹瓚鏉愭枡鏈夐檺鍏徃",
+ "VITE_BASE_API": "http://36.139.201.181:9000",
+ "VITE_JAVA_API": "http://36.139.201.181:9001"
+ },
+ "logo": "logo/YTJZLogo.png",
+ "favicon": "favicon/YTJZfavicon.ico"
+ },
+ "HYLQ": {
+ "env": {
+ "VITE_APP_TITLE": "鑸�歌矾妗ュ伐绋嬫湁闄愬叕鍙�",
+ "VITE_BASE_API": "http://36.139.202.111:9000",
+ "VITE_JAVA_API": "http://36.139.202.111:9001"
+ },
+ "logo": "logo/HYLQLogo.png",
+ "favicon": "favicon/HYLQfavicon.ico"
+ },
+ "QXY": {
+ "env": {
+ "VITE_APP_TITLE": "寮轰俊瀹囩數鍣ㄤ簯涓绘満",
+ "VITE_BASE_API": "http://36.134.154.10:9000",
+ "VITE_JAVA_API": "http://36.134.154.10:9001"
+ },
+ "logo": "logo/QXYLogo.png",
+ "favicon": "favicon/QXYfavicon.ico"
+ },
+ "HYJC": {
+ "env": {
+ "VITE_APP_TITLE": "鎭掓磱寤烘潗",
+ "VITE_BASE_API": "http://36.138.94.178:9000",
+ "VITE_JAVA_API": "http://36.138.94.178:9001"
+ },
+ "logo": "logo/HYJCLogo.png",
+ "favicon": "favicon/HYJCfavicon.ico"
+ },
+ "JHY": {
+ "env": {
+ "VITE_APP_TITLE": "灞辫タ鐪佹绀惧幙鏅嬪拰鍥鍝佹湁闄愬叕鍙�",
+ "VITE_BASE_API": "http://223.15.233.27:9001",
+ "VITE_JAVA_API": "http://223.15.233.27:9002"
+ },
+ "logo": "logo/JHYLogo.png",
+ "favicon": "favicon/JHYfavicon.ico"
+ },
+ "XCDQ": {
+ "env": {
+ "VITE_APP_TITLE": "鏃櫒鐢靛櫒绠$悊绯荤粺",
+ "VITE_BASE_API": "http://36.133.45.183:9001",
+ "VITE_JAVA_API": "http://36.133.45.183:9002"
+ },
+ "logo": "logo/XCDQLogo.png",
+ "favicon": "favicon/XCDQfavicon.ico"
+ },
"logo": "/src/assets/logo/logo.png",
"favicon": "/public/favicon.ico"
}
diff --git a/multiple/multiple-build.js b/multiple/multiple-build.js
index d87b333..afcd4d5 100644
--- a/multiple/multiple-build.js
+++ b/multiple/multiple-build.js
@@ -1,98 +1,152 @@
-import fs from 'fs/promises';
-import fsSync from 'fs';
-import path from 'path';
-import { fileURLToPath } from 'url';
+import fs from "fs/promises";
+import fsSync from "fs";
+import path from "path";
+import { fileURLToPath } from "url";
import { execSync } from "child_process";
-// 鑾峰彇 __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
-// 璇诲彇 JSON 閰嶇疆
-const data = await fs.readFile(path.join(__dirname, 'config.json'), 'utf-8');
+const data = await fs.readFile(path.join(__dirname, "config.json"), "utf-8");
const config = JSON.parse(data);
-// 椤圭洰璺緞
-const rootPath = path.resolve(__dirname, '..');
-const resourcePath = path.join(rootPath, 'multiple', 'assets');
-const replacePath = path.join(rootPath, 'replace');
+const rootPath = path.resolve(__dirname, "..");
+const resourcePath = path.join(rootPath, "multiple", "assets");
+const replacePath = path.join(rootPath, "replace");
+const envFilePath = path.join(rootPath, ".env.production.local");
-// 鑾峰彇鍛戒护琛屽弬鏁�
const params = parseArgs(process.argv);
-const company = params["company"] ?? "default";
+const company = resolveCompany(params);
const companyMap = config[company];
-const envFilePath = path.join(process.cwd(), '.env.production.local');
+if (!companyMap) {
+ const availableCompanies = Object.entries(config)
+ .filter(([, value]) => value && typeof value === "object" && value.env)
+ .map(([key]) => key)
+ .sort();
+ throw new Error(
+ `鏈煡 company: "${company}"銆傚彲閫夊��: ${availableCompanies.join(", ")}`
+ );
+}
+
+console.log(`褰撳墠 company: ${company}`);
async function copyFileWithOverwrite(src, dest) {
- await fs.mkdir(path.dirname(dest), { recursive: true });
- if (fsSync.existsSync(dest)) {
- try {
- await fs.chmod(dest, 0o666);
- } catch {
- // Ignore chmod failure and try delete directly.
- }
- await fs.rm(dest, { force: true });
+ await fs.mkdir(path.dirname(dest), { recursive: true });
+ if (fsSync.existsSync(dest)) {
+ try {
+ await fs.chmod(dest, 0o666);
+ } catch {
+ // Ignore chmod failure and continue.
}
- await fs.copyFile(src, dest);
+ await fs.rm(dest, { force: true });
+ }
+ await fs.copyFile(src, dest);
}
try {
- // 1锔忊儯 鐢熸垚 .env
- console.log("=======鐢熸垚.env=======");
- const envContent = Object.entries(companyMap.env)
- .map(([key, value]) => `${key}='${value}'`)
- .join('\n') + '\n';
- await fs.writeFile(envFilePath, envContent, 'utf-8');
+ console.log("=======鐢熸垚.env=======");
+ const envContent =
+ Object.entries(companyMap.env)
+ .map(([key, value]) => `${key}='${value}'`)
+ .join("\n") + "\n";
+ await fs.writeFile(envFilePath, envContent, "utf-8");
- // 2锔忊儯 澶囦唤鍘熷璧勬簮骞舵浛鎹�
- console.log("=======淇敼璧勬簮=======");
- for (const [key, value] of Object.entries(companyMap)) {
- if (key === 'env') continue;
+ console.log("=======淇敼璧勬簮=======");
+ for (const [key] of Object.entries(companyMap)) {
+ if (key === "env") continue;
- const originFile = path.join(rootPath, config[key]);
- const backupFile = path.join(replacePath, config[key]);
- const replaceFile = path.join(resourcePath, companyMap[key]);
+ const originFile = path.join(rootPath, config[key]);
+ const backupFile = path.join(replacePath, config[key]);
+ const replaceFile = path.join(resourcePath, companyMap[key]);
- await copyFileWithOverwrite(originFile, backupFile);
- await copyFileWithOverwrite(replaceFile, originFile);
- }
+ await copyFileWithOverwrite(originFile, backupFile);
+ await copyFileWithOverwrite(replaceFile, originFile);
+ }
- console.log("=====寮�濮嬫墦鍖�======");
- execSync("vite build", { stdio: "inherit" });
- console.log("=====鎵撳寘瀹屾垚======");
+ console.log("=====寮�濮嬫墦鍖�=====");
+ const buildEnv = createBuildEnv(companyMap.env);
+ execSync("vite build", { stdio: "inherit", cwd: rootPath, env: buildEnv });
+ console.log("=====鎵撳寘瀹屾垚======");
} finally {
- console.log("=====鎭㈠璧勬簮======");
+ console.log("=====鎭㈠璧勬簮======");
- // 鍒犻櫎涓存椂 .env 鏂囦欢
- if (fsSync.existsSync(envFilePath)) {
- await fs.unlink(envFilePath);
- console.log(`馃棏锔� 宸插垹闄� ${envFilePath}`);
+ if (fsSync.existsSync(envFilePath)) {
+ await fs.unlink(envFilePath);
+ console.log(`馃棏锔� 宸插垹闄� ${envFilePath}`);
+ }
+
+ if (fsSync.existsSync(replacePath)) {
+ for (const [key] of Object.entries(companyMap)) {
+ if (key === "env") continue;
+
+ const originFile = path.join(rootPath, config[key]);
+ const backupFile = path.join(replacePath, config[key]);
+ await copyFileWithOverwrite(backupFile, originFile);
}
-
- // 鎭㈠璧勬簮鏂囦欢
- if (fsSync.existsSync(replacePath)) {
- for (const [key, value] of Object.entries(companyMap)) {
- if (key === 'env') continue;
-
- const originFile = path.join(rootPath, config[key]);
- const backupFile = path.join(replacePath, config[key]);
-
- await copyFileWithOverwrite(backupFile, originFile);
- }
- await fs.rm(replacePath, { recursive: true, force: true });
- console.log(`馃棏锔� 宸插垹闄� ${replacePath}`);
- }
+ await fs.rm(replacePath, { recursive: true, force: true });
+ console.log(`馃棏锔� 宸插垹闄� ${replacePath}`);
+ }
}
-// 绠�鍗曞懡浠よ鍙傛暟瑙f瀽
function parseArgs(argv) {
- const params = {};
- for (const arg of argv.slice(2)) {
- if (arg.startsWith('--')) {
- const [key, value] = arg.slice(2).split('=');
- params[key] = value ?? true;
- }
+ const params = {};
+ for (let index = 2; index < argv.length; index++) {
+ const arg = argv[index];
+ if (!arg.startsWith("--")) continue;
+
+ const normalized = arg.slice(2);
+ const equalIndex = normalized.indexOf("=");
+ if (equalIndex >= 0) {
+ const key = normalized.slice(0, equalIndex);
+ const value = normalized.slice(equalIndex + 1);
+ params[key] = value || true;
+ continue;
}
- return params;
+
+ const nextArg = argv[index + 1];
+ if (nextArg && !nextArg.startsWith("--")) {
+ params[normalized] = nextArg;
+ index += 1;
+ continue;
+ }
+
+ params[normalized] = true;
+ }
+ return params;
+}
+
+function resolveCompany(parsedParams) {
+ const fromArg = parseValue(parsedParams.company);
+ if (fromArg) return fromArg;
+
+ const fromNpmConfig = parseValue(process.env.npm_config_company);
+ if (fromNpmConfig) return fromNpmConfig;
+
+ const fromEnv = parseValue(process.env.COMPANY ?? process.env.company);
+ if (fromEnv) return fromEnv;
+
+ return "default";
+}
+
+function parseValue(value) {
+ if (value == null || value === true) return undefined;
+ if (typeof value !== "string") return undefined;
+ const trimmed = value.trim();
+ if (!trimmed) return undefined;
+ return trimmed.replace(/^["']|["']$/g, "");
+}
+
+function createBuildEnv(companyEnv) {
+ const env = { ...process.env };
+ for (const key of Object.keys(env)) {
+ if (key.startsWith("VITE_")) {
+ delete env[key];
+ }
+ }
+ return {
+ ...env,
+ ...companyEnv,
+ VITE_APP_ENV: "production",
+ };
}
diff --git a/src/api/equipmentManagement/repair.js b/src/api/equipmentManagement/repair.js
index 0233ae6..16bfd28 100644
--- a/src/api/equipmentManagement/repair.js
+++ b/src/api/equipmentManagement/repair.js
@@ -70,3 +70,16 @@
data,
});
};
+
+/**
+ * @desc 楠屾敹瀹℃壒
+ * @param {楠屾敹鍙傛暟} data
+ * @returns
+ */
+export const repairAcceptance = (data) => {
+ return request({
+ url: `/device/repair/acceptance`,
+ method: "post",
+ data,
+ });
+};
diff --git a/src/api/inventoryManagement/stockInventory.js b/src/api/inventoryManagement/stockInventory.js
index 0ba0943..539eedc 100644
--- a/src/api/inventoryManagement/stockInventory.js
+++ b/src/api/inventoryManagement/stockInventory.js
@@ -17,6 +17,14 @@
});
};
+export const getStockInventoryBatchNoQty = (params) => {
+ return request({
+ url: "/stockInventory/getBatchNoQty",
+ method: "get",
+ params,
+ });
+};
+
// 鍒涘缓搴撳瓨璁板綍
export const createStockInventory = (params) => {
return request({
diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss
index be7b7a7..15d8078 100644
--- a/src/assets/styles/sidebar.scss
+++ b/src/assets/styles/sidebar.scss
@@ -225,14 +225,7 @@
width: 0;
overflow: hidden;
visibility: hidden;
- display: inline-block;
- }
- & > i {
- height: 0;
- width: 0;
- overflow: hidden;
- visibility: hidden;
- display: inline-block;
+ display: inline-block;
}
}
}
diff --git a/src/components/AIChatSidebar/assistants/index.js b/src/components/AIChatSidebar/assistants/index.js
index 61d4752..d7081b4 100644
--- a/src/components/AIChatSidebar/assistants/index.js
+++ b/src/components/AIChatSidebar/assistants/index.js
@@ -1,6 +1,15 @@
import { generalAssistant } from './generalAssistant'
import { purchaseAssistant } from './purchaseAssistant'
+import { productionAssistant } from './productionAssistant'
+import { salesAssistant } from './salesAssistant'
-export { generalAssistant, purchaseAssistant }
+export { generalAssistant, purchaseAssistant, productionAssistant, salesAssistant }
-export const builtInAssistants = [generalAssistant, purchaseAssistant]
+export const assistantRegistry = {
+ general: generalAssistant,
+ sales: salesAssistant,
+ purchase: purchaseAssistant,
+ production: productionAssistant
+}
+
+export const builtInAssistants = [generalAssistant, salesAssistant, purchaseAssistant, productionAssistant]
diff --git a/src/components/AIChatSidebar/assistants/productionAssistant.js b/src/components/AIChatSidebar/assistants/productionAssistant.js
new file mode 100644
index 0000000..fb2d737
--- /dev/null
+++ b/src/components/AIChatSidebar/assistants/productionAssistant.js
@@ -0,0 +1,27 @@
+import { Operation } from '@element-plus/icons-vue'
+
+export const productionAssistant = {
+ key: 'production',
+ label: '鐢熶骇鍔╃悊',
+ title: '鐢熶骇鏅鸿兘鍔╃悊',
+ tooltip: '鐢熶骇鏅鸿兘鍔╂墜',
+ icon: Operation,
+ apiBase: '/manufacturing-ai',
+ storageKey: 'production_ai_chat_uuid',
+ placeholder: '璇疯緭鍏ョ敓浜х浉鍏抽棶棰�... (Enter 鍙戦��, Shift+Enter 鎹㈣)',
+ welcomeMessage: '浣犲ソ',
+ description: '鎴戝彲浠ュ洿缁曠敓浜х幇鍦恒�佽鍒掋�佸伐鍗曘�佽澶囥�佽川閲忋�佺墿鏂欍�佸紓甯稿鐞嗘彁渚涙煡璇€�侀璀︺�佸垎鏋愬拰鍔炵悊寤鸿銆�',
+ allowFileUpload: false,
+ emptySessionText: '鏆傛棤鐢熶骇浼氳瘽',
+ quickPrompts: [
+ '鏌ヨ鏈湀鐢熶骇璁″垝',
+ '鏌ョ湅鏈�杩�10鏉″伐鍗�',
+ '鏌ヨ澶嘇-01鐨勭淮淇儏鍐�',
+ '鏌ヨ川閲忎笉鍚堟牸璁板綍',
+ '鏌ヤ綆搴撳瓨鐗╂枡',
+ '鏌ヨ繎7澶╁紓甯稿鐞�',
+ '鐢熸垚鍒堕�犻璀︾湅鏉�',
+ '鍒嗘瀽鏈湀鐢熶骇瀹屾垚鐜囧拰寮傚父鐜�',
+ '缁欏嚭宸ュ崟閫炬湡鍜岃澶囧緟淇殑鍔炵悊寤鸿'
+ ]
+}
diff --git a/src/components/AIChatSidebar/assistants/salesAssistant.js b/src/components/AIChatSidebar/assistants/salesAssistant.js
new file mode 100644
index 0000000..03cb102
--- /dev/null
+++ b/src/components/AIChatSidebar/assistants/salesAssistant.js
@@ -0,0 +1,28 @@
+import { TrendCharts } from '@element-plus/icons-vue'
+
+export const salesAssistant = {
+ key: 'sales',
+ label: '閿�鍞姪鎵�',
+ title: '閿�鍞櫤鑳藉姪鎵�',
+ tooltip: '閿�鍞櫤鑳藉姪鎵�',
+ icon: TrendCharts,
+ apiBase: '/sales-ai',
+ storageKey: 'sales_ai_chat_uuid',
+ placeholder: '璇疯緭鍏ラ攢鍞浉鍏抽棶棰�... (Enter 鍙戦�� / Shift+Enter 鎹㈣)',
+ welcomeMessage: '浣犲ソ',
+ description: '鎴戝彲浠ュ崗鍔╀綘鏌ヨ瀹㈡埛妗f銆侀攢鍞姤浠枫�侀攢鍞彴璐︺�侀攢鍞��璐с�佸鎴峰線鏉ャ�佸彂璐у彴璐︼紝骞堕噸鐐瑰垎鏋愬鎴锋祦澶遍闄╁強鍥炴/鎶ヤ环绛栫暐銆�',
+ allowFileUpload: false,
+ emptySessionText: '鏆傛棤閿�鍞細璇�',
+ quickPrompts: [
+ '鏌ヨ绉佹捣瀹㈡埛妗f鍓�10鏉�',
+ '鏌ヨ鍏捣瀹㈡埛妗f',
+ '鏌ヨ鏈湀閿�鍞姤浠�',
+ '鏌ヨ鏈湀閿�鍞彴璐�',
+ '鏌ヨ杩�30澶╅攢鍞��璐�',
+ '鏌ヨ杩�30澶╁鎴峰洖娆惧線鏉�',
+ '鏌ヨ鏈湀鍙戣揣鍙拌处',
+ '鏌ョ湅閿�鍞寚鏍囩粺璁�',
+ '甯垜鍋氬鎴锋祦澶遍闄╁垎鏋愶紝杩�30澶╋紝鍓�20鏉�',
+ '鐢熸垚鍥炴涓庢姤浠风瓥鐣ュ缓璁紝浼樺厛楂橀闄╁鎴�'
+ ]
+}
diff --git a/src/components/AIChatSidebar/index.vue b/src/components/AIChatSidebar/index.vue
index a2a365a..3d234d6 100644
--- a/src/components/AIChatSidebar/index.vue
+++ b/src/components/AIChatSidebar/index.vue
@@ -242,7 +242,253 @@
</el-table>
</div>
- <!-- 鎵撳瓧涓姩鐢� -->
+ <div v-if="message.manufacturingData" class="manufacturing-card">
+ <div class="manufacturing-card__title">{{ getManufacturingTypeLabel(message.type) }}</div>
+
+ <div
+ v-if="message.manufacturingData.summaryEntries?.length || message.manufacturingData.coreMetrics?.length"
+ class="manufacturing-summary-grid"
+ >
+ <div
+ v-for="(entry, entryIndex) in message.manufacturingData.summaryEntries"
+ :key="`summary-${entry.key}-${entryIndex}`"
+ class="manufacturing-summary-item"
+ >
+ <span class="manufacturing-summary-label">{{ entry.label }}</span>
+ <strong class="manufacturing-summary-value">{{ entry.value }}</strong>
+ </div>
+ <div
+ v-for="(metric, metricIndex) in message.manufacturingData.coreMetrics"
+ :key="`core-${metric.key}-${metricIndex}`"
+ class="manufacturing-summary-item manufacturing-summary-item--core"
+ >
+ <span class="manufacturing-summary-label">{{ metric.label }}</span>
+ <strong class="manufacturing-summary-value">{{ metric.value }}</strong>
+ </div>
+ </div>
+
+ <div v-if="message.manufacturingData.warningItems?.length" class="manufacturing-warning-list">
+ <div
+ v-for="(warning, warningIndex) in message.manufacturingData.warningItems"
+ :key="`warning-${warning.title || warningIndex}`"
+ class="manufacturing-warning-item"
+ >
+ <div class="manufacturing-warning-item__head">
+ <el-tag size="small" :type="getManufacturingWarningLevelType(warning.level)">
+ {{ getManufacturingWarningLevelLabel(warning.level) }}
+ </el-tag>
+ <strong>{{ warning.title || `棰勮 ${warningIndex + 1}` }}</strong>
+ <span v-if="warning.count !== '' && warning.count !== null && warning.count !== undefined" class="manufacturing-warning-count">
+ {{ warning.count }}
+ </span>
+ </div>
+ <p v-if="warning.detail" class="manufacturing-warning-detail">{{ warning.detail }}</p>
+ </div>
+ </div>
+
+ <div
+ v-if="message.manufacturingData.listItems?.length && message.manufacturingData.columns?.length"
+ class="table-wrapper manufacturing-table-wrapper"
+ >
+ <el-table :data="message.manufacturingData.listItems" border stripe size="small" style="width: 100%">
+ <el-table-column
+ v-for="col in message.manufacturingData.columns"
+ :key="col"
+ :label="getStructuredFieldLabel(col)"
+ min-width="140"
+ show-overflow-tooltip
+ >
+ <template #default="{ row }">
+ {{ formatStructuredValue(row[col]) }}
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+
+ <div v-if="message.manufacturingData.actionCards?.length" class="manufacturing-action-list">
+ <div
+ v-for="(card, cardIndex) in message.manufacturingData.actionCards"
+ :key="card.runtimeKey || `${card.code}-${card.targetApi}-${cardIndex}`"
+ class="manufacturing-action-card"
+ >
+ <div class="manufacturing-action-card__head">
+ <strong>{{ card.name || `鍔ㄤ綔 ${cardIndex + 1}` }}</strong>
+ <el-tag size="small" type="info">{{ getNormalizedRequestMethod(card.method) }}</el-tag>
+ </div>
+ <div class="manufacturing-action-card__meta">
+ <span>{{ card.code || '--' }}</span>
+ <span>{{ card.targetApi || '--' }}</span>
+ </div>
+ <p v-if="card.description" class="manufacturing-action-card__desc">{{ card.description }}</p>
+ <div v-if="card.requiredFields?.length" class="manufacturing-required-fields">
+ <span>蹇呭~瀛楁</span>
+ <el-tag
+ v-for="field in card.requiredFields"
+ :key="field"
+ size="small"
+ type="warning"
+ >
+ {{ getStructuredPathLabel(field) }}
+ </el-tag>
+ </div>
+ <el-input
+ v-model="card.payloadText"
+ type="textarea"
+ :rows="6"
+ resize="vertical"
+ :disabled="card.executing"
+ placeholder="璇疯緭鍏� JSON 璇锋眰鍙傛暟"
+ />
+ <div class="manufacturing-action-footer">
+ <span
+ v-if="card.executeResult"
+ :class="['manufacturing-action-result', card.executeError ? 'error' : 'success']"
+ >
+ {{ card.executeResult }}
+ </span>
+ <el-button
+ type="primary"
+ size="small"
+ :loading="card.executing"
+ @click="executeManufacturingAction(message, card, cardIndex)"
+ >
+ 纭骞舵墽琛�
+ </el-button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div v-if="message.salesData" class="sales-structured-card">
+ <div class="sales-structured-card__title">{{ getSalesTypeLabel(message.type) }}</div>
+
+ <div v-if="message.salesData.summaryEntries?.length" class="sales-summary-grid">
+ <div
+ v-for="(entry, entryIndex) in message.salesData.summaryEntries"
+ :key="`sales-summary-${entry.key}-${entryIndex}`"
+ class="sales-summary-item"
+ >
+ <span class="sales-summary-label">{{ entry.label }}</span>
+ <strong class="sales-summary-value">{{ entry.value }}</strong>
+ </div>
+ </div>
+
+ <div v-if="message.type === 'sales_customer_churn_risk' && message.salesData.listItems?.length" class="sales-focus-list">
+ <div
+ v-for="(item, itemIndex) in message.salesData.listItems"
+ :key="`risk-${item.customerName || itemIndex}`"
+ class="sales-focus-item"
+ >
+ <div class="sales-focus-item__head">
+ <strong>{{ formatStructuredValue(item.customerName) }}</strong>
+ <div class="sales-focus-tags">
+ <el-tag size="small" :type="getSalesLevelTagType(item.riskLevel)">
+ {{ getSalesLevelLabel(item.riskLevel, 'risk') }}
+ </el-tag>
+ <el-tag size="small" type="warning">椋庨櫓鍒� {{ formatStructuredValue(item.riskScore) }}</el-tag>
+ </div>
+ </div>
+ <div class="sales-focus-metrics">
+ <span>寰呭洖娆撅細{{ formatStructuredValue(item.pendingAmount) }}</span>
+ <span>寰呭洖娆惧崰姣旓細{{ formatStructuredValue(item.pendingRate) }}</span>
+ <span>璺濅笂娆′笅鍗曪細{{ formatStructuredValue(item.daysSinceLastOrder) }}</span>
+ </div>
+ <div v-if="toStructuredStringArray(item.riskReasons).length" class="sales-focus-reasons">
+ <el-tag
+ v-for="(reason, reasonIndex) in toStructuredStringArray(item.riskReasons)"
+ :key="`${item.customerName || itemIndex}-reason-${reasonIndex}`"
+ size="small"
+ type="danger"
+ effect="plain"
+ >
+ {{ reason }}
+ </el-tag>
+ </div>
+ </div>
+ </div>
+
+ <div v-if="message.type === 'sales_collection_quote_strategy' && message.salesData.listItems?.length" class="sales-focus-list">
+ <div
+ v-for="(item, itemIndex) in message.salesData.listItems"
+ :key="`strategy-${item.customerName || itemIndex}`"
+ class="sales-focus-item sales-focus-item--strategy"
+ >
+ <div class="sales-focus-item__head">
+ <strong>{{ formatStructuredValue(item.customerName) }}</strong>
+ <div class="sales-focus-tags">
+ <el-tag size="small" :type="getSalesLevelTagType(item.priority)">
+ {{ getSalesLevelLabel(item.priority, 'priority') }}
+ </el-tag>
+ <el-tag size="small" type="success">杞寲鐜� {{ formatStructuredValue(item.quoteConversionRate) }}</el-tag>
+ </div>
+ </div>
+ <div class="sales-focus-metrics">
+ <span>寰呭洖娆撅細{{ formatStructuredValue(item.pendingAmount) }}</span>
+ <span v-if="item.nextAction">涓嬩竴姝ワ細{{ formatStructuredValue(item.nextAction) }}</span>
+ </div>
+ <p v-if="item.collectionStrategy" class="sales-strategy-line">
+ <strong>鍥炴绛栫暐锛�</strong>{{ formatStructuredValue(item.collectionStrategy) }}
+ </p>
+ <p v-if="item.quotationStrategy" class="sales-strategy-line">
+ <strong>鎶ヤ环绛栫暐锛�</strong>{{ formatStructuredValue(item.quotationStrategy) }}
+ </p>
+ </div>
+ </div>
+
+ <div
+ v-if="message.salesData.listItems?.length && message.salesData.columns?.length && !isSalesFocusType(message.type)"
+ class="table-wrapper manufacturing-table-wrapper"
+ >
+ <el-table :data="message.salesData.listItems" border stripe size="small" style="width: 100%">
+ <el-table-column
+ v-for="col in message.salesData.columns"
+ :key="col"
+ :label="getStructuredFieldLabel(col)"
+ min-width="140"
+ show-overflow-tooltip
+ >
+ <template #default="{ row }">
+ {{ formatStructuredValue(row[col]) }}
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+
+ <div v-if="message.salesData.topCustomers?.length && message.salesData.topCustomerColumns?.length" class="table-wrapper manufacturing-table-wrapper">
+ <div class="sales-section-title">閲嶇偣瀹㈡埛</div>
+ <el-table :data="message.salesData.topCustomers" border stripe size="small" style="width: 100%">
+ <el-table-column
+ v-for="col in message.salesData.topCustomerColumns"
+ :key="`top-customer-${col}`"
+ :label="getStructuredFieldLabel(col)"
+ min-width="120"
+ show-overflow-tooltip
+ >
+ <template #default="{ row }">
+ {{ formatStructuredValue(row[col]) }}
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+
+ <div v-if="message.salesData.contractTrend?.length && message.salesData.contractTrendColumns?.length" class="table-wrapper manufacturing-table-wrapper">
+ <div class="sales-section-title">鍚堝悓瓒嬪娍</div>
+ <el-table :data="message.salesData.contractTrend" border stripe size="small" style="width: 100%">
+ <el-table-column
+ v-for="col in message.salesData.contractTrendColumns"
+ :key="`contract-trend-${col}`"
+ :label="getStructuredFieldLabel(col)"
+ min-width="120"
+ show-overflow-tooltip
+ >
+ <template #default="{ row }">
+ {{ formatStructuredValue(row[col]) }}
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </div>
+
<div v-if="message.purchaseAnalysisData" class="purchase-confirm-card">
<div class="purchase-confirm-header">
<span>{{ businessTypeLabelMap[message.purchaseAnalysisData.businessType] || message.purchaseAnalysisData.businessType || '閲囪喘涓氬姟' }}</span>
@@ -523,7 +769,7 @@
import request from '@/utils/request'
import * as echarts from 'echarts'
import { Cpu, User, Plus, Timer, Delete, ChatDotSquare, VideoPause, Upload, Document, Close, Promotion, RefreshRight } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
import { builtInAssistants, generalAssistant } from './assistants'
import todoAssistantAvatar from '@/assets/AI/寰呭姙鍔╂墜.png'
import salesAssistantAvatar from '@/assets/AI/閿�鍞姪鎵�.png'
@@ -634,6 +880,119 @@
purchase_return_order: '閲囪喘閫�璐у崟',
unknown: '鏈煡閲囪喘涓氬姟'
}
+const salesStructuredTypeSet = new Set([
+ 'sales_customer_profile_list',
+ 'sales_quotation_list',
+ 'sales_ledger_list',
+ 'sales_return_list',
+ 'sales_customer_interaction_list',
+ 'sales_shipping_list',
+ 'sales_dashboard',
+ 'sales_customer_churn_risk',
+ 'sales_collection_quote_strategy'
+])
+const salesFocusTypeSet = new Set([
+ 'sales_customer_churn_risk',
+ 'sales_collection_quote_strategy'
+])
+const salesTypeLabelMap = {
+ sales_customer_profile_list: '瀹㈡埛妗f',
+ sales_quotation_list: '閿�鍞姤浠�',
+ sales_ledger_list: '閿�鍞彴璐�',
+ sales_return_list: '閿�鍞��璐�',
+ sales_customer_interaction_list: '瀹㈡埛寰�鏉�',
+ sales_shipping_list: '鍙戣揣鍙拌处',
+ sales_dashboard: '閿�鍞寚鏍囩粺璁�',
+ sales_customer_churn_risk: '瀹㈡埛娴佸け椋庨櫓鍒嗘瀽',
+ sales_collection_quote_strategy: '鍥炴涓庢姤浠风瓥鐣ュ缓璁�'
+}
+const manufacturingStructuredTypeSet = new Set([
+ 'manufacturing_site_snapshot',
+ 'manufacturing_plan_list',
+ 'manufacturing_workorder_list',
+ 'manufacturing_device_list',
+ 'manufacturing_device_repair_list',
+ 'manufacturing_quality_list',
+ 'manufacturing_material_list',
+ 'manufacturing_exception_list',
+ 'manufacturing_warning',
+ 'manufacturing_analysis',
+ 'manufacturing_action_plan'
+])
+const manufacturingListTypeSet = new Set([
+ 'manufacturing_plan_list',
+ 'manufacturing_workorder_list',
+ 'manufacturing_device_list',
+ 'manufacturing_device_repair_list',
+ 'manufacturing_quality_list',
+ 'manufacturing_material_list',
+ 'manufacturing_exception_list'
+])
+const manufacturingTypeLabelMap = {
+ manufacturing_site_snapshot: '鐢熶骇鐜板満姒傝',
+ manufacturing_plan_list: '璁″垝鏌ヨ',
+ manufacturing_workorder_list: '宸ュ崟鏌ヨ',
+ manufacturing_device_list: '璁惧鏌ヨ',
+ manufacturing_device_repair_list: '璁惧缁翠慨璁板綍鏌ヨ',
+ manufacturing_quality_list: '璐ㄩ噺鏌ヨ',
+ manufacturing_material_list: '鐗╂枡鏌ヨ',
+ manufacturing_exception_list: '寮傚父鏌ヨ',
+ manufacturing_warning: '棰勮鐪嬫澘',
+ manufacturing_analysis: '缁忚惀鍒嗘瀽',
+ manufacturing_action_plan: '鍔炵悊寤鸿'
+}
+const structuredFieldLabelMap = {
+ workOrderNo: '宸ュ崟鍙�',
+ planEndTime: '璁″垝缁撴潫鏃堕棿',
+ planStartTime: '璁″垝寮�濮嬫椂闂�',
+ timeRange: '鏃堕棿鑼冨洿',
+ startDate: '寮�濮嬫棩鏈�',
+ endDate: '缁撴潫鏃ユ湡',
+ warningCount: '棰勮鏁伴噺',
+ overduePlanCount: '閫炬湡璁″垝鏁�',
+ overdueWorkOrderCount: '閫炬湡宸ュ崟鏁�',
+ actionCount: '寤鸿鍔ㄤ綔鏁�',
+ qualityOpenCount: '璐ㄩ噺寰呭鐞嗘暟',
+ lowStockCount: '浣庡簱瀛樻暟',
+ exceptionCount: '寮傚父鏁�',
+ userId: '鐢ㄦ埛ID',
+ tenantId: '绉熸埛ID',
+ status: '鐘舵��',
+ deviceName: '璁惧鍚嶇О',
+ deviceModel: '璁惧鍨嬪彿',
+ pendingRepairCount: '寰呯淮淇暟',
+ repairTime: '缁翠慨鏃堕棿',
+ repairName: '鎶ヤ慨浜�',
+ maintenanceName: '缁翠慨浜哄憳',
+ level: '棰勮绛夌骇',
+ title: '鏍囬',
+ count: '鏁伴噺',
+ detail: '璇︽儏',
+ remark: '澶囨敞',
+ createTime: '鍒涘缓鏃堕棿',
+ updateTime: '鏇存柊鏃堕棿',
+ exceptionType: '寮傚父绫诲瀷',
+ materialName: '鐗╂枡鍚嶇О',
+ stockQty: '搴撳瓨閲�'
+}
+Object.assign(structuredFieldLabelMap, {
+ customerName: '瀹㈡埛鍚嶇О',
+ riskLevel: '椋庨櫓绛夌骇',
+ riskScore: '椋庨櫓璇勫垎',
+ riskReasons: '椋庨櫓鍘熷洜',
+ pendingAmount: '寰呭洖娆鹃噾棰�',
+ pendingRate: '寰呭洖娆惧崰姣�',
+ daysSinceLastOrder: '璺濅笂娆′笅鍗曞ぉ鏁�',
+ priority: '浼樺厛绾�',
+ quoteConversionRate: '鎶ヤ环杞寲鐜�',
+ collectionStrategy: '鍥炴绛栫暐',
+ quotationStrategy: '鎶ヤ环绛栫暐',
+ nextAction: '涓嬩竴姝ュ姩浣�',
+ contractAmountTotal: '鍚堝悓鎬婚',
+ receivedAmountTotal: '宸插洖娆鹃噾棰�',
+ pendingAmountTotal: '寰呭洖娆炬�婚',
+ shipRate: '鍙戣揣鐜�'
+})
const purchasePayloadFieldLabelMap = {
purchaseLedgers: '閲囪喘鍙拌处',
productData: '浜у搧鏄庣粏',
@@ -785,6 +1144,437 @@
inventoryWarningQuantity: 'inventoryWarningQuantity',
isInspected: 'isInspected',
isChecked: 'isInspected'
+}
+const isPlainObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value)
+
+const stringifyStructuredPayload = (value, spaces = 2) => {
+ if (typeof value === 'string') return value
+ try {
+ return JSON.stringify(value ?? {}, null, spaces)
+ } catch (err) {
+ return '{}'
+ }
+}
+
+const structuredFieldTokenLabelMap = {
+ time: '鏃堕棿',
+ range: '鑼冨洿',
+ start: '寮�濮�',
+ end: '缁撴潫',
+ date: '鏃ユ湡',
+ warning: '棰勮',
+ overdue: '閫炬湡',
+ plan: '璁″垝',
+ work: '宸�',
+ order: '鍗�',
+ workorder: '宸ュ崟',
+ count: '鏁伴噺',
+ quality: '璐ㄩ噺',
+ low: '浣�',
+ stock: '搴撳瓨',
+ exception: '寮傚父',
+ action: '鍔ㄤ綔',
+ user: '鐢ㄦ埛',
+ tenant: '绉熸埛',
+ id: 'ID',
+ no: '缂栧彿',
+ number: '缂栧彿',
+ code: '缂栫爜',
+ name: '鍚嶇О',
+ status: '鐘舵��',
+ level: '绛夌骇',
+ title: '鏍囬',
+ detail: '璇︽儏',
+ total: '鎬绘暟',
+ rate: '姣旂巼',
+ type: '绫诲瀷',
+ pending: '寰�',
+ repair: '缁翠慨',
+ device: '璁惧',
+ material: '鐗╂枡'
+}
+
+const convertStructuredFieldKeyToChinese = (fieldKey = '') => {
+ const key = String(fieldKey || '').trim()
+ if (!key) return '-'
+ if (structuredFieldLabelMap[key]) return structuredFieldLabelMap[key]
+ if (/[\u4e00-\u9fa5]/.test(key)) return key
+
+ const rawTokens = key
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
+ .replace(/[_-]+/g, ' ')
+ .trim()
+ .split(/\s+/)
+ .filter(Boolean)
+ .map(token => token.toLowerCase())
+
+ if (!rawTokens.length) return '瀛楁'
+
+ const mappedTokens = rawTokens
+ .map(token => structuredFieldTokenLabelMap[token] || '')
+ .filter(Boolean)
+
+ if (mappedTokens.length) {
+ return mappedTokens.join('')
+ }
+
+ return '瀛楁'
+}
+
+const getStructuredFieldLabel = (fieldKey = '') => {
+ return convertStructuredFieldKeyToChinese(fieldKey)
+}
+
+const getStructuredPathLabel = (fieldPath = '') => {
+ const path = String(fieldPath || '').trim()
+ if (!path) return '-'
+
+ const segments = path
+ .replace(/\[(\d+)]/g, '.$1')
+ .split('.')
+ .filter(Boolean)
+
+ if (!segments.length) return getStructuredFieldLabel(path)
+
+ return segments.map((segment) => {
+ if (/^\d+$/.test(segment)) {
+ return `绗�${Number(segment) + 1}椤筦
+ }
+ return getStructuredFieldLabel(segment)
+ }).join(' / ')
+}
+
+const formatStructuredValue = (value) => {
+ if (value === null || value === undefined || value === '') return '-'
+ if (Array.isArray(value)) {
+ const preview = value.slice(0, 3).map(item => formatStructuredValue(item)).join('銆�')
+ return value.length > 3 ? `${preview} 绛�${value.length}椤筦 : preview
+ }
+ if (isPlainObject(value)) return stringifyStructuredPayload(value, 0)
+ return String(value)
+}
+
+const normalizeManufacturingSummaryEntries = (summary) => {
+ if (!isPlainObject(summary)) return []
+ return Object.entries(summary)
+ .filter(([, value]) => value !== undefined && value !== null && `${value}`.trim() !== '')
+ .map(([key, value]) => ({
+ key,
+ label: getStructuredFieldLabel(key),
+ value: formatStructuredValue(value)
+ }))
+}
+
+const normalizeManufacturingCoreMetrics = (coreMetrics) => {
+ if (Array.isArray(coreMetrics)) {
+ return coreMetrics.map((item, index) => {
+ if (isPlainObject(item)) {
+ const label = item.label || item.name || item.key || `鎸囨爣${index + 1}`
+ const metricValue = item.value ?? item.metricValue ?? item.data ?? '-'
+ const unit = item.unit ? ` ${item.unit}` : ''
+ return {
+ key: String(item.key || item.name || index),
+ label,
+ value: `${formatStructuredValue(metricValue)}${unit}`.trim()
+ }
+ }
+ return {
+ key: String(index),
+ label: `鎸囨爣${index + 1}`,
+ value: formatStructuredValue(item)
+ }
+ })
+ }
+
+ if (isPlainObject(coreMetrics)) {
+ return Object.entries(coreMetrics).map(([key, value]) => ({
+ key,
+ label: getStructuredFieldLabel(key),
+ value: formatStructuredValue(value)
+ }))
+ }
+
+ return []
+}
+
+const normalizeManufacturingWarningItems = (items = []) => {
+ if (!Array.isArray(items)) return []
+ return items
+ .filter(item => isPlainObject(item))
+ .map(item => ({
+ level: String(item.level || '').toLowerCase(),
+ title: item.title || '',
+ count: item.count ?? '',
+ detail: item.detail ?? ''
+ }))
+}
+
+const inferManufacturingColumns = (items = []) => {
+ if (!Array.isArray(items) || !items.length) return []
+ const fieldSet = new Set()
+ items.forEach((item) => {
+ if (!isPlainObject(item)) return
+ Object.keys(item).forEach((key) => fieldSet.add(key))
+ })
+ return Array.from(fieldSet)
+}
+
+const getManufacturingActionCardRuntimeKey = (card = {}, index = 0) => {
+ const code = String(card?.code || '').trim()
+ const api = String(card?.targetApi || '').trim()
+ const name = String(card?.name || '').trim()
+ return `${code}::${api}::${name}::${index}`
+}
+
+const normalizeManufacturingActionCards = (actionCards = [], previousCards = []) => {
+ const previousMap = new Map()
+ if (Array.isArray(previousCards)) {
+ previousCards.forEach((card, index) => {
+ previousMap.set(getManufacturingActionCardRuntimeKey(card, index), card)
+ })
+ }
+
+ return (Array.isArray(actionCards) ? actionCards : [])
+ .filter(card => isPlainObject(card))
+ .map((card, index) => {
+ const runtimeKey = getManufacturingActionCardRuntimeKey(card, index)
+ const previousCard = previousMap.get(runtimeKey)
+ const fallbackPayloadText = stringifyStructuredPayload(card.examplePayload, 2)
+ return {
+ ...card,
+ runtimeKey,
+ payloadText: previousCard?.payloadText ?? fallbackPayloadText,
+ executing: Boolean(previousCard?.executing),
+ executed: Boolean(previousCard?.executed),
+ executeResult: previousCard?.executeResult || '',
+ executeError: Boolean(previousCard?.executeError)
+ }
+ })
+}
+
+const buildManufacturingStructuredData = (parsedData, previousData = null) => {
+ const type = String(parsedData?.type || '')
+ if (!manufacturingStructuredTypeSet.has(type)) return null
+
+ const rawData = isPlainObject(parsedData?.data) ? parsedData.data : {}
+ const items = Array.isArray(rawData.items) ? rawData.items.filter(item => isPlainObject(item)) : []
+ const warningItems = type === 'manufacturing_warning' ? normalizeManufacturingWarningItems(items) : []
+ const listItems = manufacturingListTypeSet.has(type) ? items : []
+ const actionCards = type === 'manufacturing_action_plan'
+ ? normalizeManufacturingActionCards(rawData.actionCards, previousData?.actionCards)
+ : []
+
+ return {
+ type,
+ summaryEntries: normalizeManufacturingSummaryEntries(parsedData?.summary),
+ coreMetrics: normalizeManufacturingCoreMetrics(rawData.coreMetrics),
+ listItems,
+ columns: inferManufacturingColumns(listItems),
+ warningItems,
+ actionCards
+ }
+}
+
+const getManufacturingTypeLabel = (type = '') => manufacturingTypeLabelMap[String(type || '')] || '鍒堕�犵粨鏋�'
+
+const inferSalesColumns = (items = []) => {
+ if (!Array.isArray(items) || !items.length) return []
+ const fieldSet = new Set()
+ items.forEach((item) => {
+ if (!isPlainObject(item)) return
+ Object.keys(item).forEach((key) => fieldSet.add(key))
+ })
+ return Array.from(fieldSet)
+}
+
+const normalizeSalesListItems = (items) => {
+ if (!Array.isArray(items)) return []
+ return items.filter(item => isPlainObject(item))
+}
+
+const buildSalesStructuredData = (parsedData) => {
+ const type = String(parsedData?.type || '')
+ if (!salesStructuredTypeSet.has(type)) return null
+
+ const rawData = isPlainObject(parsedData?.data) ? parsedData.data : {}
+ const listItems = normalizeSalesListItems(rawData.items)
+ const topCustomers = normalizeSalesListItems(rawData.topCustomers)
+ const contractTrend = normalizeSalesListItems(rawData.contractTrend)
+
+ return {
+ type,
+ summaryEntries: normalizeManufacturingSummaryEntries(parsedData?.summary),
+ listItems,
+ columns: inferSalesColumns(listItems),
+ topCustomers,
+ topCustomerColumns: inferSalesColumns(topCustomers),
+ contractTrend,
+ contractTrendColumns: inferSalesColumns(contractTrend)
+ }
+}
+
+const getSalesTypeLabel = (type = '') => salesTypeLabelMap[String(type || '')] || '閿�鍞煡璇㈢粨鏋�'
+
+const isSalesFocusType = (type = '') => salesFocusTypeSet.has(String(type || ''))
+
+const getSalesLevelTagType = (level = '') => {
+ const normalizedLevel = String(level || '').toLowerCase()
+ if (normalizedLevel === 'high') return 'danger'
+ if (normalizedLevel === 'medium') return 'warning'
+ if (normalizedLevel === 'low') return 'success'
+ return 'info'
+}
+
+const getSalesLevelLabel = (level = '', mode = 'risk') => {
+ const normalizedLevel = String(level || '').toLowerCase()
+ const suffix = mode === 'priority' ? '浼樺厛绾�' : '椋庨櫓'
+ if (normalizedLevel === 'high') return `楂�${suffix}`
+ if (normalizedLevel === 'medium') return `涓�${suffix}`
+ if (normalizedLevel === 'low') return `浣�${suffix}`
+ if (!normalizedLevel) return mode === 'priority' ? '鏈垎绾�' : '鏈瘎浼�'
+ return normalizedLevel.toUpperCase()
+}
+
+const toStructuredStringArray = (value) => {
+ if (Array.isArray(value)) {
+ return value.map(item => String(item || '').trim()).filter(Boolean)
+ }
+ if (typeof value === 'string') {
+ return value
+ .split(/[,\uFF0C\u3001;\uFF1B\n]/)
+ .map(item => item.trim())
+ .filter(Boolean)
+ }
+ return []
+}
+
+const getManufacturingWarningLevelType = (level = '') => {
+ const normalizedLevel = String(level || '').toLowerCase()
+ if (normalizedLevel === 'high') return 'danger'
+ if (normalizedLevel === 'medium') return 'warning'
+ return 'info'
+}
+
+const getManufacturingWarningLevelLabel = (level = '') => {
+ const normalizedLevel = String(level || '').toLowerCase()
+ if (normalizedLevel === 'high') return '楂�'
+ if (normalizedLevel === 'medium') return '涓�'
+ return normalizedLevel ? normalizedLevel.toUpperCase() : '涓�鑸�'
+}
+
+const normalizeRequestMethod = (method = 'POST') => {
+ const normalized = String(method || 'POST').trim().toUpperCase()
+ if (['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(normalized)) return normalized
+ return 'POST'
+}
+
+const getNormalizedRequestMethod = (method) => normalizeRequestMethod(method)
+
+const getPayloadValueByPath = (payload, fieldPath = '') => {
+ const normalizedPath = String(fieldPath || '').trim()
+ if (!normalizedPath || !isPlainObject(payload)) return undefined
+
+ const pathSegments = normalizedPath
+ .replace(/\[(\d+)]/g, '.$1')
+ .split('.')
+ .filter(Boolean)
+
+ return pathSegments.reduce((current, segment) => {
+ if (current === null || current === undefined) return undefined
+ if (!['object', 'function'].includes(typeof current)) return undefined
+ return current[segment]
+ }, payload)
+}
+
+const getMissingRequiredFields = (requiredFields = [], payload = {}) => {
+ if (!Array.isArray(requiredFields) || !requiredFields.length) return []
+ return requiredFields.filter((fieldPath) => {
+ const value = getPayloadValueByPath(payload, fieldPath)
+ return !hasMeaningfulPayloadValue(value)
+ })
+}
+
+const parseManufacturingActionPayload = (payloadText = '') => {
+ const text = String(payloadText ?? '').trim()
+ if (!text) return {}
+ return JSON.parse(text)
+}
+
+const executeManufacturingAction = async (message, actionCard, cardIndex = 0) => {
+ if (!message?.manufacturingData || !actionCard || actionCard.executing) return
+
+ const actionName = actionCard.name || `鍔ㄤ綔 ${cardIndex + 1}`
+ const targetApi = String(actionCard.targetApi || '').trim()
+ if (!targetApi) {
+ actionCard.executeError = true
+ actionCard.executeResult = '缂哄皯 targetApi锛屾棤娉曟墽琛屽姩浣�'
+ return
+ }
+
+ let payload = {}
+ try {
+ payload = parseManufacturingActionPayload(actionCard.payloadText)
+ } catch (err) {
+ actionCard.executeError = true
+ actionCard.executeResult = '璇锋眰鍙傛暟涓嶆槸鍚堟硶 JSON锛岃妫�鏌ュ悗閲嶈瘯'
+ return
+ }
+
+ const requiredFields = Array.isArray(actionCard.requiredFields) ? actionCard.requiredFields : []
+ if (requiredFields.length && !isPlainObject(payload)) {
+ actionCard.executeError = true
+ actionCard.executeResult = '蹇呭~瀛楁鏍¢獙澶辫触锛氳姹傚弬鏁板繀椤绘槸 JSON 瀵硅薄'
+ return
+ }
+
+ const missingFields = getMissingRequiredFields(requiredFields, payload)
+ if (missingFields.length) {
+ actionCard.executeError = true
+ const missingFieldLabels = missingFields.map(field => getStructuredPathLabel(field))
+ actionCard.executeResult = `缂哄皯蹇呭~瀛楁锛�${missingFieldLabels.join('銆�')}`
+ return
+ }
+
+ try {
+ await ElMessageBox.confirm(`纭鎵ц銆�${actionName}銆嶅悧锛焋, '鎵ц纭', {
+ confirmButtonText: '纭鎵ц',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ })
+ } catch (err) {
+ return
+ }
+
+ actionCard.executing = true
+ actionCard.executeError = false
+ actionCard.executeResult = ''
+
+ const method = normalizeRequestMethod(actionCard.method)
+ const requestConfig = {
+ url: targetApi,
+ method: method.toLowerCase()
+ }
+
+ if (method === 'GET') {
+ requestConfig.params = payload
+ } else {
+ requestConfig.data = payload
+ }
+
+ try {
+ const res = await request(requestConfig)
+ const successMsg = res?.msg || `${actionName}鎵ц鎴愬姛`
+ actionCard.executed = true
+ actionCard.executeError = false
+ actionCard.executeResult = successMsg
+ ElMessage.success(successMsg)
+ } catch (err) {
+ actionCard.executed = false
+ actionCard.executeError = true
+ actionCard.executeResult = err?.message || `${actionName}鎵ц澶辫触锛岃绋嶅悗閲嶈瘯`
+ } finally {
+ actionCard.executing = false
+ }
}
// 鍘嗗彶浼氳瘽鐩稿叧
@@ -1034,6 +1824,9 @@
tableData: null,
payloadTreeData: null,
payloadHiddenData: null,
+ purchaseAnalysisData: null,
+ manufacturingData: null,
+ salesData: null,
localUploadFiles: isUser ? mapHistoryFilePathsToSnapshots(msg.filePaths, uuid.value, idx) : []
}
@@ -1273,15 +2066,32 @@
const applyStructuredMessageData = (messageObj, parsedData, msgIndex, shouldRenderCharts = true) => {
if (!messageObj || !parsedData?.success) return
+ const previousManufacturingData = messageObj.manufacturingData
messageObj.type = parsedData.type || ''
+ messageObj.tableData = null
+ messageObj.purchaseAnalysisData = null
+ messageObj.manufacturingData = null
+ messageObj.salesData = null
if (messageObj.type === 'todo_list' && parsedData.data) {
messageObj.tableData = parsedData.data
}
+ const salesData = buildSalesStructuredData(parsedData)
+ if (salesData) {
+ messageObj.salesData = salesData
+ }
+
+ const manufacturingData = buildManufacturingStructuredData(parsedData, previousManufacturingData)
+ if (manufacturingData) {
+ messageObj.manufacturingData = manufacturingData
+ }
+
if (parsedData.action === 'confirm_required' && parsedData.businessType) {
messageObj.type = 'purchase_analysis_confirm'
messageObj.purchaseAnalysisData = parsedData
+ messageObj.manufacturingData = null
+ messageObj.salesData = null
if (!Array.isArray(messageObj.payloadTreeData) || !messageObj.payloadTreeData.length) {
initializePurchasePayloadTree(messageObj, parsedData.payload || {})
}
@@ -1321,6 +2131,25 @@
}
return null
+}
+
+const getStructuredFallbackText = (parsedData) => {
+ if (!parsedData) return '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
+ if (parsedData.type === 'todo_list') return '宸蹭负鎮ㄦ暣鐞嗗ソ鐩稿叧鏁版嵁銆�'
+ if (salesStructuredTypeSet.has(parsedData.type)) {
+ if (parsedData.type === 'sales_customer_churn_risk') return '宸蹭负鎮ㄧ敓鎴愬鎴锋祦澶遍闄╁垎鏋愩��'
+ if (parsedData.type === 'sales_collection_quote_strategy') return '宸蹭负鎮ㄧ敓鎴愬洖娆句笌鎶ヤ环绛栫暐寤鸿銆�'
+ if (parsedData.type === 'sales_dashboard') return '宸蹭负鎮ㄧ敓鎴愰攢鍞寚鏍囩粺璁°��'
+ return '宸茶繑鍥為攢鍞煡璇㈢粨鏋溿��'
+ }
+ if (manufacturingStructuredTypeSet.has(parsedData.type)) {
+ if (parsedData.type === 'manufacturing_action_plan') return '宸蹭负鎮ㄧ敓鎴愬姙鐞嗗缓璁紝璇风‘璁ゅ姩浣滃悗鎵ц銆�'
+ if (parsedData.type === 'manufacturing_warning') return '宸蹭负鎮ㄧ敓鎴愬埗閫犻璀︾湅鏉裤��'
+ if (parsedData.type === 'manufacturing_analysis') return '宸蹭负鎮ㄧ敓鎴愬埗閫犲垎鏋愮粨鏋溿��'
+ return '宸茶繑鍥炲埗閫犳煡璇㈢粨鏋溿��'
+ }
+ if (parsedData.charts && Object.keys(parsedData.charts).length > 0) return '宸蹭负鎮ㄧ敓鎴愬垎鏋愬浘琛ㄣ��'
+ return '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
}
const buildPurchaseMaterialRankCharts = (parsedData) => {
@@ -2446,7 +3275,10 @@
type: '',
tableData: null,
payloadTreeData: null,
- payloadHiddenData: null
+ payloadHiddenData: null,
+ purchaseAnalysisData: null,
+ manufacturingData: null,
+ salesData: null
})
outputState.value[botMsgIndex] = {
@@ -2569,7 +3401,10 @@
type: '',
tableData: null,
payloadTreeData: null,
- payloadHiddenData: null
+ payloadHiddenData: null,
+ purchaseAnalysisData: null,
+ manufacturingData: null,
+ salesData: null
}
messages.value.push(botMsg)
@@ -2735,13 +3570,7 @@
}
if (!display) {
- if (parsed.type === 'todo_list') {
- display = '宸蹭负鎮ㄦ暣鐞嗗ソ鐩稿叧鏁版嵁銆�'
- } else if (parsed.charts && Object.keys(parsed.charts).length > 0) {
- display = '宸蹭负鎮ㄧ敓鎴愬垎鏋愬浘琛ㄣ��'
- } else {
- display = '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
- }
+ display = getStructuredFallbackText(parsed)
}
} else if (startIdx !== -1) {
const lastBraceIdx = output.lastIndexOf('}')
@@ -2763,13 +3592,7 @@
}
if (!display) {
- if (parsed.type === 'todo_list') {
- display = '宸蹭负鎮ㄦ暣鐞嗗ソ鐩稿叧鏁版嵁锛�'
- } else if (parsed.charts && Object.keys(parsed.charts).length > 0) {
- display = '宸蹭负鎮ㄧ敓鎴愬垎鏋愬浘琛細'
- } else {
- display = '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
- }
+ display = getStructuredFallbackText(parsed)
}
} catch (e) {
// 瑙f瀽澶辫触锛岃鏄� JSON 杩樺湪浼犺緭涓垨鏍煎紡涓嶆纭�
@@ -3792,6 +4615,284 @@
}
}
+.manufacturing-card {
+ margin-top: 12px;
+ width: 100%;
+ background: #fff;
+ border: 1px solid rgba(0, 85, 212, 0.12);
+ border-radius: 12px;
+ box-shadow: $shadow-card;
+ padding: 14px;
+}
+
+.manufacturing-card__title {
+ font-size: 14px;
+ font-weight: 700;
+ color: $deep-blue;
+ margin-bottom: 10px;
+}
+
+.manufacturing-summary-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: 8px;
+ margin-bottom: 12px;
+}
+
+.manufacturing-summary-item {
+ border-radius: 10px;
+ padding: 10px 12px;
+ border: 1px solid rgba(0, 85, 212, 0.08);
+ background: linear-gradient(180deg, #f8fbff, #f1f7ff);
+ min-height: 66px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ gap: 6px;
+}
+
+.manufacturing-summary-item--core {
+ border-color: rgba(30, 91, 255, 0.24);
+}
+
+.manufacturing-summary-label {
+ font-size: 12px;
+ color: #4b5563;
+}
+
+.manufacturing-summary-value {
+ font-size: 15px;
+ color: #1f2937;
+ line-height: 1.4;
+ word-break: break-all;
+}
+
+.manufacturing-warning-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 12px;
+}
+
+.manufacturing-warning-item {
+ border-radius: 10px;
+ border: 1px solid rgba(245, 158, 11, 0.22);
+ background: linear-gradient(135deg, rgba(255, 247, 237, 0.9), rgba(255, 255, 255, 0.98));
+ padding: 10px 12px;
+}
+
+.manufacturing-warning-item__head {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: #92400e;
+ font-size: 13px;
+}
+
+.manufacturing-warning-count {
+ margin-left: auto;
+ font-weight: 700;
+ color: #c2410c;
+}
+
+.manufacturing-warning-detail {
+ margin: 8px 0 0;
+ font-size: 12px;
+ line-height: 1.6;
+ color: #7c2d12;
+ word-break: break-all;
+}
+
+.manufacturing-table-wrapper {
+ margin-top: 10px;
+}
+
+.manufacturing-action-list {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin-top: 12px;
+}
+
+.manufacturing-action-card {
+ border: 1px solid rgba(0, 85, 212, 0.1);
+ border-radius: 10px;
+ padding: 10px 12px;
+ background: #f8fbff;
+}
+
+.manufacturing-action-card__head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ font-size: 13px;
+ color: #1f2937;
+ margin-bottom: 8px;
+}
+
+.manufacturing-action-card__meta {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-bottom: 8px;
+ font-size: 12px;
+ color: #64748b;
+ word-break: break-all;
+}
+
+.manufacturing-action-card__desc {
+ margin: 0 0 8px;
+ font-size: 12px;
+ line-height: 1.6;
+ color: #475467;
+}
+
+.manufacturing-required-fields {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 6px;
+ margin-bottom: 8px;
+ font-size: 12px;
+ color: #7c2d12;
+}
+
+.manufacturing-action-footer {
+ margin-top: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 12px;
+}
+
+.manufacturing-action-result {
+ flex: 1;
+ font-size: 12px;
+ line-height: 1.5;
+
+ &.success {
+ color: #1f9d55;
+ }
+
+ &.error {
+ color: #d93025;
+ }
+}
+
+.sales-structured-card {
+ margin-top: 12px;
+ width: 100%;
+ background: #fff;
+ border: 1px solid rgba(31, 122, 114, 0.2);
+ border-radius: 12px;
+ box-shadow: $shadow-card;
+ padding: 14px;
+}
+
+.sales-structured-card__title {
+ font-size: 14px;
+ font-weight: 700;
+ color: #1f5ddf;
+ margin-bottom: 10px;
+}
+
+.sales-summary-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: 8px;
+ margin-bottom: 12px;
+}
+
+.sales-summary-item {
+ border-radius: 10px;
+ padding: 10px 12px;
+ border: 1px solid rgba(30, 91, 255, 0.12);
+ background: linear-gradient(180deg, #f7fbff, #edf6ff);
+ min-height: 66px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ gap: 6px;
+}
+
+.sales-summary-label {
+ font-size: 12px;
+ color: #4b5563;
+}
+
+.sales-summary-value {
+ font-size: 15px;
+ color: #1f2937;
+ line-height: 1.4;
+ word-break: break-all;
+}
+
+.sales-focus-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.sales-focus-item {
+ border-radius: 10px;
+ border: 1px solid rgba(30, 91, 255, 0.14);
+ background: #f8fbff;
+ padding: 10px 12px;
+}
+
+.sales-focus-item--strategy {
+ border-color: rgba(31, 122, 114, 0.22);
+ background: linear-gradient(180deg, #f7fcfb, #edf9f6);
+}
+
+.sales-focus-item__head {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 10px;
+ font-size: 13px;
+ color: #1f2937;
+}
+
+.sales-focus-tags {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+}
+
+.sales-focus-metrics {
+ margin-top: 8px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ font-size: 12px;
+ color: #475467;
+}
+
+.sales-focus-reasons {
+ margin-top: 8px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+.sales-strategy-line {
+ margin: 8px 0 0;
+ font-size: 12px;
+ line-height: 1.6;
+ color: #334155;
+}
+
+.sales-section-title {
+ margin: 4px 0 8px;
+ font-size: 13px;
+ font-weight: 700;
+ color: $deep-blue;
+}
+
.purchase-confirm-card {
margin-top: 12px;
width: 100%;
diff --git a/src/components/ProcessParamListDialog.vue b/src/components/ProcessParamListDialog.vue
index 38e892d..deee249 100644
--- a/src/components/ProcessParamListDialog.vue
+++ b/src/components/ProcessParamListDialog.vue
@@ -130,6 +130,7 @@
</el-form-item>
<el-form-item label="鏍囧噯鍊�">
<el-input v-model="selectedParam.standardValue"
+ @input="val => onStandardValueInput(val, selectedParam)"
placeholder="璇疯緭鍏ラ粯璁ゅ��" />
</el-form-item>
<el-form-item label="鏄惁蹇呭~">
@@ -144,7 +145,8 @@
</div>
</div>
<template #footer>
- <el-button type="primary" @click="handleParamSelectSubmit">纭畾</el-button>
+ <el-button type="primary"
+ @click="handleParamSelectSubmit">纭畾</el-button>
<el-button @click="selectParamDialogVisible = false">鍙栨秷</el-button>
</template>
</el-dialog>
@@ -174,11 +176,13 @@
<el-form-item label="鏍囧噯鍊�"
prop="standardValue">
<el-input v-model="editParamForm.standardValue"
+ @input="val => onStandardValueInput(val, editParamForm)"
placeholder="璇疯緭鍏ユ爣鍑嗗��" />
</el-form-item>
</el-form>
<template #footer>
- <el-button type="primary" @click="handleEditParamSubmit">纭畾</el-button>
+ <el-button type="primary"
+ @click="handleEditParamSubmit">纭畾</el-button>
<el-button @click="editParamDialogVisible = false">鍙栨秷</el-button>
</template>
</el-dialog>
@@ -266,8 +270,32 @@
paramFormat: "",
unit: "",
});
+
+ const onStandardValueInput = (val, target) => {
+ const data = target.value || target;
+ const type = data.paramType || data.parameterType;
+ if (type === 1) {
+ // 鏁板�兼牸寮忥細涓嶈兘杈撳叆涓枃鎴栬嫳鏂囧瓧绗�
+ data.standardValue = val.replace(/[a-zA-Z\u4e00-\u9fa5]/g, "");
+ }
+ };
+
const editParamRules = ref({
- // standardValue: [{ required: true, message: "璇疯緭鍏ユ爣鍑嗗��", trigger: "blur" }],
+ standardValue: [
+ {
+ validator: (rule, value, callback) => {
+ const type =
+ editParamForm.value.paramType || editParamForm.value.parameterType;
+ if (type === 1 && value) {
+ if (/[a-zA-Z\u4e00-\u9fa5]/.test(value)) {
+ return callback(new Error("鏁板�兼牸寮忎笉鑳藉寘鍚腑鑻辨枃瀛楃"));
+ }
+ }
+ callback();
+ },
+ trigger: "blur",
+ },
+ ],
});
const editParamFormRef = ref(null);
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index 0692dda..d1d60b3 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -82,6 +82,7 @@
margin-bottom: 6px;
border-radius: 14px;
color: v-bind(getMenuTextColor);
+ font-size: 13px;
&:hover {
background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
@@ -99,6 +100,19 @@
.el-sub-menu__title {
color: v-bind(getMenuTextColor);
+ }
+
+ :deep(.el-sub-menu__icon-arrow) {
+ display: inline-flex !important;
+ align-items: center;
+ justify-content: center;
+ width: 14px;
+ height: 14px;
+ margin-top: -7px;
+ right: 12px;
+ font-size: 14px !important;
+ color: currentColor !important;
+ opacity: 0.72;
}
:deep(.el-sub-menu.is-active > .el-sub-menu__title) {
@@ -128,6 +142,7 @@
:deep(.el-sub-menu.is-active > .el-sub-menu__title .menu-title),
:deep(.el-sub-menu.is-active > .el-sub-menu__title .svg-icon),
+ :deep(.el-sub-menu.is-active > .el-sub-menu__title .el-sub-menu__icon-arrow),
:deep(.el-menu-item.is-active .menu-title),
:deep(.el-menu-item.is-active .svg-icon) {
color: v-bind(theme) !important;
diff --git a/src/views/aiIndustrialBrain/MAINTAIN_RULES.md b/src/views/aiIndustrialBrain/MAINTAIN_RULES.md
new file mode 100644
index 0000000..97b9a5c
--- /dev/null
+++ b/src/views/aiIndustrialBrain/MAINTAIN_RULES.md
@@ -0,0 +1,7 @@
+# AI宸ヤ笟澶ц剳缁存姢瑙勫垯
+
+1. 褰� `src/views/aiIndustrialBrain/index.vue` 鏂板鏅鸿兘浣擄紙`agents`锛夐�昏緫鏃讹紝蹇呴』鍚屾纭寮圭獥鍔╂墜鍙敤鎬с��
+2. 寮圭獥鍔╂墜鐢� `src/components/AIChatSidebar/assistants/index.js` 鐨� `assistantRegistry` 缁熶竴娉ㄥ唽銆�
+3. 鏂板鏅鸿兘浣撶殑 `key` 鑻ヨ鍦ㄥ脊绐椾腑鍙敤锛屽繀椤诲湪 `assistantRegistry` 涓彁渚涘悓鍚嶉厤缃��
+4. 鏈湪 `assistantRegistry` 娉ㄥ唽鐨勬櫤鑳戒綋浼氬湪寮圭獥涓樉绀轰负 `pending`锛堝紑鍙戜腑锛夋�併��
+
diff --git a/src/views/aiIndustrialBrain/components/AiAssistantWorkspace.vue b/src/views/aiIndustrialBrain/components/AiAssistantWorkspace.vue
index 2f55511..1eb5e7b 100644
--- a/src/views/aiIndustrialBrain/components/AiAssistantWorkspace.vue
+++ b/src/views/aiIndustrialBrain/components/AiAssistantWorkspace.vue
@@ -17,7 +17,7 @@
v-if="assistantMode !== 'pending'"
:key="assistantMode"
class="workspace-chat"
- :assistants="assistantMode === 'purchase' ? [purchaseAssistant] : [generalAssistant]"
+ :assistants="resolvedAssistants"
:default-assistant="assistantMode"
:hide-trigger="true"
:auto-open="true"
@@ -43,7 +43,7 @@
import { computed } from "vue";
import { ArrowLeftBold } from "@element-plus/icons-vue";
import AIChatSidebar from "@/components/AIChatSidebar/index.vue";
-import { generalAssistant, purchaseAssistant } from "@/components/AIChatSidebar/assistants";
+import { assistantRegistry } from "@/components/AIChatSidebar/assistants";
const props = defineProps({
visible: {
@@ -60,11 +60,17 @@
const agentKey = computed(() => String(props.agent?.key || ""));
const agentTitle = computed(() => String(props.agent?.name || "AI鍔╂墜"));
+
+/**
+ * 缁存姢瑙勫垯锛�
+ * AI宸ヤ笟澶ц剳鏂板鏅鸿兘浣撴椂锛岃嫢甯屾湜鍙充晶寮圭獥鍙敤锛岄渶淇濊瘉鏅鸿兘浣� key 鍦� assistantRegistry 涓湁鍚屽悕閰嶇疆銆�
+ * 鏈厤缃椂浼氳繘鍏� pending锛堝紑鍙戜腑锛夋�侊紝浣滀负鏄惧紡鎻愰啋銆�
+ */
+const resolvedAssistant = computed(() => assistantRegistry[agentKey.value] || null);
const assistantMode = computed(() => {
- if (agentKey.value === "purchase") return "purchase";
- if (agentKey.value === "general") return "general";
- return "pending";
+ return resolvedAssistant.value ? agentKey.value : "pending";
});
+const resolvedAssistants = computed(() => (resolvedAssistant.value ? [resolvedAssistant.value] : []));
</script>
<style scoped>
diff --git a/src/views/aiIndustrialBrain/index.vue b/src/views/aiIndustrialBrain/index.vue
index 33b967d..4afafa5 100644
--- a/src/views/aiIndustrialBrain/index.vue
+++ b/src/views/aiIndustrialBrain/index.vue
@@ -154,6 +154,7 @@
const router = useRouter();
+// 缁存姢绾﹀畾瑙侊細src/views/aiIndustrialBrain/MAINTAIN_RULES.md
const agents = [
{
key: "general",
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index 07da49b..b05b215 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -5,7 +5,7 @@
<el-input v-model="search"
style="width: 210px"
placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�"
- @change="searchFilter"
+ @input="debouncedSearch"
@clear="searchFilter"
clearable
prefix-icon="Search" />
@@ -565,40 +565,31 @@
proxy.$modal.msg("宸插彇娑�");
});
};
- // 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
- const filterNode = (value, data, node) => {
- if (!value) {
- //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
- return true;
- }
- // 鏌ヨ鍒楄〃鏄惁鏈夊尮閰嶆暟鎹紝灏嗗�煎皬鍐欙紝鍖归厤鑻辨枃鏁版嵁
- let val = value.toLowerCase();
- return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
+ const debounce = (fn, delay = 300) => {
+ let timer;
+ return (...args) => {
+ clearTimeout(timer);
+ timer = setTimeout(() => fn(...args), delay);
+ };
};
- // 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
- const chooseNode = (value, data, node) => {
- if (data.label.indexOf(value) !== -1) {
+
+ const debouncedSearch = debounce(() => {
+ searchFilter();
+ }, 300);
+
+ const filterNode = (value, data) => {
+ if (!value) return true;
+ return chooseNode(value.toLowerCase(), data);
+ };
+
+ const chooseNode = (value, data) => {
+ const label = (data.label || '').toLowerCase();
+ if (label.indexOf(value) !== -1) {
return true;
}
- const level = node.level;
- // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
- if (level === 1) {
- return false;
+ if (data.children && data.children.length > 0) {
+ return data.children.some(child => chooseNode(value, child));
}
- // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
- let parentData = node.parent;
- // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
- let index = 0;
- while (index < level - 1) {
- // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紝姝ゅname鍊兼槸涓枃瀛楃锛宔nName鏄嫳鏂囧瓧绗︺�傚垽鏂尮閰嶄腑鑻辨枃杩囨护
- if (parentData.data.label.indexOf(value) !== -1) {
- return true;
- }
- // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
- parentData = parentData.parent;
- index++;
- }
- // 娌″尮閰嶅埌杩斿洖false
return false;
};
getProductTreeList();
diff --git a/src/views/basicData/supplierManage/components/BlacklistTab.vue b/src/views/basicData/supplierManage/components/BlacklistTab.vue
index 8f6204b..206ba62 100644
--- a/src/views/basicData/supplierManage/components/BlacklistTab.vue
+++ b/src/views/basicData/supplierManage/components/BlacklistTab.vue
@@ -231,7 +231,10 @@
</div>
</template>
</el-dialog>
- <files-dia ref="filesDia"></files-dia>
+ <FileList v-if="fileListDialogVisible"
+ v-model:visible="fileListDialogVisible"
+ record-type="supplier_manage"
+ :record-id="recordId" />
</div>
</template>
@@ -249,7 +252,9 @@
} from "@/api/basicData/supplierManageFile.js";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
-import FilesDia from "../filesDia.vue";
+const FileList = defineAsyncComponent(() =>
+ import("@/components/Dialog/FileList.vue")
+);
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
@@ -327,7 +332,7 @@
name: "璧勮川鏂囦欢",
type: "text",
clickFun: (row) => {
- openFilesFormDia(row)
+ openFileDialog(row)
}
}
],
@@ -342,7 +347,8 @@
size: 100,
total: 0,
});
-const filesDia = ref()
+const fileListDialogVisible = ref(false);
+const recordId = ref();
// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
const operationType = ref("");
const dialogFormVisible = ref(false);
@@ -567,10 +573,9 @@
return `${year}-${month}-${day}`;
}
// 鎵撳紑闄勪欢寮规
-const openFilesFormDia = (row) => {
- nextTick(() => {
- filesDia.value?.openDialog(row)
- })
+const openFileDialog = async row => {
+ recordId.value = row.id;
+ fileListDialogVisible.value = true;
};
onMounted(() => {
diff --git a/src/views/basicData/supplierManage/components/HomeTab.vue b/src/views/basicData/supplierManage/components/HomeTab.vue
index 47dce00..da62ca4 100644
--- a/src/views/basicData/supplierManage/components/HomeTab.vue
+++ b/src/views/basicData/supplierManage/components/HomeTab.vue
@@ -237,7 +237,10 @@
</div>
</template>
</el-dialog>
- <files-dia ref="filesDia"></files-dia>
+ <FileList v-if="fileListDialogVisible"
+ v-model:visible="fileListDialogVisible"
+ record-type="supplier_manage"
+ :record-id="recordId" />
</div>
</template>
@@ -255,7 +258,9 @@
} from "@/api/basicData/supplierManageFile.js";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
-import FilesDia from "../filesDia.vue";
+const FileList = defineAsyncComponent(() =>
+ import("@/components/Dialog/FileList.vue")
+);
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
@@ -333,7 +338,7 @@
name: "璧勮川鏂囦欢",
type: "text",
clickFun: (row) => {
- openFilesFormDia(row)
+ openFileDialog(row)
}
}
],
@@ -343,12 +348,13 @@
const selectedRows = ref([]);
const userList = ref([]);
const tableLoading = ref(false);
+const fileListDialogVisible = ref(false);
+const recordId = ref();
const page = reactive({
current: 1,
size: 100,
total: 0,
});
-const filesDia = ref()
// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
const operationType = ref("");
const dialogFormVisible = ref(false);
@@ -573,10 +579,9 @@
return `${year}-${month}-${day}`;
}
// 鎵撳紑闄勪欢寮规
-const openFilesFormDia = (row) => {
- nextTick(() => {
- filesDia.value?.openDialog(row)
- })
+const openFileDialog = async row => {
+ recordId.value = row.id;
+ fileListDialogVisible.value = true;
};
onMounted(() => {
diff --git a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
index 6461b2d..5bd7a3e 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -152,7 +152,8 @@
startDate: "", // 璇峰亣寮�濮嬫椂闂�
endDate: "", // 璇峰亣缁撴潫鏃堕棿
price: null, // 鎶ラ攢閲戦
- location: "" // 鍑哄樊鍦扮偣
+ location: "", // 鍑哄樊鍦扮偣
+ storageBlobDTOS: []
},
rules: {
approveId: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -270,7 +271,7 @@
return
}
}
- form.value.storageBlobDTOList = fileList.value
+ form.value.storageBlobDTOS = fileList.value
proxy.$refs.formRef.validate(valid => {
if (valid) {
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index dba6bc1..11a2869 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -126,20 +126,23 @@
<!-- 寮圭獥缁勪欢 -->
<info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
<approval-dia ref="approvalDia" @close="handleQuery" :approveType="currentApproveType"></approval-dia>
- <FileList ref="fileListRef" />
+ <FileList v-if="fileDialogVisible"
+ v-model:visible="fileDialogVisible"
+ record-type="approve_process"
+ :record-id="recordId" />
</div>
</template>
<script setup>
-import FileList from "./fileList.vue";
import { Search, Plus, Delete, Download, RefreshRight, DocumentChecked } from "@element-plus/icons-vue";
-import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
+import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance, defineAsyncComponent} from "vue";
import {ElMessageBox} from "element-plus";
import { useRoute } from 'vue-router';
import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user";
+const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
const userStore = useUserStore();
const route = useRoute();
@@ -337,7 +340,7 @@
name: "闄勪欢",
type: "text",
clickFun: (row) => {
- downLoadFile(row);
+ openFilesFormDia(row);
},
});
}
@@ -371,11 +374,17 @@
page.current = 1;
getList();
};
-const fileListRef = ref(null)
-const downLoadFile = (row) => {
- fileListRef.value.open(row.commonFileList)
+// 鎵撳紑闄勪欢寮圭獥
+const recordId =ref(0)
+const fileDialogVisible = ref(false)
+
+// 鎵撳紑闄勪欢寮规
+const openFilesFormDia = async (row) => {
+ recordId.value = row.id
+ fileDialogVisible.value = true
}
+
const pagination = (obj) => {
page.current = obj.page;
page.size = obj.limit;
diff --git a/src/views/collaborativeApproval/sealManagement/index.vue b/src/views/collaborativeApproval/sealManagement/index.vue
index a6232c2..9d68848 100644
--- a/src/views/collaborativeApproval/sealManagement/index.vue
+++ b/src/views/collaborativeApproval/sealManagement/index.vue
@@ -87,10 +87,18 @@
</el-form-item>
<el-form-item label="绱ф�ョ▼搴�" prop="urgency">
<el-radio-group v-model="sealForm.urgency">
- <el-radio label="normal">鏅��</el-radio>
- <el-radio label="urgent">绱ф��</el-radio>
- <el-radio label="very-urgent">鐗规��</el-radio>
+ <el-radio value="normal">鏅��</el-radio>
+ <el-radio value="urgent">绱ф��</el-radio>
+ <el-radio value="very-urgent">鐗规��</el-radio>
</el-radio-group>
+ </el-form-item>
+ <el-form-item label="闄勪欢涓婁紶">
+ <AttachmentUploadFile
+ v-model:fileList="sealForm.storageBlobDTOs"
+ :limit="10"
+ :fileSize="50"
+ buttonText="鐐瑰嚮涓婁紶闄勪欢"
+ />
</el-form-item>
</el-form>
</FormDialog>
@@ -119,8 +127,27 @@
</el-descriptions-item>
<el-descriptions-item label="鐢宠鍘熷洜" :span="2">{{ currentSealDetail.reason }}</el-descriptions-item>
</el-descriptions>
+ <!-- 闄勪欢鍒楄〃 -->
+ <div v-if="currentSealDetail.storageBlobVOList?.length || currentSealDetail.storageBlobDTOs?.length" class="attachment-section">
+ <div class="attachment-title">闄勪欢鍒楄〃锛�</div>
+ <el-table :data="currentSealDetail.storageBlobVOList || currentSealDetail.storageBlobDTOs" border class="attachment-table">
+ <el-table-column label="闄勪欢鍚嶇О" show-overflow-tooltip>
+ <template #default="scope">
+ {{ scope.row.originalFilename || scope.row.name || scope.row.fileName || '鏈懡鍚嶆枃浠�' }}
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
+ <template #default="scope">
+ <el-button link type="primary" size="small" @click="previewFile(scope.row)">棰勮</el-button>
+ <el-button link type="primary" size="small" @click="downloadFile(scope.row)">涓嬭浇</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
</div>
</FormDialog>
+ <!-- 鏂囦欢棰勮缁勪欢 -->
+ <FilePreview ref="filePreviewRef" />
</div>
</template>
@@ -134,6 +161,9 @@
import useUserStore from '@/store/modules/user'
import FormDialog from '@/components/Dialog/FormDialog.vue'
import PIMTable from '@/components/PIMTable/PIMTable.vue'
+import AttachmentUploadFile from '@/components/AttachmentUpload/file/index.vue'
+import FilePreview from '@/components/filePreview/index.vue'
+import download from '@/plugins/download.js'
// 鍝嶅簲寮忔暟鎹�
// 鐢ㄥ嵃鐢宠鐩稿叧
@@ -143,6 +173,7 @@
const tableLoading = ref(false)
const showSealDetailDialog = ref(false)
const currentSealDetail = ref(null)
+const filePreviewRef = ref(null)
const sealFormRef = ref()
const userList = ref([])
const sealForm = reactive({
@@ -152,7 +183,8 @@
reason: '',
approveUserId: '',
urgency: 'normal',
- status: 'pending'
+ status: 'pending',
+ storageBlobDTOs: []
})
const sealRules = {
@@ -281,7 +313,8 @@
reason: '',
approveUserId: '',
urgency: 'normal',
- status: 'pending'
+ status: 'pending',
+ storageBlobDTOs: []
})
}
}).catch(err => {
@@ -301,7 +334,8 @@
reason: '',
approveUserId: '',
urgency: 'normal',
- status: 'pending'
+ status: 'pending',
+ storageBlobDTOs: []
})
// 娓呴櫎琛ㄥ崟楠岃瘉鐘舵��
if (sealFormRef.value) {
@@ -318,6 +352,27 @@
const viewSealDetail = (row) => {
currentSealDetail.value = row
showSealDetailDialog.value = true
+}
+
+// 棰勮鏂囦欢
+const previewFile = (row) => {
+ const url = row.previewURL || row.previewUrl || row.url
+ if (url && filePreviewRef.value) {
+ filePreviewRef.value.open(url)
+ } else {
+ ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曢瑙�')
+ }
+}
+
+// 涓嬭浇鏂囦欢
+const downloadFile = (row) => {
+ const url = row.downloadURL || row.downloadUrl || row.url
+ if (url) {
+ const filename = row.originalFilename || row.name || row.fileName || 'download'
+ download.byUrl(url, filename)
+ } else {
+ ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曚笅杞�')
+ }
}
// 瀹℃壒鐢ㄥ嵃鐢宠
const approveSeal = (row) => {
@@ -421,4 +476,19 @@
.ml-10 {
margin-left: 10px;
}
+
+.attachment-section {
+ margin-top: 20px;
+}
+
+.attachment-title {
+ font-size: 14px;
+ color: #606266;
+ margin-bottom: 10px;
+ font-weight: 500;
+}
+
+.attachment-table {
+ border-radius: 4px;
+}
</style>
diff --git a/src/views/equipmentManagement/inspectionManagement/components/formDia.vue b/src/views/equipmentManagement/inspectionManagement/components/formDia.vue
index 9f509b1..3e7f1ca 100644
--- a/src/views/equipmentManagement/inspectionManagement/components/formDia.vue
+++ b/src/views/equipmentManagement/inspectionManagement/components/formDia.vue
@@ -26,6 +26,21 @@
</el-row>
<el-row>
<el-col :span="12">
+ <el-form-item label="宸℃椤圭洰" prop="inspectionProject">
+ <el-input v-model="form.inspectionProject" placeholder="璇疯緭鍏ュ贰妫�椤圭洰" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁鍚敤" prop="isEnabled">
+ <el-radio-group v-model="form.isEnabled">
+ <el-radio :value="1">鏄�</el-radio>
+ <el-radio :value="0">鍚�</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="12">
<el-form-item label="澶囨敞" prop="remarks">
<el-input v-model="form.remarks" placeholder="璇疯緭鍏ュ娉�" type="textarea" />
</el-form-item>
@@ -118,6 +133,8 @@
taskName: undefined,
inspector: '',
inspectorIds: '',
+ inspectionProject: '',
+ isEnabled: 1,
remarks: '',
frequencyType: '',
frequencyDetail: '',
@@ -245,6 +262,8 @@
taskName: undefined,
inspector: '',
inspectorIds: '',
+ inspectionProject: '',
+ isEnabled: 1,
remarks: '',
frequencyType: '',
frequencyDetail: '',
diff --git a/src/views/equipmentManagement/inspectionManagement/index.vue b/src/views/equipmentManagement/inspectionManagement/index.vue
index 35f82d5..809fd4f 100644
--- a/src/views/equipmentManagement/inspectionManagement/index.vue
+++ b/src/views/equipmentManagement/inspectionManagement/index.vue
@@ -70,6 +70,12 @@
class="no-data">--</span>
</div>
</template>
+ <template #isEnabled="{ row }">
+ <el-tag :type="row.isEnabled === 1 ? 'success' : 'danger'"
+ size="small">
+ {{ row.isEnabled == 1 ? '鏄�' : '鍚�' }}
+ </el-tag>
+ </template>
</PIMTable>
</div>
</el-card>
@@ -126,8 +132,16 @@
// 鍒楅厤缃�
const columns = ref([
{ prop: "taskName", label: "宸℃浠诲姟鍚嶇О", minWidth: 160 },
+ { prop: "inspectionProject", label: "宸℃椤圭洰", minWidth: 150 },
{ prop: "remarks", label: "澶囨敞", minWidth: 150 },
{ prop: "inspector", label: "鎵ц宸℃浜�", minWidth: 150, slot: "inspector" },
+ {
+ prop: "isEnabled",
+ label: "鏄惁鍚敤",
+ minWidth: 100,
+ dataType: "slot",
+ slot: "isEnabled",
+ },
{
prop: "frequencyType",
label: "棰戞",
@@ -176,6 +190,19 @@
},
{ prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
{ prop: "createTime", label: "鐧昏鏃ユ湡", minWidth: 100 },
+ {
+ prop: "inspectionResult",
+ label: "宸℃缁撴灉",
+ minWidth: 100,
+ dataType: "tag",
+ formatData: val => {
+ return val == 1 ? "姝e父" : "寮傚父";
+ },
+ formatType: val => {
+ return val == 1 ? "success" : "danger";
+ },
+ },
+ { prop: "abnormalDescription", label: "寮傚父鎻忚堪", minWidth: 100 },
]);
// 鎿嶄綔鍒楅厤缃�
@@ -227,8 +254,10 @@
operationsArr.value = ["edit"];
} else if (value === "task") {
const operationColumn = getOperationColumn(["viewFile"]);
+ // 瀹氭椂浠诲姟璁板綍涓嶅睍绀�"鏄惁鍚敤"鍒�
+ const taskColumns = columns.value.filter(col => col.prop !== "isEnabled");
tableColumns.value = [
- ...columns.value,
+ ...taskColumns,
...(operationColumn ? [operationColumn] : []),
];
operationsArr.value = ["viewFile"];
diff --git a/src/views/equipmentManagement/measurementEquipment/components/formDia.vue b/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
index 6b7feec..16ac41f 100644
--- a/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
+++ b/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -36,15 +36,6 @@
</el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="瀹夎浣嶇疆锛�" prop="instationLocation">
- <el-input
- v-model="form.instationLocation"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
<el-form-item label="妫�瀹氬崟浣嶏細" prop="unit">
<el-input
v-model="form.unit"
@@ -53,17 +44,17 @@
/>
</el-form-item>
</el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="璇佷功缂栧彿锛�" prop="model">
+ <el-col :span="12">
+ <el-form-item label="璇佷功缂栧彿锛�" prop="model">
<el-input
v-model="form.model"
placeholder="璇疯緭鍏�"
clearable
/>
</el-form-item>
- </el-col>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
<el-col :span="12">
<el-form-item label="鏈�鏂伴壌瀹氭棩鏈燂細" prop="mostDate">
<el-date-picker
@@ -77,8 +68,6 @@
/>
</el-form-item>
</el-col>
- </el-row>
- <el-row :gutter="30">
<el-col :span="12">
<el-form-item label="鏈夋晥鏃ユ湡(澶�)锛�" prop="valid">
<el-input
@@ -91,15 +80,6 @@
>
<template #append>鏃�</template>
</el-input>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="妫�瀹氬懆鏈燂細" prop="cycle">
- <el-input
- v-model="form.cycle"
- placeholder="璇疯緭鍏ユ瀹氬懆鏈�"
- clearable
- />
</el-form-item>
</el-col>
</el-row>
@@ -184,10 +164,8 @@
form: {
code: "",
name: "",
- instationLocation: "",
mostDate:"",
model: "",
- cycle:"",
validDate: "",
nextDate: "",
userId: "",
@@ -203,9 +181,7 @@
nextDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
userId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
recordDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
- instationLocation: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
mostDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
- cycle: [{required: true, message: "璇烽�夋嫨", trigger: "blur"}],
valid: [
{required: true, message: "璇疯緭鍏�", trigger: "blur"},
{
diff --git a/src/views/equipmentManagement/measurementEquipment/index.vue b/src/views/equipmentManagement/measurementEquipment/index.vue
index c1d5379..d2ec2d7 100644
--- a/src/views/equipmentManagement/measurementEquipment/index.vue
+++ b/src/views/equipmentManagement/measurementEquipment/index.vue
@@ -42,6 +42,7 @@
:tableLoading="tableLoading"
@pagination="pagination"
:dbRowClick="dbRowClick"
+ :rowClassName="rowClassName"
></PIMTable>
</div>
<form-dia ref="formDia" @close="handleQuery"></form-dia>
@@ -89,12 +90,6 @@
align: "center",
},
{
- label: "瀹夎浣嶇疆",
- prop: "instationLocation",
- width: 150,
- align:"center"
- },
- {
label: "妫�瀹氬崟浣�",
prop: "unit",
width: 200,
@@ -130,12 +125,6 @@
width: 130,
align:"center"
},
- {
- label: "妫�瀹氬懆鏈�(澶�)",
- prop: "cycle",
- width: 130,
- align:"center"
- },
{
label: "鐘舵��",
prop: "status",
@@ -193,6 +182,31 @@
const dbRowClick = (row)=>{
rowClickData.value?.openDialog(row)
+}
+
+// 琛屾牱寮忥細蹇埌鏈燂紙7澶╁唴锛夋垨閫炬湡鏍囩孩
+const rowClassName = ({ row }) => {
+ console.log('rowClassName called:', row);
+ // valid 鏄湁鏁堝ぉ鏁帮紝mostDate 鏄渶鏂版瀹氭棩鏈�
+ if (row.valid && row.mostDate) {
+ const mostDate = new Date(row.mostDate);
+ // 璁$畻鍒版湡鏃ユ湡 = 妫�瀹氭棩鏈� + 鏈夋晥澶╂暟
+ const validDays = parseInt(row.valid) || 0;
+ const expireDate = new Date(mostDate);
+ expireDate.setDate(expireDate.getDate() + validDays);
+
+ const now = new Date();
+ const diffDays = Math.ceil((expireDate - now) / (1000 * 60 * 60 * 24));
+ console.log('row:', row.code, 'validDays:', validDays, 'expireDate:', expireDate, 'diffDays:', diffDays);
+ // 7澶╁唴鍒版湡鎴栧凡閫炬湡閮芥爣绾�
+ if (diffDays <= 7) {
+ console.log('return warning-row');
+ return 'warning-row';
+ }
+ } else {
+ console.log('row missing valid or mostDate:', row.valid, row.mostDate);
+ }
+ return '';
}
// 琛ㄦ牸閫夋嫨鏁版嵁
@@ -294,5 +308,13 @@
</script>
<style scoped>
-
+:deep(.el-table .warning-row) {
+ background-color: #fef0f0 !important;
+}
+:deep(.el-table .warning-row:hover > td) {
+ background-color: #f9d5d5 !important;
+}
+:deep(.el-table .el-table__body tr.warning-row td) {
+ background-color: #fef0f0 !important;
+}
</style>
\ No newline at end of file
diff --git a/src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue b/src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue
new file mode 100644
index 0000000..6d61a9f
--- /dev/null
+++ b/src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue
@@ -0,0 +1,144 @@
+<template>
+ <FormDialog
+ v-model="visible"
+ title="楠屾敹瀹℃壒"
+ width="500px"
+ @confirm="submitForm"
+ @cancel="handleCancel"
+ @close="handleCancel"
+ >
+ <el-form :model="form" :rules="rules" label-width="100px">
+ <el-form-item label="楠屾敹浜�" prop="acceptanceName">
+ <el-select
+ v-model="form.acceptanceName"
+ placeholder="璇烽�夋嫨楠屾敹浜�"
+ filterable
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in userList"
+ :key="item.userId"
+ :label="item.nickName"
+ :value="item.nickName"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="楠屾敹鏃堕棿" prop="acceptanceTime">
+ <el-date-picker
+ v-model="form.acceptanceTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨楠屾敹鏃堕棿"
+ format="YYYY-MM-DD HH:mm:ss"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ style="width: 100%"
+ />
+ </el-form-item>
+ <el-form-item label="楠屾敹澶囨敞" prop="acceptanceRemark">
+ <el-input
+ v-model="form.acceptanceRemark"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ラ獙鏀跺娉�"
+ />
+ </el-form-item>
+ </el-form>
+ </FormDialog>
+</template>
+
+<script setup>
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { ref, reactive } from "vue";
+import { ElMessage } from "element-plus";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import { repairAcceptance } from "@/api/equipmentManagement/repair";
+import dayjs from "dayjs";
+
+defineOptions({
+ name: "楠屾敹瀹℃壒寮圭獥",
+});
+
+const emits = defineEmits(["ok"]);
+
+const visible = ref(false);
+const loading = ref(false);
+const repairId = ref(null);
+const userList = ref([]);
+
+const form = reactive({
+ acceptanceName: undefined,
+ acceptanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+ acceptanceRemark: undefined,
+});
+
+const rules = {
+ acceptanceName: [
+ { required: true, message: "璇烽�夋嫨楠屾敹浜�", trigger: "change" },
+ ],
+ acceptanceTime: [
+ { required: true, message: "璇烽�夋嫨楠屾敹鏃堕棿", trigger: "change" },
+ ],
+ acceptanceRemark: [
+ { required: true, message: "璇疯緭鍏ラ獙鏀跺娉�", trigger: "blur" },
+ ],
+};
+
+// 鍔犺浇鐢ㄦ埛鍒楄〃
+const loadUserList = async () => {
+ const { data } = await userListNoPageByTenantId();
+ userList.value = data;
+};
+
+// 鎵撳紑寮圭獥
+const open = async (row) => {
+ repairId.value = row.id;
+ visible.value = true;
+ // 閲嶇疆琛ㄥ崟
+ form.acceptanceName = undefined;
+ form.acceptanceTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
+ form.acceptanceRemark = undefined;
+ await loadUserList();
+};
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+ if (!form.acceptanceName) {
+ ElMessage.warning("璇烽�夋嫨楠屾敹浜�");
+ return;
+ }
+ if (!form.acceptanceTime) {
+ ElMessage.warning("璇烽�夋嫨楠屾敹鏃堕棿");
+ return;
+ }
+ if (!form.acceptanceRemark) {
+ ElMessage.warning("璇疯緭鍏ラ獙鏀跺娉�");
+ return;
+ }
+
+ loading.value = true;
+ try {
+ const { code } = await repairAcceptance({
+ id: repairId.value,
+ acceptanceName: form.acceptanceName,
+ acceptanceTime: form.acceptanceTime,
+ acceptanceRemark: form.acceptanceRemark,
+ });
+ if (code === 200) {
+ ElMessage.success("楠屾敹閫氳繃");
+ visible.value = false;
+ emits("ok");
+ }
+ } finally {
+ loading.value = false;
+ }
+};
+
+const handleCancel = () => {
+ visible.value = false;
+};
+
+defineExpose({
+ open,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/repair/Modal/RepairModal.vue b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
index 5e31943..4e73833 100644
--- a/src/views/equipmentManagement/repair/Modal/RepairModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -49,19 +49,44 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="椤圭洰">
- <el-input v-model="form.machineryCategory" placeholder="璇疯緭鍏ラ」鐩�" />
+ <el-form-item label="鎶ヤ慨鎶ヤ慨椤圭洰">
+ <el-input v-model="form.machineryCategory" placeholder="璇疯緭鍏ユ姤淇姤淇」鐩�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="缁翠慨浜�">
+ <el-input v-model="form.maintenanceName" placeholder="璇疯緭鍏ョ淮淇汉濮撳悕" />
</el-form-item>
</el-col>
</el-row>
<el-row v-if="id">
<el-col :span="12">
<el-form-item label="鎶ヤ慨鐘舵��">
- <el-select v-model="form.status">
+ <el-select v-model="form.status" disabled>
<el-option label="寰呯淮淇�" :value="0"></el-option>
- <el-option label="瀹岀粨" :value="1"></el-option>
+ <el-option label="宸查獙鏀�" :value="1"></el-option>
<el-option label="澶辫触" :value="2"></el-option>
</el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 楠屾敹淇℃伅灞曠ず -->
+ <el-row v-if="id && form.status === 1">
+ <el-col :span="12">
+ <el-form-item label="楠屾敹浜�">
+ <el-input v-model="form.acceptanceName" disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="楠屾敹鏃堕棿">
+ <el-input v-model="form.acceptanceTime" disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="楠屾敹澶囨敞">
+ <el-input v-model="form.acceptanceRemark" type="textarea" :rows="2" disabled />
</el-form-item>
</el-col>
</el-row>
@@ -131,6 +156,7 @@
status: 0, // 鎶ヤ慨鐘舵��
machineryCategory: undefined,
storageBlobDTOs: [],
+ maintenanceName: undefined, // 缁翠慨浜�
});
const setDeviceModel = (deviceId) => {
@@ -148,6 +174,10 @@
form.status = data.status;
form.machineryCategory = data.machineryCategory;
form.storageBlobDTOs = data.storageBlobVOs || [];
+ form.maintenanceName = data.maintenanceName;
+ form.acceptanceName = data.acceptanceName;
+ form.acceptanceTime = data.acceptanceTime;
+ form.acceptanceRemark = data.acceptanceRemark;
};
const sendForm = async () => {
diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index f3a4330..2835356 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -100,13 +100,14 @@
<template #statusRef="{ row }">
<el-tag v-if="row.status === 2" type="danger">澶辫触</el-tag>
<el-tag v-if="row.status === 1" type="success">瀹岀粨</el-tag>
+ <el-tag v-if="row.status === 3" type="info">寰呴獙鏀�</el-tag>
<el-tag v-if="row.status === 0" type="warning">寰呯淮淇�</el-tag>
</template>
<template #operation="{ row }">
<el-button
type="primary"
link
- :disabled="row.status === 1"
+ :disabled="row.status === 1 || row.status === 3"
@click="editRepair(row.id)"
>
缂栬緫
@@ -114,15 +115,23 @@
<el-button
type="success"
link
- :disabled="row.status === 1"
+ :disabled="row.status !== 0"
@click="addMaintain(row)"
>
缁翠慨
</el-button>
<el-button
+ type="warning"
+ link
+ :disabled="row.status !== 3"
+ @click="openAcceptance(row)"
+ >
+ 楠屾敹
+ </el-button>
+ <el-button
type="danger"
link
- :disabled="row.status === 1"
+ :disabled="row.status === 1 || row.status === 3"
@click="delRepairByIds(row.id)"
>
鍒犻櫎
@@ -139,6 +148,7 @@
</div>
<RepairModal ref="repairModalRef" @ok="getTableData"/>
<MaintainModal ref="maintainModalRef" @ok="getTableData"/>
+ <AcceptanceModal ref="acceptanceModalRef" @ok="getTableData"/>
<FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" :record-type="'device_repair'" :record-id="recordId" />
</div>
</template>
@@ -151,6 +161,7 @@
import {ElMessageBox, ElMessage} from "element-plus";
import dayjs from "dayjs";
import MaintainModal from "./Modal/MaintainModal.vue";
+import AcceptanceModal from "./Modal/AcceptanceModal.vue";
const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
defineOptions({
@@ -162,6 +173,7 @@
// 妯℃�佹瀹炰緥
const repairModalRef = ref();
const maintainModalRef = ref();
+const acceptanceModalRef = ref();
// 琛ㄦ牸澶氶�夋閫変腑椤�
const multipleList = ref([]);
@@ -197,7 +209,7 @@
prop: "deviceModel",
},
{
- label: "椤圭洰",
+ label: "鎶ヤ慨椤圭洰",
align: "center",
prop: "machineryCategory",
},
@@ -232,6 +244,17 @@
align: "center",
prop: "maintenanceTime",
formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
+ },
+ {
+ label: "楠屾敹浜�",
+ align: "center",
+ prop: "acceptanceName",
+ },
+ {
+ label: "楠屾敹鏃堕棿",
+ align: "center",
+ prop: "acceptanceTime",
+ formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : ""),
},
{
label: "鐘舵��",
@@ -301,6 +324,11 @@
maintainModalRef.value.open(row.id, row);
};
+// 鎵撳紑楠屾敹寮圭獥
+const openAcceptance = (row) => {
+ acceptanceModalRef.value.open(row);
+};
+
const changePage = ({page, limit}) => {
pagination.currentPage = page;
pagination.pageSize = limit;
diff --git a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
index e86b64a..0fcccb2 100644
--- a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
@@ -1,199 +1,216 @@
<template>
- <FormDialog
- v-model="visible"
- :title="'璁惧淇濆吇'"
- width="500px"
- @confirm="sendForm"
- @cancel="handleCancel"
- @close="handleClose"
- >
- <el-form :model="form" label-width="100px">
+ <FormDialog v-model="visible"
+ :title="'璁惧淇濆吇'"
+ width="500px"
+ @confirm="sendForm"
+ @cancel="handleCancel"
+ @close="handleClose">
+ <el-form :model="form"
+ label-width="100px">
<el-form-item label="瀹為檯淇濆吇浜�">
- <el-input
- v-model="form.maintenanceActuallyName"
- placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
- ></el-input>
+ <el-input v-model="form.maintenanceActuallyName"
+ placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"></el-input>
</el-form-item>
<el-form-item label="瀹為檯淇濆吇鏃ユ湡">
- <el-date-picker
- v-model="form.maintenanceActuallyTime"
- placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
- format="YYYY-MM-DD HH:mm:ss"
- value-format="YYYY-MM-DD HH:mm:ss"
- type="datetime"
- clearable
- style="width: 100%"
- />
+ <el-date-picker v-model="form.maintenanceActuallyTime"
+ placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
+ format="YYYY-MM-DD HH:mm:ss"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ type="datetime"
+ clearable
+ style="width: 100%" />
</el-form-item>
<el-form-item label="淇濆吇鐘舵��">
<el-select v-model="form.status">
- <el-option label="寰呬繚鍏�" :value="0"></el-option>
- <el-option label="瀹岀粨" :value="1"></el-option>
- <el-option label="澶辫触" :value="2"></el-option>
+ <el-option label="寰呬繚鍏�"
+ :value="0"></el-option>
+ <el-option label="瀹岀粨"
+ :value="1"></el-option>
+ <el-option label="澶辫触"
+ :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="淇濆吇缁撴灉">
- <el-input
- v-model="form.maintenanceResult"
- placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
- type="text" />
+ <el-input v-model="form.maintenanceResult"
+ placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
+ type="text" />
</el-form-item>
<el-form-item label="璁惧澶囦欢">
- <el-select v-model="form.sparePartsIds" :loading="loadingSparePartOptions" placeholder="璇烽�夋嫨璁惧澶囦欢" multiple filterable>
- <el-option
- v-for="item in sparePartOptions"
- :key="item.id"
- :label="item.name"
- :value="item.id"
- />
+ <el-select v-model="form.sparePartsIds"
+ :loading="loadingSparePartOptions"
+ placeholder="璇烽�夋嫨璁惧澶囦欢"
+ multiple
+ filterable>
+ <el-option v-for="item in sparePartOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id" />
</el-select>
</el-form-item>
-
- <el-form-item v-if="selectedSpareParts.length" label="棰嗙敤鏁伴噺">
+ <el-form-item v-if="selectedSpareParts.length"
+ label="棰嗙敤鏁伴噺">
<div style="width: 100%">
- <div
- v-for="item in selectedSpareParts"
- :key="item.id"
- style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;"
- >
+ <div v-for="item in selectedSpareParts"
+ :key="item.id"
+ style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
<div style="flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ item.name }}
- <span v-if="item.quantity !== null && item.quantity !== undefined" style="color: #909399;">
+ <span v-if="item.quantity !== null && item.quantity !== undefined"
+ style="color: #909399;">
锛堝簱瀛橈細{{ item.quantity }}锛�
</span>
</div>
- <el-input-number
- v-model="sparePartQtyMap[item.id]"
- :min="1"
- :max="item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : undefined"
- :step="1"
- controls-position="right"
- style="width: 180px"
- />
+ <el-input-number v-model="sparePartQtyMap[item.id]"
+ :min="1"
+ :max="item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : undefined"
+ :step="1"
+ controls-position="right"
+ style="width: 180px" />
</div>
</div>
+ </el-form-item>
+ <el-form-item label="闄勪欢">
+ <FileUpload v-model:file-list="form.storageBlobDTOs" />
</el-form-item>
</el-form>
</FormDialog>
</template>
<script setup>
-import FormDialog from "@/components/Dialog/FormDialog.vue";
-import { addMaintenance } from "@/api/equipmentManagement/upkeep";
-import useFormData from "@/hooks/useFormData";
-import dayjs from "dayjs";
-import useUserStore from "@/store/modules/user";
-import { ElMessage } from "element-plus";
-import {computed, ref} from "vue";
-import {getSparePartsList} from "@/api/equipmentManagement/spareParts.js";
+ import FormDialog from "@/components/Dialog/FormDialog.vue";
+ import FileUpload from "@/components/AttachmentUpload/file/index.vue";
+ import { addMaintenance } from "@/api/equipmentManagement/upkeep";
+ import useFormData from "@/hooks/useFormData";
+ import dayjs from "dayjs";
+ import useUserStore from "@/store/modules/user";
+ import { ElMessage } from "element-plus";
+ import { computed, ref, nextTick, getCurrentInstance } from "vue";
+ import { getSparePartsList } from "@/api/equipmentManagement/spareParts.js";
-defineOptions({
- name: "淇濆吇妯℃�佹",
-});
+ defineOptions({
+ name: "淇濆吇妯℃�佹",
+ });
-const emits = defineEmits(["ok"]);
+ const emits = defineEmits(["ok"]);
-// 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
-const planId = ref();
-const visible = ref(false);
-const loading = ref(false);
-const userStore = useUserStore();
+ const { proxy } = getCurrentInstance();
+ // 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
+ const planId = ref();
+ const visible = ref(false);
+ const loading = ref(false);
+ const userStore = useUserStore();
-const { form, resetForm } = useFormData({
- maintenanceActuallyName: undefined, // 瀹為檯淇濆吇浜�
- maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
- maintenanceResult: undefined, // 淇濆吇缁撴灉
- status: 0, // 淇濆吇鐘舵��
- sparePartsIds: [],
-});
+ const { form, resetForm } = useFormData({
+ maintenanceActuallyName: undefined, // 瀹為檯淇濆吇浜�
+ maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
+ maintenanceResult: undefined, // 淇濆吇缁撴灉
+ status: 0, // 淇濆吇鐘舵��
+ sparePartsIds: [],
+ storageBlobDTOs: [],
+ });
-const sparePartOptions = ref([])
-const loadingSparePartOptions = ref(true)
-const sparePartQtyMap = ref({})
+ const sparePartOptions = ref([]);
+ const loadingSparePartOptions = ref(true);
+ const sparePartQtyMap = ref({});
-const selectedSpareParts = computed(() => {
- const ids = Array.isArray(form.sparePartsIds) ? form.sparePartsIds : [];
- const set = new Set(ids.map((i) => String(i)));
- return (sparePartOptions.value || []).filter((p) => set.has(String(p.id)));
-});
+ const selectedSpareParts = computed(() => {
+ const ids = Array.isArray(form.sparePartsIds) ? form.sparePartsIds : [];
+ const set = new Set(ids.map(i => String(i)));
+ return (sparePartOptions.value || []).filter(p => set.has(String(p.id)));
+ });
-const setForm = (data) => {
- form.maintenanceActuallyName =
- data.maintenanceActuallyName ?? userStore.nickName;
- form.maintenanceActuallyTime =
- data.maintenanceActuallyTime
+ const setForm = data => {
+ form.maintenanceActuallyName =
+ data.maintenanceActuallyName ?? userStore.nickName;
+ form.maintenanceActuallyTime = data.maintenanceActuallyTime
? dayjs(data.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss")
: dayjs().format("YYYY-MM-DD HH:mm:ss");
- form.maintenanceResult = data.maintenanceResult;
- form.status = 1; // 榛樿鐘舵�佷负瀹岀粨
- // multiple 閫夋嫨鍣ㄨ姹傛暟缁勶紱鍚庣甯歌繑鍥� "1,2,3"
- if (Array.isArray(data?.sparePartsIds)) {
- form.sparePartsIds = data.sparePartsIds.map((v) => Number(v)).filter((v) => Number.isFinite(v));
- } else if (typeof data?.sparePartsIds === "string") {
- form.sparePartsIds = data.sparePartsIds
+ form.maintenanceResult = data.maintenanceResult;
+ form.status = 1; // 榛樿鐘舵�佷负瀹岀粨
+ // multiple 閫夋嫨鍣ㄨ姹傛暟缁勶紱鍚庣甯歌繑鍥� "1,2,3"
+ if (Array.isArray(data?.sparePartsIds)) {
+ form.sparePartsIds = data.sparePartsIds
+ .map(v => Number(v))
+ .filter(v => Number.isFinite(v));
+ } else if (typeof data?.sparePartsIds === "string") {
+ form.sparePartsIds = data.sparePartsIds
.split(",")
- .map((s) => Number(String(s).trim()))
- .filter((v) => Number.isFinite(v));
- } else if (typeof data?.sparePartsIds === "number") {
- form.sparePartsIds = [data.sparePartsIds];
- } else {
- form.sparePartsIds = [];
- }
-};
+ .map(s => Number(String(s).trim()))
+ .filter(v => Number.isFinite(v));
+ } else if (typeof data?.sparePartsIds === "number") {
+ form.sparePartsIds = [data.sparePartsIds];
+ } else {
+ form.sparePartsIds = [];
+ }
+ form.storageBlobDTOs = data.storageBlobVOs || [];
+ };
-/**
- * @desc 淇濆瓨淇濆吇
- */
-const sendForm = async () => {
- loading.value = true;
- try {
- // 棰嗙敤鏁伴噺鏍¢獙
- if (Array.isArray(form.sparePartsIds) && form.sparePartsIds.length > 0) {
- for (const partId of form.sparePartsIds) {
- const qty = Number(sparePartQtyMap.value?.[partId]);
- if (!Number.isFinite(qty) || qty <= 0) {
- proxy?.$modal?.msgError?.("璇峰~鍐欏浠堕鐢ㄦ暟閲�");
- return;
- }
- const part = sparePartOptions.value.find((p) => String(p.id) === String(partId));
- const stock = part?.quantity;
- if (stock !== null && stock !== undefined && Number.isFinite(Number(stock))) {
- if (qty > Number(stock)) {
- proxy?.$modal?.msgError?.(`澶囦欢銆�${part?.name || ""}銆嶉鐢ㄦ暟閲忎笉鑳借秴杩囧簱瀛橈紙${stock}锛塦);
+ /**
+ * @desc 淇濆瓨淇濆吇
+ */
+ const sendForm = async () => {
+ loading.value = true;
+ try {
+ // 棰嗙敤鏁伴噺鏍¢獙
+ if (Array.isArray(form.sparePartsIds) && form.sparePartsIds.length > 0) {
+ for (const partId of form.sparePartsIds) {
+ const qty = Number(sparePartQtyMap.value?.[partId]);
+ if (!Number.isFinite(qty) || qty <= 0) {
+ proxy?.$modal?.msgError?.("璇峰~鍐欏浠堕鐢ㄦ暟閲�");
return;
+ }
+ const part = sparePartOptions.value.find(
+ p => String(p.id) === String(partId)
+ );
+ const stock = part?.quantity;
+ if (
+ stock !== null &&
+ stock !== undefined &&
+ Number.isFinite(Number(stock))
+ ) {
+ if (qty > Number(stock)) {
+ proxy?.$modal?.msgError?.(
+ `澶囦欢銆�${part?.name || ""}銆嶉鐢ㄦ暟閲忎笉鑳借秴杩囧簱瀛橈紙${stock}锛塦
+ );
+ return;
+ }
}
}
}
- }
- const data = {
- id: planId.value,
- ...form,
- sparePartsIds: form.sparePartsIds ? form.sparePartsIds.join(",") : "",
- sparePartsQty: form.sparePartsIds
- ? form.sparePartsIds.map((id) => sparePartQtyMap.value?.[id] ?? 1).join(",")
+ const data = {
+ id: planId.value,
+ ...form,
+ sparePartsIds: form.sparePartsIds ? form.sparePartsIds.join(",") : "",
+ sparePartsQty: form.sparePartsIds
+ ? form.sparePartsIds
+ .map(id => sparePartQtyMap.value?.[id] ?? 1)
+ .join(",")
: "",
- sparePartsUseList: form.sparePartsIds
- ? form.sparePartsIds.map((id) => ({ id, quantity: sparePartQtyMap.value?.[id] ?? 1 }))
+ sparePartsUseList: form.sparePartsIds
+ ? form.sparePartsIds.map(id => ({
+ id,
+ quantity: sparePartQtyMap.value?.[id] ?? 1,
+ }))
: [],
+ };
+ const { code } = await addMaintenance(data);
+ if (code == 200) {
+ ElMessage.success("淇濆吇鎴愬姛");
+ emits("ok");
+ resetForm();
+ sparePartQtyMap.value = {};
+ visible.value = false;
+ }
+ } finally {
+ loading.value = false;
}
- const { code } = await addMaintenance(data);
- if (code == 200) {
- ElMessage.success("淇濆吇鎴愬姛");
- emits("ok");
- resetForm();
- sparePartQtyMap.value = {};
- visible.value = false;
- }
- } finally {
- loading.value = false;
- }
-};
+ };
-const fetchSparePartOptions = () => {
- loadingSparePartOptions.value = true;
- // 鍜屽浠剁鐞嗛〉涓�鑷达細/spareParts/listPage 鈫� res.data.records
- getSparePartsList({ current: 1, size: 1000 })
- .then((res) => {
+ const fetchSparePartOptions = () => {
+ loadingSparePartOptions.value = true;
+ // 鍜屽浠剁鐞嗛〉涓�鑷达細/spareParts/listPage 鈫� res.data.records
+ getSparePartsList({ current: 1, size: 1000 })
+ .then(res => {
if (res.code === 200) {
sparePartOptions.value = res?.data?.records || [];
} else {
@@ -206,31 +223,31 @@
.finally(() => {
loadingSparePartOptions.value = false;
});
-}
+ };
-const handleCancel = () => {
- resetForm();
- sparePartQtyMap.value = {};
- visible.value = false;
-};
+ const handleCancel = () => {
+ resetForm();
+ sparePartQtyMap.value = {};
+ visible.value = false;
+ };
-const handleClose = () => {
- resetForm();
- sparePartQtyMap.value = {};
- visible.value = false;
-};
+ const handleClose = () => {
+ resetForm();
+ sparePartQtyMap.value = {};
+ visible.value = false;
+ };
-const open = async (id, row) => {
- planId.value = id; // 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
- visible.value = true;
- await nextTick();
- fetchSparePartOptions()
- setForm(row);
-};
+ const open = async (id, row) => {
+ planId.value = id; // 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
+ visible.value = true;
+ await nextTick();
+ fetchSparePartOptions();
+ setForm(row);
+ };
-defineExpose({
- open,
-});
+ defineExpose({
+ open,
+ });
</script>
<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
index ee59ce2..8a9cd98 100644
--- a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
@@ -32,10 +32,10 @@
disabled
/>
</el-form-item>
- <el-form-item label="椤圭洰">
+ <el-form-item label="淇濆吇椤圭洰">
<el-input
v-model="form.machineryCategory"
- placeholder="璇疯緭鍏ラ」鐩�"
+ placeholder="璇疯緭鍏ヤ繚鍏婚」鐩�"
/>
</el-form-item>
<el-form-item label="褰曞叆浜�">
@@ -61,6 +61,13 @@
<el-option label="瀹岀粨" :value="1"></el-option>
<el-option label="澶辫触" :value="2"></el-option>
</el-select>
+ </el-form-item>
+ <el-form-item label="淇濆吇浜�">
+ <el-input
+ v-model="form.maintenancePerson"
+ placeholder="璇疯緭鍏ヤ繚鍏讳汉濮撳悕"
+ clearable
+ />
</el-form-item>
<el-form-item label="璁″垝淇濆吇鏃ユ湡">
<el-date-picker
@@ -124,6 +131,7 @@
status: 0, //淇濅慨鐘舵��
machineryCategory: undefined,
storageBlobDTOs: [],
+ maintenancePerson: undefined, // 淇濆吇浜�
});
const setDeviceModel = (deviceId) => {
@@ -142,6 +150,7 @@
form.createUser = Number(data.createUser);
form.status = data.status;
form.machineryCategory = data.machineryCategory;
+ form.maintenancePerson = data.maintenancePerson;
if (data.maintenancePlanTime) {
form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
"YYYY-MM-DD HH:mm:ss"
diff --git a/src/views/equipmentManagement/upkeep/Form/formDia.vue b/src/views/equipmentManagement/upkeep/Form/formDia.vue
index 9550b08..6856ae1 100644
--- a/src/views/equipmentManagement/upkeep/Form/formDia.vue
+++ b/src/views/equipmentManagement/upkeep/Form/formDia.vue
@@ -67,10 +67,20 @@
</el-row>
<el-row>
<el-col :span="12">
- <el-form-item label="璁惧椤圭洰" prop="machineryCategory">
+ <el-form-item label="淇濆吇椤圭洰" prop="machineryCategory">
<el-input
v-model.trim="form.machineryCategory"
- placeholder="璇疯緭鍏ヨ澶囬」鐩�"
+ placeholder="璇疯緭鍏ヤ繚鍏婚」鐩�"
+ maxlength="100"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="淇濆吇浜�" prop="maintenancePerson">
+ <el-input
+ v-model.trim="form.maintenancePerson"
+ placeholder="璇疯緭鍏ヤ繚鍏讳汉濮撳悕"
maxlength="100"
clearable
/>
@@ -173,13 +183,14 @@
week: '',
time: '',
deviceModel: undefined, // 瑙勬牸鍨嬪彿
- registrationDate: ''
+ registrationDate: '',
+ maintenancePerson: '' // 淇濆吇浜�
},
rules: {
taskId: [{ required: true, message: "璇烽�夋嫨璁惧", trigger: "change" },],
inspector: [{ required: true, message: "璇烽�夋嫨褰曞叆浜�", trigger: "blur" },],
registrationDate: [{ required: true, message: "璇烽�夋嫨鐧昏鏃堕棿", trigger: "change" }],
- machineryCategory: [{ required: true, message: "璇疯緭鍏ヨ澶囬」鐩�", trigger: "blur" }]
+ machineryCategory: [{ required: true, message: "璇疯緭鍏ヤ繚鍏婚」鐩�", trigger: "blur" }]
}
})
const { form, rules } = toRefs(data)
@@ -259,7 +270,8 @@
week: '',
time: '',
deviceModel: undefined,
- registrationDate: ''
+ registrationDate: '',
+ maintenancePerson: ''
}
}
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 2fc3eae..c22ebcc 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -2,8 +2,8 @@
<div class="app-container">
<el-tabs v-model="activeTab"
@tab-change="handleTabChange">
- <!-- 瀹氭椂浠诲姟绠$悊tab -->
- <el-tab-pane label="瀹氭椂浠诲姟绠$悊"
+ <!-- 淇濆吇浠诲姟tab -->
+ <el-tab-pane label="淇濆吇浠诲姟"
name="scheduled">
<div class="search_form">
<el-form :model="scheduledFilters"
@@ -37,7 +37,7 @@
<div class="table_list">
<div class="actions">
<el-text class="mx-1"
- size="large">瀹氭椂浠诲姟绠$悊</el-text>
+ size="large">淇濆吇浠诲姟</el-text>
<div>
<el-button type="primary"
icon="Plus"
@@ -84,8 +84,8 @@
</PIMTable>
</div>
</el-tab-pane>
- <!-- 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛� -->
- <el-tab-pane label="浠诲姟璁板綍"
+ <!-- 淇濆吇璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛� -->
+ <el-tab-pane label="淇濆吇璁板綍"
name="record">
<div class="search_form">
<el-form :model="filters"
@@ -130,7 +130,7 @@
<div class="table_list">
<div class="actions">
<el-text class="mx-1"
- size="large">浠诲姟璁板綍</el-text>
+ size="large">淇濆吇璁板綍</el-text>
<div>
<el-button type="success"
icon="Van"
@@ -262,7 +262,7 @@
const fileDialogVisible = ref(false);
const currentMaintenanceTaskId = ref(null);
- // 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
+ // 淇濆吇璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
const filters = reactive({
deviceName: "",
maintenancePlanTime: "",
@@ -278,7 +278,7 @@
});
const multipleList = ref([]);
- // 瀹氭椂浠诲姟绠$悊tab鐩稿叧鍙橀噺
+ // 淇濆吇浠诲姟tab鐩稿叧鍙橀噺
const scheduledFilters = reactive({
taskName: "",
status: "",
@@ -292,7 +292,7 @@
});
const scheduledMultipleList = ref([]);
- // 瀹氭椂浠诲姟绠$悊琛ㄦ牸鍒楅厤缃�
+ // 淇濆吇浠诲姟琛ㄦ牸鍒楅厤缃�
const scheduledColumns = ref([
{ prop: "taskName", label: "璁惧鍚嶇О" },
{
@@ -300,7 +300,7 @@
prop: "deviceModel",
},
{
- label: "璁惧椤圭洰",
+ label: "淇濆吇椤圭洰",
prop: "machineryCategory",
minWidth: 120,
formatData: cell => cell || "--",
@@ -342,6 +342,7 @@
);
},
},
+ { prop: "maintenancePerson", label: "淇濆吇浜�", minWidth: 100 },
{ prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
{ prop: "registrationDate", label: "鐧昏鏃ユ湡", minWidth: 100 },
{
@@ -354,7 +355,7 @@
},
]);
- // 浠诲姟璁板綍琛ㄦ牸鍒楅厤缃紙鍘熻澶囦繚鍏昏〃鏍煎垪锛�
+ // 淇濆吇璁板綍琛ㄦ牸鍒楅厤缃紙鍘熻澶囦繚鍏昏〃鏍煎垪锛�
const columns = ref([
{
label: "璁惧鍚嶇О",
@@ -378,7 +379,7 @@
prop: "createUserName",
},
{
- label: "璁惧椤圭洰",
+ label: "淇濆吇椤圭洰",
align: "center",
prop: "machineryCategory",
formatData: cell => cell || "--",
@@ -435,7 +436,7 @@
}
};
- // 瀹氭椂浠诲姟绠$悊鐩稿叧鏂规硶
+ // 淇濆吇浠诲姟鐩稿叧鏂规硶
const getScheduledTableData = async () => {
try {
const params = {
@@ -502,7 +503,7 @@
ElMessage.info("瀵煎嚭瀹氭椂浠诲姟鍔熻兘寰呭疄鐜�");
};
- // 浠诲姟璁板綍鐩稿叧鏂规硶锛堝師璁惧淇濆吇椤甸潰鏂规硶锛�
+ // 淇濆吇璁板綍鐩稿叧鏂规硶锛堝師璁惧淇濆吇椤甸潰鏂规硶锛�
const getTableData = async () => {
try {
const params = {
diff --git a/src/views/financialManagement/assets/fixedAssets.vue b/src/views/financialManagement/assets/fixedAssets.vue
index c6241c8..24b4cc3 100644
--- a/src/views/financialManagement/assets/fixedAssets.vue
+++ b/src/views/financialManagement/assets/fixedAssets.vue
@@ -38,7 +38,7 @@
<div>
<el-button type="primary" @click="add" icon="Plus">鏂板璧勪骇</el-button>
<el-button type="warning" @click="handleDepreciation" icon="Money">鎶樻棫璁℃彁</el-button>
- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+ <!-- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button> -->
</div>
</div>
<PIMTable
diff --git a/src/views/financialManagement/assets/intangibleAssets.vue b/src/views/financialManagement/assets/intangibleAssets.vue
index 47820c2..4642166 100644
--- a/src/views/financialManagement/assets/intangibleAssets.vue
+++ b/src/views/financialManagement/assets/intangibleAssets.vue
@@ -39,7 +39,7 @@
<div>
<el-button type="primary" @click="add" icon="Plus">鏂板璧勪骇</el-button>
<el-button type="warning" @click="handleAmortization" icon="Money">鎽婇攢璁℃彁</el-button>
- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+ <!-- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button> -->
</div>
</div>
<PIMTable
diff --git a/src/views/financialManagement/generalLedger/index.vue b/src/views/financialManagement/generalLedger/index.vue
index 556567b..a7b1d30 100644
--- a/src/views/financialManagement/generalLedger/index.vue
+++ b/src/views/financialManagement/generalLedger/index.vue
@@ -44,8 +44,8 @@
<el-button type="primary"
@click="add"
icon="Plus">鏂板</el-button>
- <el-button @click="handleOut"
- icon="Download">瀵煎嚭</el-button>
+ <!-- <el-button @click="handleOut"
+ icon="Download">瀵煎嚭</el-button> -->
</div>
</div>
<el-table ref="tableRef"
diff --git a/src/views/financialManagement/voucher/detailLedger.vue b/src/views/financialManagement/voucher/detailLedger.vue
index 1909d0e..c07574c 100644
--- a/src/views/financialManagement/voucher/detailLedger.vue
+++ b/src/views/financialManagement/voucher/detailLedger.vue
@@ -32,7 +32,7 @@
<el-form-item>
<el-button type="primary" @click="getTableData">鏌ヨ</el-button>
<el-button @click="resetFilters">閲嶇疆</el-button>
- <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>
+<!-- <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>-->
<el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
</el-form-item>
</el-form>
diff --git a/src/views/financialManagement/voucher/generalLedger.vue b/src/views/financialManagement/voucher/generalLedger.vue
index 9683487..b362279 100644
--- a/src/views/financialManagement/voucher/generalLedger.vue
+++ b/src/views/financialManagement/voucher/generalLedger.vue
@@ -32,34 +32,34 @@
<el-form-item>
<el-button type="primary" @click="getTableData">鏌ヨ</el-button>
<el-button @click="resetFilters">閲嶇疆</el-button>
- <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>
- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+<!-- <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>-->
+ <!-- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button> -->
</el-form-item>
</el-form>
<div class="table_list">
<el-table :data="dataList" border style="width: 100%">
- <el-table-column prop="date" label="鏃ユ湡" width="120" />
- <el-table-column prop="voucherNo" label="鍑瘉瀛楀彿" width="120" />
- <el-table-column prop="summary" label="鎽樿" min-width="200" show-overflow-tooltip />
- <el-table-column prop="debit" label="鍊熸柟" width="150">
+ <el-table-column prop="date" label="鏃ユ湡"/>
+ <!-- <el-table-column prop="voucherNo" label="鍑瘉瀛楀彿" width="120" /> -->
+ <!-- <el-table-column prop="summary" label="鎽樿" min-width="200" show-overflow-tooltip /> -->
+ <el-table-column prop="debit" label="鍊熸柟">
<template #default="{ row }">
<span v-if="row.debit > 0" class="text-danger">楼{{ formatMoney(row.debit) }}</span>
<span v-else>-</span>
</template>
</el-table-column>
- <el-table-column prop="credit" label="璐锋柟" width="150">
+ <el-table-column prop="credit" label="璐锋柟">
<template #default="{ row }">
<span v-if="row.credit > 0" class="text-success">楼{{ formatMoney(row.credit) }}</span>
<span v-else>-</span>
</template>
</el-table-column>
- <el-table-column label="鏂瑰悜" width="80">
+ <el-table-column label="鏂瑰悜">
<template #default="{ row }">
<el-tag :type="row.direction === '鍊�' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
</template>
</el-table-column>
- <el-table-column label="浣欓" width="150">
+ <el-table-column label="浣欓">
<template #default="{ row }">
<span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">楼{{ formatMoney(Math.abs(row.balance)) }}</span>
</template>
diff --git a/src/views/financialManagement/voucher/index.vue b/src/views/financialManagement/voucher/index.vue
index 03c0856..1aa6f69 100644
--- a/src/views/financialManagement/voucher/index.vue
+++ b/src/views/financialManagement/voucher/index.vue
@@ -32,13 +32,13 @@
<div class="table_list">
<div class="actions">
<div>
- <el-statistic title="鍊熸柟鍚堣" :value="totalDebit" precision="2" prefix="楼" />
- <el-statistic title="璐锋柟鍚堣" :value="totalCredit" precision="2" prefix="楼" style="margin-left: 30px;" />
+ <el-statistic title="鍊熸柟鍚堣" :value="totalDebit" :precision="2" prefix="楼" />
+ <el-statistic title="璐锋柟鍚堣" :value="totalCredit" :precision="2" prefix="楼" style="margin-left: 30px;" />
</div>
<div>
<el-button type="primary" @click="add" icon="Plus">鏂板鍑瘉</el-button>
- <el-button @click="handleImport" icon="Upload">瀵煎叆</el-button>
- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+ <!-- <el-button @click="handleImport" icon="Upload">瀵煎叆</el-button> -->
+ <!-- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button> -->
</div>
</div>
<PIMTable
@@ -84,6 +84,11 @@
<span class="label">鍑瘉瀛楋細</span>
<el-select v-model="form.voucherPrefix" :disabled="isViewMode" style="width: 70px;">
<el-option label="璁�" value="璁�" />
+ <el-option label="鐜�" value="鐜�" />
+ <el-option label="閾�" value="閾�" />
+ <el-option label="杞�" value="杞�" />
+ <el-option label="鏀�" value="鏀�" />
+ <el-option label="浠�" value="浠�" />
</el-select>
<el-input v-model="form.voucherNum" :disabled="isViewMode" style="width: 60px;" />
<span class="label" style="margin-left: 5px;">鍙�</span>
@@ -96,7 +101,6 @@
<span class="label">闄勪欢锛�</span>
<el-input-number v-model="form.attachmentCount" :disabled="isViewMode" :min="0" :controls="false" style="width: 60px;" />
<span class="label" style="margin-left: 5px;">寮�</span>
- <el-button type="primary" link :disabled="isViewMode" style="margin-left: 10px;">涓婁紶鏂囦欢</el-button>
</div>
</div>
<div class="voucher-table">
@@ -153,12 +157,12 @@
@change="(val) => handleSubjectChange(val, rowIndex)"
@focus="selectRow(rowIndex)"
/>
- <div class="subject-name">{{ entry.subjectName }}</div>
+ <!-- <div class="subject-name">{{ entry.subjectName }}</div> -->
</td>
<!-- 鍊熸柟11鍒� -->
<template v-if="editingCell.row === rowIndex && editingCell.type === 'debit'">
<td colspan="11" class="debit-input-cell">
- <el-input-number ref="amountInputRef" v-model="entry.debit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
+ <el-input-number ref="amountInputRef" v-model="entry.debit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" />
</td>
</template>
<template v-else>
@@ -169,7 +173,7 @@
<!-- 璐锋柟11鍒� -->
<template v-if="editingCell.row === rowIndex && editingCell.type === 'credit'">
<td colspan="11" class="credit-input-cell">
- <el-input-number ref="amountInputRef" v-model="entry.credit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
+ <el-input-number ref="amountInputRef" v-model="entry.credit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" :value-on-clear="undefined" size="small" @blur="finishEdit" class="full-width-input" />
</td>
</template>
<template v-else>
@@ -217,7 +221,36 @@
</el-select>
</div>
</div>
+ <!-- 缂栬緫妯″紡锛氫娇鐢� AttachmentUploadFile 涓婁紶缁勪欢 -->
+ <div class="voucher-attachment-upload" v-if="!isViewMode">
+ <div class="attachment-label">闄勪欢涓婁紶锛�</div>
+ <AttachmentUploadFile
+ v-model:fileList="form.attachments"
+ :disabled="isViewMode"
+ :limit="10"
+ :fileSize="50"
+ buttonText="鐐瑰嚮涓婁紶闄勪欢"
+ @change="handleAttachmentChange"
+ />
+ </div>
</el-form>
+ <!-- 鏌ョ湅妯″紡锛氬睍绀洪檮浠跺垪琛紙鏀惧湪 el-form 澶栭潰锛岄伩鍏嶈 disabled锛� -->
+ <div class="voucher-attachment-upload" v-if="isViewMode && form.attachments?.length">
+ <div class="attachment-label">闄勪欢鍒楄〃锛�</div>
+ <el-table :data="form.attachments" border class="attachment-table">
+ <el-table-column label="闄勪欢鍚嶇О" show-overflow-tooltip>
+ <template #default="scope">
+ {{ scope.row.originalFilename || scope.row.name || scope.row.fileName || '鏈懡鍚嶆枃浠�' }}
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
+ <template #default="scope">
+ <el-button link type="primary" size="small" @click="previewFile(scope.row)">棰勮</el-button>
+ <el-button link type="primary" size="small" @click="downloadFile(scope.row)">涓嬭浇</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
</div>
<template #footer>
<div>
@@ -226,6 +259,8 @@
</div>
</template>
</FormDialog>
+ <!-- 鏂囦欢棰勮缁勪欢 -->
+ <FilePreview ref="filePreviewRef" />
</div>
</template>
@@ -233,6 +268,10 @@
import { ref, reactive, onMounted, computed, nextTick } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
+import AttachmentUploadFile from "@/components/AttachmentUpload/file/index.vue";
+import FileList from "@/components/Dialog/FileList.vue";
+import FilePreview from "@/components/filePreview/index.vue";
+import download from "@/plugins/download.js";
import useUserStore from "@/store/modules/user";
import { userListNoPageByTenantId } from "@/api/system/user";
import { listAccountSubject } from "@/api/financialManagement/accountSubject";
@@ -284,6 +323,7 @@
const isEdit = ref(false);
const currentId = ref(null);
const isViewMode = computed(() => dialogMode.value === "view");
+const filePreviewRef = ref(null);
const fallbackSubjectTree = [
{ subjectCode: "1001", subjectName: "搴撳瓨鐜伴噾", balanceDirection: "鍊熸柟", children: [] },
@@ -326,8 +366,8 @@
subjectName: "",
balanceDirection: "",
summary: "",
- debit: 0,
- credit: 0,
+ debit: undefined,
+ credit: undefined,
});
const createDefaultForm = () => ({
@@ -336,6 +376,7 @@
voucherNum: "",
voucherDate: "",
attachmentCount: 0,
+ attachments: [],
entries: [createEmptyEntry(), createEmptyEntry()],
creator: getDefaultCreator(),
remark: "",
@@ -490,6 +531,31 @@
form.entries.push(createEmptyEntry());
};
+const handleAttachmentChange = (fileList) => {
+ form.attachmentCount = fileList?.length || 0;
+};
+
+// 浣跨敤椤圭洰灏佽鐨� filePreview 缁勪欢棰勮鏂囦欢
+const previewFile = (row) => {
+ const url = row.previewURL || row.previewUrl || row.url;
+ if (url && filePreviewRef.value) {
+ filePreviewRef.value.open(url);
+ } else {
+ ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曢瑙�');
+ }
+};
+
+// 浣跨敤椤圭洰灏佽鐨� download 鎻掍欢涓嬭浇鏂囦欢
+const downloadFile = (row) => {
+ const url = row.downloadURL || row.downloadUrl || row.url;
+ if (url) {
+ const filename = row.originalFilename || row.name || row.fileName || 'download';
+ download.byUrl(url, filename);
+ } else {
+ ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曚笅杞�');
+ }
+};
+
const selectRow = (index) => {
selectedRowIndex.value = index;
};
@@ -589,10 +655,13 @@
const { data } = await getVoucherDetail(row.id);
const detail = data || row;
const parts = (detail.voucherNo || "").split("-");
- Object.assign(form, createDefaultForm(), detail, {
+ const attachments = detail.storageBlobVOList || detail.storageBlobDTOs || detail.attachments || [];
+ Object.assign(form, createDefaultForm(), {
+ ...detail,
voucherPrefix: parts[0] || "璁�",
voucherNum: parts[1] || "",
creator: detail.creator || getDefaultCreator(),
+ attachments,
entries:
detail.entries?.map(item => ({
subjectCode: item.subjectCode || "",
@@ -696,6 +765,7 @@
remark: form.remark,
debit: totalDebitEntry.value,
credit: totalCreditEntry.value,
+ storageBlobDTOs: form.attachments || [],
entries: validEntries.map(entry => ({
subjectCode: entry.subjectCode,
subjectName: entry.subjectName,
@@ -801,6 +871,21 @@
}
}
+.voucher-attachment-upload {
+ margin-top: 15px;
+ padding: 0 10px;
+
+ .attachment-label {
+ font-size: 14px;
+ color: #606266;
+ margin-bottom: 10px;
+ }
+
+ .attachment-table {
+ border-radius: 4px;
+ }
+}
+
.voucher-table {
border: 1px solid #dcdfe6;
border-right: none;
diff --git a/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue b/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
new file mode 100644
index 0000000..a835ef4
--- /dev/null
+++ b/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
@@ -0,0 +1,227 @@
+<template>
+ <el-dialog
+ v-model="isShow"
+ title="搴撳瓨璇︽儏"
+ width="90%"
+ top="3vh"
+ class="batch-no-qty-detail-dialog"
+ @close="closeModal"
+ >
+ <div class="detail-content">
+ <div class="detail-table-wrapper">
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ style="width: 100%"
+ height="100%"
+ >
+ <el-table-column
+ label="浜у搧鍚嶇О"
+ prop="productName"
+ show-overflow-tooltip
+ />
+ <el-table-column label="瑙勬牸鍨嬪彿" prop="model" show-overflow-tooltip />
+ <el-table-column label="鍗曚綅" prop="unit" show-overflow-tooltip />
+ <el-table-column label="鎵瑰彿" prop="batchNo" show-overflow-tooltip />
+ <el-table-column
+ label="鍚堟牸搴撳瓨鏁伴噺"
+ prop="qualifiedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="涓嶅悎鏍煎簱瀛樻暟閲�"
+ prop="unQualifiedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鍚堟牸鍐荤粨鏁伴噺"
+ prop="qualifiedLockedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="涓嶅悎鏍煎喕缁撴暟閲�"
+ prop="unQualifiedLockedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="搴撳瓨棰勮鏁伴噺"
+ prop="warnNum"
+ show-overflow-tooltip
+ />
+ <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
+ <el-table-column
+ label="鏈�杩戞洿鏂版椂闂�"
+ prop="updateTime"
+ show-overflow-tooltip
+ />
+ <el-table-column fixed="right" label="鎿嶄綔" min-width="180" align="center">
+ <template #default="scope">
+ <el-button
+ link
+ type="primary"
+ @click="handleSubtract(scope.row)"
+ :disabled="
+ (scope.row.qualifiedUnLockedQuantity || 0) +
+ (scope.row.qualifiedPendingOutQuantity || 0) <=
+ 0 &&
+ (scope.row.unQualifiedUnLockedQuantity || 0) +
+ (scope.row.unQualifiedPendingOutQuantity || 0) <=
+ 0
+ "
+ >棰嗙敤</el-button
+ >
+ <el-button
+ link
+ type="primary"
+ v-if="
+ scope.row.unQualifiedUnLockedQuantity > 0 ||
+ scope.row.qualifiedUnLockedQuantity > 0
+ "
+ @click="handleFrozen(scope.row)"
+ >鍐荤粨</el-button
+ >
+ <el-button
+ link
+ type="primary"
+ v-if="
+ scope.row.qualifiedLockedQuantity > 0 ||
+ scope.row.unQualifiedLockedQuantity > 0
+ "
+ @click="handleThaw(scope.row)"
+ >瑙e喕</el-button
+ >
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
+ </div>
+ </el-dialog>
+</template>
+
+<script setup>
+import pagination from "@/components/PIMTable/Pagination.vue";
+import { computed, reactive, ref, watch } from "vue";
+import { getStockInventoryBatchNoQty } from "@/api/inventoryManagement/stockInventory.js";
+
+const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ },
+ record: {
+ type: Object,
+ default: () => ({}),
+ },
+});
+
+const emit = defineEmits(["update:visible", "subtract", "frozen", "thaw"]);
+
+const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit("update:visible", val);
+ },
+});
+
+const tableData = ref([]);
+const tableLoading = ref(false);
+const total = ref(0);
+const page = reactive({
+ current: 1,
+ size: 20,
+});
+
+const getList = () => {
+ if (!props.record?.productId || !props.record?.productModelId) {
+ tableData.value = [];
+ total.value = 0;
+ return;
+ }
+
+ tableLoading.value = true;
+ getStockInventoryBatchNoQty({
+ current: page.current,
+ size: page.size,
+ productId: props.record.productId,
+ productModelId: props.record.productModelId,
+ })
+ .then((res) => {
+ tableData.value = res.data?.records || [];
+ total.value = res.data?.total || 0;
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+};
+
+const paginationChange = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+
+const handleSubtract = (row) => {
+ emit("subtract", row);
+};
+
+const handleFrozen = (row) => {
+ emit("frozen", row);
+};
+
+const handleThaw = (row) => {
+ emit("thaw", row);
+};
+
+const closeModal = () => {
+ isShow.value = false;
+ page.current = 1;
+ page.size = 20;
+ tableData.value = [];
+ total.value = 0;
+};
+
+watch(
+ () => props.visible,
+ (visible) => {
+ if (!visible) {
+ return;
+ }
+ page.current = 1;
+ getList();
+ },
+ { immediate: true }
+);
+</script>
+
+<style scoped lang="scss">
+.detail-content {
+ display: flex;
+ flex-direction: column;
+ height: calc(100vh - 170px);
+ min-height: 520px;
+}
+
+.detail-table-wrapper {
+ flex: 1;
+ min-height: 0;
+}
+
+:deep(.batch-no-qty-detail-dialog .el-dialog) {
+ max-width: calc(100vw - 48px);
+}
+
+:deep(.batch-no-qty-detail-dialog .el-dialog__body) {
+ padding-top: 12px;
+}
+</style>
diff --git a/src/views/inventoryManagement/stockManagement/Record.vue b/src/views/inventoryManagement/stockManagement/Record.vue
index 7c0a461..3b532f8 100644
--- a/src/views/inventoryManagement/stockManagement/Record.vue
+++ b/src/views/inventoryManagement/stockManagement/Record.vue
@@ -3,143 +3,233 @@
<div class="search_form mb10">
<div>
<span class="search_title ml10">浜у搧澶х被锛�</span>
- <el-input v-model="searchForm.productName"
- style="width: 240px"
- placeholder="璇疯緭鍏�"
- clearable/>
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
+ <el-input
+ v-model="searchForm.productName"
+ style="width: 240px"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
+ >鎼滅储</el-button
+ >
</div>
<div>
- <el-button type="primary" @click="isShowNewModal = true">鏂板搴撳瓨</el-button>
- <el-button type="info" plain icon="Upload" @click="isShowImportModal = true">
+ <el-button type="primary" @click="isShowNewModal = true"
+ >鏂板搴撳瓨</el-button
+ >
+ <el-button
+ type="info"
+ plain
+ icon="Upload"
+ @click="isShowImportModal = true"
+ >
瀵煎叆搴撳瓨
</el-button>
<el-button @click="handleOut">瀵煎嚭</el-button>
</div>
</div>
<div class="table_list">
- <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
- :expand-row-keys="expandedRowKeys" :row-key="(row, index) => index" style="width: 100%"
- :row-class-name="tableRowClassName" height="calc(100vh - 18.5em)">
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ :expand-row-keys="expandedRowKeys"
+ :row-key="(row, index) => index"
+ style="width: 100%"
+ :row-class-name="tableRowClassName"
+ height="calc(100vh - 18.5em)"
+ >
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column label="浜у搧澶х被" prop="productName" show-overflow-tooltip />
+ <el-table-column
+ label="浜у搧鍚嶇О"
+ prop="productName"
+ show-overflow-tooltip
+ />
<el-table-column label="瑙勬牸鍨嬪彿" prop="model" show-overflow-tooltip />
<el-table-column label="鍗曚綅" prop="unit" show-overflow-tooltip />
<el-table-column label="鎵瑰彿" prop="batchNo" show-overflow-tooltip />
- <el-table-column label="鍚堟牸搴撳瓨鏁伴噺" prop="qualifiedQuantity" show-overflow-tooltip />
- <el-table-column label="涓嶅悎鏍煎簱瀛樻暟閲�" prop="unQualifiedQuantity" show-overflow-tooltip />
- <el-table-column label="鍚堟牸鍐荤粨鏁伴噺" prop="qualifiedLockedQuantity" show-overflow-tooltip />
- <el-table-column label="涓嶅悎鏍煎喕缁撴暟閲�" prop="unQualifiedLockedQuantity" show-overflow-tooltip />
- <el-table-column label="搴撳瓨棰勮鏁伴噺" prop="warnNum" show-overflow-tooltip />
- <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
- <el-table-column label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" show-overflow-tooltip />
- <el-table-column fixed="right" label="鎿嶄綔" min-width="90" align="center">
+ <el-table-column
+ label="鍚堟牸搴撳瓨鏁伴噺"
+ prop="qualifiedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="涓嶅悎鏍煎簱瀛樻暟閲�"
+ prop="unQualifiedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鍚堟牸鍐荤粨鏁伴噺"
+ prop="qualifiedLockedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="涓嶅悎鏍煎喕缁撴暟閲�"
+ prop="unQualifiedLockedQuantity"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="搴撳瓨棰勮鏁伴噺"
+ prop="warnNum"
+ show-overflow-tooltip
+ />
+ <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
+ <el-table-column
+ label="鏈�杩戞洿鏂版椂闂�"
+ prop="updateTime"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ fixed="right"
+ label="鎿嶄綔"
+ min-width="80"
+ align="center"
+ >
<template #default="scope">
- <el-button link type="primary" @click="showSubtractModal(scope.row)" :disabled="((scope.row.qualifiedUnLockedQuantity || 0) + (scope.row.qualifiedPendingOutQuantity || 0) <= 0) && ((scope.row.unQualifiedUnLockedQuantity || 0) + (scope.row.unQualifiedPendingOutQuantity || 0) <= 0)">棰嗙敤</el-button>
- <el-button link type="primary" v-if="scope.row.unQualifiedUnLockedQuantity > 0 || scope.row.qualifiedUnLockedQuantity > 0" @click="showFrozenModal(scope.row)">鍐荤粨</el-button>
- <el-button link type="primary" v-if="scope.row.qualifiedLockedQuantity > 0 || scope.row.unQualifiedLockedQuantity > 0" @click="showThawModal(scope.row)">瑙e喕</el-button>
+ <el-button
+ link
+ type="primary"
+ @click="showDetailModal(scope.row)"
+ >璇︽儏</el-button
+ >
</template>
</el-table-column>
</el-table>
- <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
- :page="page.current" :limit="page.size" @pagination="paginationChange" />
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
</div>
- <new-stock-inventory v-if="isShowNewModal"
- v-model:visible="isShowNewModal"
- :top-product-parent-id="props.productId"
- @completed="handleQuery" />
+ <batch-no-qty-detail
+ v-if="isShowDetailModal"
+ v-model:visible="isShowDetailModal"
+ :record="record"
+ @subtract="handleDetailSubtract"
+ @frozen="handleDetailFrozen"
+ @thaw="handleDetailThaw"
+ />
+ <new-stock-inventory
+ v-if="isShowNewModal"
+ v-model:visible="isShowNewModal"
+ :top-product-parent-id="props.productId"
+ @completed="handleQuery"
+ />
- <subtract-stock-inventory v-if="isShowSubtractModal"
- v-model:visible="isShowSubtractModal"
- :record="record"
- :type="record.stockType"
- @completed="handleQuery" />
+ <subtract-stock-inventory
+ v-if="isShowSubtractModal"
+ v-model:visible="isShowSubtractModal"
+ :record="record"
+ :type="record.stockType"
+ @completed="handleQuery"
+ />
<!-- 瀵煎叆搴撳瓨-->
- <import-stock-inventory v-if="isShowImportModal"
- v-model:visible="isShowImportModal"
- type="qualified"
- @uploadSuccess="handleQuery" />
+ <import-stock-inventory
+ v-if="isShowImportModal"
+ v-model:visible="isShowImportModal"
+ type="qualified"
+ @uploadSuccess="handleQuery"
+ />
<!-- 鍐荤粨/瑙e喕搴撳瓨-->
- <frozen-and-thaw-stock-inventory v-if="isShowFrozenAndThawModal"
- v-model:visible="isShowFrozenAndThawModal"
- :record="record"
- :operation-type="operationType"
- :type="record.stockType"
- @completed="handleQuery" />
+ <frozen-and-thaw-stock-inventory
+ v-if="isShowFrozenAndThawModal"
+ v-model:visible="isShowFrozenAndThawModal"
+ :record="record"
+ :operation-type="operationType"
+ :type="record.stockType"
+ @completed="handleQuery"
+ />
</div>
</template>
<script setup>
-import pagination from '@/components/PIMTable/Pagination.vue'
-import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
-import {ElMessage, ElMessageBox} from "element-plus";
-import {
- getStockInventoryListPageCombined
-} from "@/api/inventoryManagement/stockInventory.js";
+import pagination from "@/components/PIMTable/Pagination.vue";
+import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js";
const props = defineProps({
productId: {
type: Number,
required: true,
- default: 0
- }
+ default: 0,
+ },
});
-const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue"));
-const SubtractStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Subtract.vue"));
-const ImportStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Import.vue"));
-const FrozenAndThawStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/FrozenAndThaw.vue"));
-const { proxy } = getCurrentInstance()
-const tableData = ref([])
-const selectedRows = ref([])
-const record = ref({})
-const tableLoading = ref(false)
+const NewStockInventory = defineAsyncComponent(() =>
+ import("@/views/inventoryManagement/stockManagement/New.vue")
+);
+const SubtractStockInventory = defineAsyncComponent(() =>
+ import("@/views/inventoryManagement/stockManagement/Subtract.vue")
+);
+const ImportStockInventory = defineAsyncComponent(() =>
+ import("@/views/inventoryManagement/stockManagement/Import.vue")
+);
+const FrozenAndThawStockInventory = defineAsyncComponent(() =>
+ import("@/views/inventoryManagement/stockManagement/FrozenAndThaw.vue")
+);
+const BatchNoQtyDetail = defineAsyncComponent(() =>
+ import("@/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue")
+);
+const { proxy } = getCurrentInstance();
+const tableData = ref([]);
+const selectedRows = ref([]);
+const record = ref({});
+const tableLoading = ref(false);
const page = reactive({
current: 1,
size: 100,
-})
-const total = ref(0)
+});
+const total = ref(0);
// 鏄惁鏄剧ず鏂板寮规
-const isShowNewModal = ref(false)
+const isShowNewModal = ref(false);
// 鏄惁鏄剧ず棰嗙敤寮规
-const isShowSubtractModal = ref(false)
+const isShowSubtractModal = ref(false);
// 鏄惁鏄剧ず鍐荤粨/瑙e喕寮规
-const isShowFrozenAndThawModal = ref(false)
+const isShowFrozenAndThawModal = ref(false);
+// 鏄惁鏄剧ず璇︽儏寮规
+const isShowDetailModal = ref(false);
// 鎿嶄綔绫诲瀷
-const operationType = ref('frozen')
+const operationType = ref("frozen");
// 鏄惁鏄剧ず瀵煎叆寮规
-const isShowImportModal = ref(false)
+const isShowImportModal = ref(false);
const data = reactive({
searchForm: {
- productName: '',
+ productName: "",
topParentProductId: props.productId,
- }
-})
-const { searchForm } = toRefs(data)
+ },
+});
+const { searchForm } = toRefs(data);
// 鏌ヨ鍒楄〃
/** 鎼滅储鎸夐挳鎿嶄綔 */
const handleQuery = () => {
- page.current = 1
- getList()
-}
+ page.current = 1;
+ getList();
+};
const paginationChange = (obj) => {
page.current = obj.page;
page.size = obj.limit;
- getList()
-}
+ getList();
+};
const getList = () => {
- tableLoading.value = true
- getStockInventoryListPageCombined({ ...searchForm.value, ...page }).then(res => {
- tableLoading.value = false
- tableData.value = res.data.records
- total.value = res.data.total
- // 鏁版嵁鍔犺浇瀹屾垚鍚庢鏌ュ簱瀛�
- // checkStockAndCreatePurchase();
- }).catch(() => {
- tableLoading.value = false
- })
-}
+ tableLoading.value = true;
+ getStockInventoryListPageCombined({ ...searchForm.value, ...page })
+ .then((res) => {
+ tableLoading.value = false;
+ tableData.value = res.data.records;
+ total.value = res.data.total;
+ // 鏁版嵁鍔犺浇瀹屾垚鍚庢鏌ュ簱瀛�
+ // checkStockAndCreatePurchase();
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+};
const handleFileSuccess = (response) => {
const { code, msg } = response;
@@ -154,61 +244,89 @@
// 鐐瑰嚮棰嗙敤
const showSubtractModal = (row) => {
- record.value = row
- isShowSubtractModal.value = true
-}
+ record.value = row;
+ isShowSubtractModal.value = true;
+};
+
+// 鐐瑰嚮璇︽儏
+const showDetailModal = (row) => {
+ if (!row?.productId || !row?.productModelId) {
+ proxy.$modal.msgError("褰撳墠鏁版嵁缂哄皯浜у搧ID鎴栬鏍煎瀷鍙稩D");
+ return;
+ }
+ record.value = row;
+ isShowDetailModal.value = true;
+};
+
+const handleDetailSubtract = (row) => {
+ isShowDetailModal.value = false;
+ showSubtractModal(row);
+};
+
+const handleDetailFrozen = (row) => {
+ isShowDetailModal.value = false;
+ showFrozenModal(row);
+};
+
+const handleDetailThaw = (row) => {
+ isShowDetailModal.value = false;
+ showThawModal(row);
+};
// 鐐瑰嚮鍐荤粨
const showFrozenModal = (row) => {
- record.value = row
- isShowFrozenAndThawModal.value = true
- operationType.value = 'frozen'
-}
+ record.value = row;
+ isShowFrozenAndThawModal.value = true;
+ operationType.value = "frozen";
+};
// 鐐瑰嚮瑙e喕
const showThawModal = (row) => {
- record.value = row
- isShowFrozenAndThawModal.value = true
- operationType.value = 'thaw'
-}
+ record.value = row;
+ isShowFrozenAndThawModal.value = true;
+ operationType.value = "thaw";
+};
// 琛ㄦ牸閫夋嫨鏁版嵁
const handleSelectionChange = (selection) => {
// 杩囨护鎺夊瓙鏁版嵁
- selectedRows.value = selection.filter(item => item.id);
- console.log('selection', selectedRows.value)
-}
-const expandedRowKeys = ref([])
+ selectedRows.value = selection.filter((item) => item.id);
+ console.log("selection", selectedRows.value);
+};
+const expandedRowKeys = ref([]);
// 琛ㄦ牸琛岀被鍚�
const tableRowClassName = ({ row }) => {
const stock = Number(row?.qualifiedUnLockedQuantity ?? 0);
const warn = Number(row?.warnNum ?? 0);
if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
- return '';
+ return "";
}
- return stock < warn ? 'row-low-stock' : '';
+ return stock < warn ? "row-low-stock" : "";
};
// 瀵煎嚭
const handleOut = () => {
- ElMessageBox.confirm(
- '鏄惁纭瀵煎嚭锛�',
- '瀵煎嚭', {
- confirmButtonText: '纭',
- cancelButtonText: '鍙栨秷',
- type: 'warning',
- }
- ).then(() => {
- proxy.download("/stockInventory/exportStockInventory", {topParentProductId: props.productId}, '搴撳瓨淇℃伅.xlsx')
- }).catch(() => {
- proxy.$modal.msg("宸插彇娑�")
+ ElMessageBox.confirm("鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
})
-}
+ .then(() => {
+ proxy.download(
+ "/stockInventory/exportStockInventory",
+ { topParentProductId: props.productId },
+ "搴撳瓨淇℃伅.xlsx"
+ );
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
onMounted(() => {
- getList()
-})
+ getList();
+});
</script>
<style scoped lang="scss">
diff --git a/src/views/personnelManagement/contractManagement/index.vue b/src/views/personnelManagement/contractManagement/index.vue
index a55a502..074b9ac 100644
--- a/src/views/personnelManagement/contractManagement/index.vue
+++ b/src/views/personnelManagement/contractManagement/index.vue
@@ -23,6 +23,12 @@
:total="page.total"></PIMTable>
</div>
<form-dia ref="formDia" @close="handleQuery"></form-dia>
+ <renew-contract
+ v-if="isShowRenewContractModal"
+ v-model:visible="isShowRenewContractModal"
+ :id="id"
+ @completed="handleQuery"
+ />
<!-- 鍚堝悓瀵煎叆瀵硅瘽妗� -->
<el-dialog
@@ -71,8 +77,9 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
-import { onMounted, ref } from "vue";
+import { onMounted, ref, defineAsyncComponent } from "vue";
import FormDia from "@/views/personnelManagement/contractManagement/components/formDia.vue";
+const RenewContract = defineAsyncComponent(() => import("@/views/personnelManagement/employeeRecord/components/RenewContract.vue"));
import { ElMessageBox } from "element-plus";
import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js";
import dayjs from "dayjs";
@@ -183,7 +190,7 @@
label: "鎿嶄綔",
align: "center",
fixed: 'right',
- width: 120,
+ width: 160,
operation: [
{
name: "璇︽儏",
@@ -191,11 +198,22 @@
clickFun: (row) => {
openForm("edit", row);
},
+ },
+ {
+ name: "缁鍚堝悓",
+ type: "text",
+ showHide: row => row.staffState === 1,
+ clickFun: (row) => {
+ isShowRenewContractModal.value = true;
+ id.value = row.id;
+ },
}
],
},
]);
const filesDia = ref()
+const isShowRenewContractModal = ref(false);
+const id = ref(0);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
diff --git a/src/views/personnelManagement/employeeRecord/index.vue b/src/views/personnelManagement/employeeRecord/index.vue
index cd4ecf5..5dda8c7 100644
--- a/src/views/personnelManagement/employeeRecord/index.vue
+++ b/src/views/personnelManagement/employeeRecord/index.vue
@@ -52,16 +52,14 @@
:tableLoading="tableLoading"
@pagination="pagination"
:total="page.total"
- ></PIMTable>
+ >
+ <template #positiveDate="{ row }">
+ <span :class="getPositiveDateClass(row.positiveDate)">{{ row.positiveDate }}</span>
+ </template>
+ </PIMTable>
</div>
<show-form-dia ref="formDia" @close="handleQuery"></show-form-dia>
<new-or-edit-form-dia ref="formDiaNewOrEditFormDia" @close="handleQuery"></new-or-edit-form-dia>
- <renew-contract
- v-if="isShowRenewContractModal"
- v-model:visible="isShowRenewContractModal"
- :id="id"
- @completed="handleQuery"
- />
<!-- 瀵煎叆瀵硅瘽妗� -->
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
@@ -107,7 +105,6 @@
const NewOrEditFormDia = defineAsyncComponent(() => import("@/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue"));
const ShowFormDia = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/Show.vue"));
-const RenewContract = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/RenewContract.vue"));
const data = reactive({
searchForm: {
@@ -119,8 +116,6 @@
deptOptions: [],
});
const { searchForm, deptOptions } = toRefs(data);
-const isShowRenewContractModal = ref(false);
-const id = ref(0);
const tableColumn = ref([
{
label: "鐘舵��",
@@ -177,6 +172,13 @@
width: 120,
},
{
+ label: "杞鏃ユ湡",
+ prop: "positiveDate",
+ width: 120,
+ dataType: "slot",
+ slot: "positiveDate",
+ },
+ {
label: "骞撮緞",
prop: "age",
},
@@ -208,22 +210,6 @@
openFormNewOrEditFormDia("edit", row);
},
},
- {
- name: "缁鍚堝悓",
- type: "text",
- showHide: row => row.staffState === 1,
- clickFun: (row) => {
- isShowRenewContractModal.value = true;
- id.value = row.id;
- },
- },
- // {
- // name: "璇︽儏",
- // type: "text",
- // clickFun: (row) => {
- // openForm("edit", row);
- // },
- // },
],
},
]);
@@ -253,6 +239,22 @@
// 涓婁紶鐨勫湴鍧�
url: import.meta.env.VITE_APP_BASE_API + "/staff/staffOnJob/import"
})
+
+// 鍒ゆ柇杞鏃ユ湡鏄惁鍦�7澶╁唴
+const getPositiveDateClass = (positiveDate) => {
+ if (!positiveDate) return '';
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+ const positive = new Date(positiveDate);
+ positive.setHours(0, 0, 0, 0);
+ const diffTime = positive - today;
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+ // 7澶╁唴杞锛堝寘鎷粖澶╋級鏄剧ず璀﹀憡鑹�
+ if (diffDays >= 0 && diffDays <= 7) {
+ return 'positive-date-warning';
+ }
+ return '';
+};
const fetchDeptOptions = () => {
deptTreeSelect().then(response => {
@@ -402,4 +404,9 @@
.search_title2 {
margin-left: 10px;
}
+
+.positive-date-warning {
+ color: #f56c6c;
+ font-weight: bold;
+}
</style>
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index cffdcc6..adf7b6e 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -693,6 +693,7 @@
const salesContractList = ref([]);
const supplierList = ref([]);
const tableLoading = ref(false);
+ const recordId = ref();
const fileListDialogVisible = ref(false);
const page = reactive({
current: 1,
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index 99d4ab1..83ee60a 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -315,7 +315,7 @@
:step="1"
controls-position="right"
style="width: 100%"
- @change="handleUnitQuantityChange(row)"
+ @change="handleUnitQuantityChange"
:disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
</el-form-item>
</template>
@@ -333,7 +333,7 @@
:step="1"
controls-position="right"
style="width: 100%"
- :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+ :disabled="true" />
</el-form-item>
</template>
</el-table-column>
@@ -1089,6 +1089,53 @@
}
});
};
+
+ const toQuantityNumber = value => {
+ const numberValue = Number(value);
+ if (!Number.isFinite(numberValue)) {
+ return 0;
+ }
+ return Number(numberValue.toFixed(2));
+ };
+
+ const syncDemandedQuantityTree = (items, parentDemandedQuantity = null) => {
+ items.forEach(item => {
+ if (parentDemandedQuantity !== null) {
+ item.demandedQuantity = toQuantityNumber(
+ parentDemandedQuantity * toQuantityNumber(item.unitQuantity)
+ );
+ }
+
+ if (Array.isArray(item.children) && item.children.length > 0) {
+ syncDemandedQuantityTree(
+ item.children,
+ toQuantityNumber(item.demandedQuantity)
+ );
+ }
+ });
+ };
+
+ const recalculateDemandedQuantities = () => {
+ if (pageType.value !== "order") {
+ return;
+ }
+
+ const rootDemandedQuantity = routeInfo.value.quantity;
+ if (
+ rootDemandedQuantity === undefined ||
+ rootDemandedQuantity === null ||
+ rootDemandedQuantity === ""
+ ) {
+ syncDemandedQuantityTree(bomDataValue.value.dataList);
+ return;
+ }
+
+ syncDemandedQuantityTree(
+ bomDataValue.value.dataList,
+ toQuantityNumber(rootDemandedQuantity)
+ );
+ };
+
const processChange = value => {
processOptions.value.forEach(item => {
if (item.id == value) {
@@ -1117,6 +1164,7 @@
);
bomDataValue.value.dataList = data || [];
normalizeTreeData(bomDataValue.value.dataList);
+ recalculateDemandedQuantities();
} catch (err) {
console.error("鑾峰彇BOM鏁版嵁澶辫触锛�", err);
}
@@ -1212,10 +1260,8 @@
});
};
- const handleUnitQuantityChange = row => {
- if (routeInfo.value.quantity && routeInfo.value.quantity !== 0) {
- row.demandedQuantity = (row.unitQuantity || 0) * routeInfo.value.quantity;
- }
+ const handleUnitQuantityChange = () => {
+ recalculateDemandedQuantities();
};
const addchildItem = (item, tempId) => {
@@ -1236,14 +1282,12 @@
"",
operationName: "",
unitQuantity: 1,
- demandedQuantity:
- routeInfo.value.quantity && routeInfo.value.quantity !== 0
- ? 1 * routeInfo.value.quantity
- : 0,
+ demandedQuantity: 0,
children: [],
unit: "",
tempId: new Date().getTime(),
});
+ recalculateDemandedQuantities();
return true;
}
if (item.children && item.children.length > 0) {
@@ -1275,14 +1319,12 @@
"",
operationName: "",
unitQuantity: 1,
- demandedQuantity:
- routeInfo.value.quantity && routeInfo.value.quantity !== 0
- ? 1 * routeInfo.value.quantity
- : 0,
+ demandedQuantity: 0,
unit: "",
children: [],
tempId: new Date().getTime(),
});
+ recalculateDemandedQuantities();
return;
}
addchildItem(item, tempId);
@@ -1350,6 +1392,7 @@
console.log(bomDataValue.value.dataList, "bomDataValue.value.dataList");
normalizeTreeData(bomDataValue.value.dataList);
+ recalculateDemandedQuantities();
const valid = validateAllBom();
if (valid) {
@@ -1361,7 +1404,7 @@
.then(() => {
ElMessage.success("BOM淇濆瓨鎴愬姛");
bomDataValue.value.isEdit = false;
- fetchBomData();
+ refreshCurrentPage();
})
.catch(() => {
ElMessage.error("BOM淇濆瓨澶辫触");
@@ -1374,11 +1417,15 @@
}
};
- onMounted(() => {
+ const refreshCurrentPage = () => {
getRouteInfo();
getList();
getProcessList();
fetchBomData();
+ };
+
+ onMounted(() => {
+ refreshCurrentPage();
});
onUnmounted(() => {
diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
index 750d584..0c326cc 100644
--- a/src/views/productionManagement/productStructure/Detail/index.vue
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -86,6 +86,7 @@
:step="1"
controls-position="right"
style="width: 100%"
+ @change="handleUnitQuantityChange"
:disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
</el-form-item>
</template>
@@ -103,7 +104,7 @@
:step="1"
controls-position="right"
style="width: 100%"
- :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
+ :disabled="true" />
</el-form-item>
</template>
</el-table-column>
@@ -268,6 +269,42 @@
});
};
+ const toQuantityNumber = (value: any) => {
+ const numberValue = Number(value);
+ if (!Number.isFinite(numberValue)) {
+ return 0;
+ }
+ return Number(numberValue.toFixed(2));
+ };
+
+ const syncDemandedQuantityTree = (
+ items: any[],
+ parentDemandedQuantity: number | null = null
+ ) => {
+ items.forEach((item: any) => {
+ if (parentDemandedQuantity !== null) {
+ item.demandedQuantity = toQuantityNumber(
+ parentDemandedQuantity * toQuantityNumber(item.unitQuantity)
+ );
+ }
+
+ if (Array.isArray(item.children) && item.children.length > 0) {
+ syncDemandedQuantityTree(
+ item.children,
+ toQuantityNumber(item.demandedQuantity)
+ );
+ }
+ });
+ };
+
+ const recalculateDemandedQuantities = () => {
+ if (!isOrderPage.value) {
+ return;
+ }
+
+ syncDemandedQuantityTree(dataValue.dataList);
+ };
+
const buildSubmitTree = (items: any[]) => {
return items.map((item: any) => {
const current = { ...item };
@@ -279,9 +316,45 @@
});
};
+ const findSiblings = (items: any[], tempId: string): any[] | null => {
+ if (!items || items.length === 0) return null;
+ // 妫�鏌ュ綋鍓嶅眰绾�
+ if (items.some(item => item.tempId === tempId)) {
+ return items;
+ }
+ // 閫掑綊鏌ユ壘瀛愮骇
+ for (const item of items) {
+ if (item.children && item.children.length > 0) {
+ const result = findSiblings(item.children, tempId);
+ if (result) return result;
+ }
+ }
+ return null;
+ };
+
const handleProcessChange = (row: any, value: any) => {
+ if (value) {
+ const siblings = findSiblings(dataValue.dataList, row.tempId);
+ if (siblings) {
+ const isDuplicate = siblings.some(
+ s => s.tempId !== row.tempId && s.processId === value
+ );
+ if (isDuplicate) {
+ const option = getProcessOptionById(value);
+ const processName = option?.name || "璇ュ伐搴�";
+ ElMessage.warning(`鍚屼竴灞傜骇涓嬩笉鑳介�夋嫨閲嶅鐨勬秷鑰楀伐搴忥細${processName}`);
+ row.processId = "";
+ syncProcessOperationFields(row);
+ return;
+ }
+ }
+ }
row.processId = value || "";
syncProcessOperationFields(row);
+ };
+
+ const handleUnitQuantityChange = () => {
+ recalculateDemandedQuantities();
};
const tableData = reactive([
@@ -304,6 +377,7 @@
const { data } = await listProcessBom({ orderId: routeOrderId.value });
dataValue.dataList = (data as any) || [];
normalizeTreeData(dataValue.dataList);
+ recalculateDemandedQuantities();
} else {
// 闈炶鍗曟儏鍐碉細浣跨敤鍘熸潵鐨勬帴鍙�
const { data } = await queryList(routeId.value);
@@ -389,8 +463,37 @@
const validateAll = () => {
let isValid = true;
+ // 鏍¢獙涓�缁勫厔寮熻妭鐐圭殑宸ュ簭鏄惁鍞竴
+ const checkProcessUniqueness = (items: any[]) => {
+ if (!items || items.length === 0 || !isValid) return;
+
+ const processIds = new Set();
+ for (const item of items) {
+ if (item.processId) {
+ if (processIds.has(item.processId)) {
+ const option = getProcessOptionById(item.processId);
+ const processName = option?.name || item.processName || "鏈煡宸ュ簭";
+ ElMessage.error(
+ `浜у搧銆�${item.productName}銆嶇殑娑堣�楀伐搴忋��${processName}銆嶅湪褰撳墠灞傜骇宸插瓨鍦紝璇峰嬁閲嶅璁剧疆`
+ );
+ isValid = false;
+ return;
+ }
+ processIds.add(item.processId);
+ }
+ }
+
+ // 閫掑綊鏍¢獙瀛愮骇鐨勫厔寮熻妭鐐�
+ for (const item of items) {
+ if (item.children && item.children.length > 0) {
+ checkProcessUniqueness(item.children);
+ }
+ }
+ };
+
// 鏍¢獙鍑芥暟
const validateItem = (item: any, isTopLevel = false) => {
+ if (!isValid) return;
// 鏍¢獙褰撳墠椤圭殑蹇呭~瀛楁
if (!item.model) {
ElMessage.error("璇烽�夋嫨瑙勬牸");
@@ -418,7 +521,7 @@
// return;
// }
- // 閫掑綊鏍¢獙瀛愰」
+ // 閫掑綊鏍¢獙瀛愰」瀛楁
if (item.children && item.children.length > 0) {
item.children.forEach(child => {
validateItem(child, false);
@@ -426,7 +529,11 @@
}
};
- // 閬嶅巻鎵�鏈夐《灞傞」
+ // 1. 棣栧厛鏍¢獙鍚屼竴鐖剁骇涓嬬殑鍚屽眰娑堣�楀伐搴忔槸鍚﹀敮涓�
+ checkProcessUniqueness(dataValue.dataList);
+ if (!isValid) return false;
+
+ // 2. 鐒跺悗閬嶅巻鏍¢獙鎵�鏈夐《灞傞」鐨勫瓧娈靛繀濉儏鍐�
dataValue.dataList.forEach(item => {
validateItem(item, true);
});
@@ -437,6 +544,7 @@
const submit = () => {
dataValue.loading = true;
normalizeTreeData(dataValue.dataList);
+ recalculateDemandedQuantities();
// 鍏堣繘琛岃〃鍗曟牎楠�
const valid = validateAll();
@@ -514,6 +622,7 @@
tempId: new Date().getTime(),
});
+ recalculateDemandedQuantities();
return;
}
addchildItem(item, tempId);
@@ -542,6 +651,7 @@
unit: "",
tempId: new Date().getTime(),
});
+ recalculateDemandedQuantities();
return true;
}
if (item.children && item.children.length > 0) {
@@ -587,4 +697,4 @@
await fetchProcessOptions();
await fetchData();
});
-</script>
\ No newline at end of file
+</script>
diff --git a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
index d058896..09e7421 100644
--- a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
+++ b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -111,6 +111,7 @@
<span class="dialog-footer">
<el-button type="primary"
:loading="materialSaving"
+ :disabled="isSaveDisabled"
@click="handleMaterialSave">淇濆瓨</el-button>
<el-button @click="dialogVisible = false">鍙栨秷</el-button>
</span>
@@ -155,6 +156,33 @@
const materialTableLoading = ref(false);
const materialSaving = ref(false);
const materialTableData = ref([]);
+
+ const isSaveDisabled = computed(() => {
+ if (materialTableData.value.length === 0) return true;
+ return !materialTableData.value.some(row => {
+ // 妫�鏌ユ槸鍚︽湁浠讳綍鐢ㄦ埛杈撳叆鍐呭
+ const hasBatch = Array.isArray(row.batchNo) && row.batchNo.length > 0;
+ const hasPickQty =
+ row.pickQty !== null && row.pickQty !== undefined && row.pickQty !== 0;
+
+ if (row.bom) {
+ // 瀵逛簬鏉ヨ嚜BOM鐨勮锛岃緭鍏ユ鍙湁鈥滄壒鍙封�濆拰鈥滈鐢ㄦ暟閲忊��
+ return hasBatch || hasPickQty;
+ } else {
+ // 瀵逛簬鏂板琛岋紝杈撳叆妗嗗寘鎷�滃伐搴忊�濄�佲�滃師鏂欌�濄�佲�滈渶姹傛暟閲忊�濄�佲�滄壒鍙封�濆拰鈥滈鐢ㄦ暟閲忊��
+ const hasOperation = !!row.operationName;
+ const hasMaterial = !!row.materialName;
+ const hasDemanded =
+ row.demandedQuantity !== null &&
+ row.demandedQuantity !== undefined &&
+ row.demandedQuantity !== 0;
+ return (
+ hasBatch || hasPickQty || hasOperation || hasMaterial || hasDemanded
+ );
+ }
+ });
+ });
+
const processOptions = ref([]);
const currentMaterialSelectRowIndex = ref(-1);
let materialTempId = 0;
diff --git a/src/views/productionManagement/productionProcess/index.vue b/src/views/productionManagement/productionProcess/index.vue
index 6d007a4..ee49657 100644
--- a/src/views/productionManagement/productionProcess/index.vue
+++ b/src/views/productionManagement/productionProcess/index.vue
@@ -44,7 +44,7 @@
<div class="card-body">
<!-- <div class="process-name">{{ process.name }}</div> -->
<div class="process-desc">{{ process.remark || '鏆傛棤鎻忚堪' }}</div>
- <div class="process-device">鍏宠仈璁惧: {{ deviceOptions.find(item => item.id === Number(process.deviceLedgerId))?.deviceName|| '鏈叧鑱�' }}</div>
+ <div class="process-device">鍏宠仈璁惧: {{ (deviceOptions.find(item => item.id === Number(process.deviceLedgerId))?.deviceName) || '鏈叧鑱�' }}</div>
</div>
<div class="card-footer">
<div class="status-tag">
@@ -243,6 +243,7 @@
</el-form-item>
<el-form-item label="鏍囧噯鍊�">
<el-input v-model="selectedParam.standardValue"
+ @input="val => onStandardValueInput(val, selectedParam)"
placeholder="璇疯緭鍏ラ粯璁ゅ��" />
</el-form-item>
</el-form>
@@ -273,6 +274,7 @@
<el-form-item label="鏍囧噯鍊�"
prop="standardValue">
<el-input v-model="editParamForm.standardValue"
+ @input="val => onStandardValueInput(val, editParamForm)"
placeholder="璇疯緭鍏ユ爣鍑嗗��" />
</el-form-item>
</el-form>
@@ -392,7 +394,18 @@
technologyParamId: null,
paramName: "",
standardValue: null,
+ paramType: null,
});
+
+ const onStandardValueInput = (val, target) => {
+ const data = target.value || target;
+ const type = data.paramType;
+ if (type === 1) {
+ // 鏁板�兼牸寮忥細涓嶈兘杈撳叆涓枃鎴栬嫳鏂囧瓧绗�
+ data.standardValue = val.replace(/[a-zA-Z\u4e00-\u9fa5]/g, "");
+ }
+ };
+
const editParamRules = {
standardValue: [
{
@@ -403,6 +416,12 @@
if (value === null || value === undefined || value === "") {
callback(new Error("璇疯緭鍏ユ爣鍑嗗��"));
} else {
+ const type = editParamForm.paramType;
+ if (type === 1 && value) {
+ if (/[a-zA-Z\u4e00-\u9fa5]/.test(value)) {
+ return callback(new Error("鏁板�兼牸寮忎笉鑳藉寘鍚腑鑻辨枃瀛楃"));
+ }
+ }
callback();
}
},
@@ -551,7 +570,10 @@
processForm.isQuality = !!process.isQuality;
processForm.isProduction = !!process.isProduction;
processForm.remark = process.remark || "";
- processForm.deviceLedgerId = Number(process.deviceLedgerId);
+ // 濡傛灉璁惧 ID 涓� 0 鎴栬�呭湪璁惧鍒楄〃涓壘涓嶅埌锛屽垯鍥炴樉涓虹┖锛坣ull锛�
+ const deviceId = Number(process.deviceLedgerId);
+ const hasDevice = deviceOptions.value.some(item => item.id === deviceId);
+ processForm.deviceLedgerId = deviceId && hasDevice ? deviceId : null;
processForm.type = process.type;
processDialogVisible.value = true;
};
@@ -717,6 +739,7 @@
editParamForm.technologyParamId = row.technologyParamId;
editParamForm.paramName = row.paramName;
editParamForm.standardValue = row.standardValue;
+ editParamForm.paramType = row.paramType;
editParamDialogVisible.value = true;
};
diff --git a/src/views/productionManagement/productionTraceability/index.vue b/src/views/productionManagement/productionTraceability/index.vue
index ab1c34a..2b220dc 100644
--- a/src/views/productionManagement/productionTraceability/index.vue
+++ b/src/views/productionManagement/productionTraceability/index.vue
@@ -1,5 +1,6 @@
<template>
<div class="app-container">
+ <PageHeader content="鐢熶骇璁㈠崟" />
<el-card style="height:82vh;overflow:auto;">
<template #header>
<div class="card-header">
diff --git a/src/views/productionManagement/workOrderManagement/index.vue b/src/views/productionManagement/workOrderManagement/index.vue
index 119bd9e..60fe511 100644
--- a/src/views/productionManagement/workOrderManagement/index.vue
+++ b/src/views/productionManagement/workOrderManagement/index.vue
@@ -673,7 +673,10 @@
}
}
currentReportRowData.value = row;
- reportForm.planQuantity = row.planQuantity;
+ const planQuantity = Number(row.planQuantity || 0);
+ const completeQuantity = Number(row.completeQuantity || 0);
+ const remainingQuantity = Math.max(0, planQuantity - completeQuantity);
+ reportForm.planQuantity = remainingQuantity;
reportForm.quantity =
row.quantity !== undefined && row.quantity !== null ? row.quantity : null;
reportForm.productProcessRouteItemId = row.productProcessRouteItemId;
diff --git a/src/views/qualityManagement/finalInspection/components/formDia.vue b/src/views/qualityManagement/finalInspection/components/formDia.vue
index 5f4c975..10bfad9 100644
--- a/src/views/qualityManagement/finalInspection/components/formDia.vue
+++ b/src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -2,7 +2,7 @@
<div>
<el-dialog
v-model="dialogFormVisible"
- :title="operationType === 'add' ? '鏂板鍑哄巶妫�楠�' : '缂栬緫鍑哄巶妫�楠�'"
+ :title="operationType === 'add' ? '鏂板鍑哄巶妫�楠�' : operationType === 'view' ? '鏌ョ湅鍑哄巶妫�楠�' : '缂栬緫鍑哄巶妫�楠�'"
width="70%"
@close="closeDia"
>
@@ -18,19 +18,21 @@
@change="getModels"
:data="productOptions"
:render-after-expand="false"
- :disabled="operationType === 'edit'"
+ :disabled="isViewMode || operationType === 'edit'"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
- <el-select v-model="form.productModelId" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'edit'"
+ <el-select v-model="form.productModelId" placeholder="璇烽�夋嫨" clearable :disabled="isViewMode || operationType === 'edit'"
filterable readonly @change="handleChangeModel">
<el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
+ </el-row>
+ <el-row :gutter="30">
<el-col :span="12">
<el-form-item label="鎸囨爣閫夋嫨锛�" prop="testStandardId">
<el-select
@@ -39,6 +41,7 @@
clearable
@change="handleTestStandardChange"
style="width: 100%"
+ :disabled="isViewMode"
>
<el-option
v-for="item in testStandardOptions"
@@ -58,21 +61,52 @@
</el-col>
<el-col :span="12">
<el-form-item label="鏁伴噺锛�" prop="quantity">
- <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="璇疯緭鍏�" clearable :precision="2" :disabled="quantityDisabled"/>
+ <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="璇疯緭鍏�" clearable :precision="2" :disabled="isViewMode || processQuantityDisabled"/>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚堟牸鏁伴噺锛�"
+ prop="qualifiedQuantity">
+ <el-input-number :step="0.01"
+ :min="0"
+ style="width: 100%"
+ v-model="form.qualifiedQuantity"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :precision="2"
+ @change="handleQualifiedQuantityChange"
+ :disabled="isViewMode" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="涓嶅悎鏍兼暟閲忥細"
+ prop="unqualifiedQuantity">
+ <el-input-number :step="0.01"
+ :min="0"
+ style="width: 100%"
+ v-model="form.unqualifiedQuantity"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :precision="2"
+ @change="handleUnqualifiedQuantityChange"
+ :disabled="isViewMode" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="妫�娴嬪崟浣嶏細" prop="checkCompany">
- <el-input v-model="form.checkCompany" placeholder="璇疯緭鍏�" clearable/>
+ <el-input v-model="form.checkCompany" placeholder="璇疯緭鍏�" clearable :disabled="isViewMode"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="妫�娴嬬粨鏋滐細" prop="checkResult">
- <el-select v-model="form.checkResult">
+ <el-select v-model="form.checkResult" :disabled="isViewMode">
<el-option label="鍚堟牸" value="鍚堟牸" />
<el-option label="涓嶅悎鏍�" value="涓嶅悎鏍�" />
+ <el-option label="閮ㄥ垎鍚堟牸" value="閮ㄥ垎鍚堟牸" />
</el-select>
</el-form-item>
</el-col>
@@ -80,10 +114,10 @@
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="妫�楠屽憳锛�" prop="checkName">
- <el-select v-model="form.checkName" placeholder="璇烽�夋嫨" clearable>
- <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
- :value="item.nickName"/>
- </el-select>
+ <el-select v-model="form.checkName" placeholder="璇烽�夋嫨" clearable :disabled="isViewMode">
+ <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
+ :value="item.nickName"/>
+ </el-select>
</el-form-item>
</el-col>
<el-col :span="12">
@@ -96,6 +130,7 @@
format="YYYY-MM-DD"
clearable
style="width: 100%"
+ :disabled="isViewMode"
/>
</el-form-item>
</el-col>
@@ -109,13 +144,16 @@
height="400"
>
<template #slot="{ row }">
- <el-input v-model="row.testValue" clearable/>
+ <el-input v-model="row.testValue" clearable :disabled="isViewMode"/>
</template>
</PIMTable>
<template #footer>
<div class="dialog-footer">
- <el-button type="primary" @click="submitForm">纭</el-button>
- <el-button @click="closeDia">鍙栨秷</el-button>
+ <template v-if="!isViewMode">
+ <el-button type="primary" @click="submitForm">纭</el-button>
+ <el-button @click="closeDia">鍙栨秷</el-button>
+ </template>
+ <el-button v-else @click="closeDia">鍏抽棴</el-button>
</div>
</template>
</el-dialog>
@@ -134,7 +172,7 @@
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
-const operationType = ref('')
+const operationType = ref("");
const data = reactive({
form: {
checkTime: "",
@@ -147,25 +185,31 @@
testStandardId: "",
unit: "",
quantity: "",
+ qualifiedQuantity: "",
+ unqualifiedQuantity: "",
checkCompany: "",
checkResult: "",
},
rules: {
- checkTime: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" },],
+ checkTime: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
process: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
checkName: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
productId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- testStandardId: [{required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change"}],
+ testStandardId: [{ required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change" }],
unit: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ qualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ unqualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
checkCompany: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
checkResult: [{ required: true, message: "璇疯緭鍏�", trigger: "change" }],
},
});
const { form, rules } = toRefs(data);
-// 缂栬緫鏃讹細productMainId 鎴� purchaseLedgerId 浠讳竴鏈夊�煎垯鏁伴噺缃伆
-const quantityDisabled = computed(() => {
+// 鏄惁涓烘煡鐪嬫ā寮�
+const isViewMode = computed(() => operationType.value === 'view');
+// 缂栬緫鏃讹細productMainId 鎴� purchaseLedgerId 浠讳竴鏈夊�煎垯宸ュ簭銆佹暟閲忕疆鐏�
+const processQuantityDisabled = computed(() => {
const v = form.value || {};
return !!(v.productMainId != null || v.purchaseLedgerId != null);
});
@@ -209,7 +253,7 @@
// 鍏堟竻绌鸿〃鍗曢獙璇佺姸鎬侊紝閬垮厤闂儊
await nextTick();
proxy.$refs.formRef?.clearValidate();
-
+
// 骞惰鍔犺浇鍩虹鏁版嵁
const [userListsRes] = await Promise.all([
userListNoPage(),
@@ -219,12 +263,12 @@
})
]);
userList.value = userListsRes.data;
-
+
form.value = {}
testStandardOptions.value = [];
tableData.value = [];
-
- if (operationType.value === 'edit') {
+
+ if (operationType.value === 'edit' || operationType.value === 'view') {
// 鍏堜繚瀛� testStandardId锛岄伩鍏嶈娓呯┖
const savedTestStandardId = row.testStandardId;
// 鍏堣缃〃鍗曟暟鎹紝浣嗘殏鏃舵竻绌� testStandardId锛岀瓑閫夐」鍔犺浇瀹屾垚鍚庡啀璁剧疆
@@ -234,18 +278,18 @@
nextTick(() => {
proxy.$refs.formRef?.clearValidate();
});
-
+
// 缂栬緫妯″紡涓嬶紝骞惰鍔犺浇瑙勬牸鍨嬪彿鍜屾寚鏍囬�夐」
if (currentProductId.value) {
// 璁剧疆浜у搧鍚嶇О
form.value.productName = findNodeById(productOptions.value, currentProductId.value);
-
+
// 骞惰鍔犺浇瑙勬牸鍨嬪彿鍜屾寚鏍囬�夐」
const params = {
productId: currentProductId.value,
inspectType: 2
};
-
+
Promise.all([
modelList({ id: currentProductId.value }),
qualityInspectDetailByProductId(params)
@@ -260,15 +304,15 @@
form.value.unit = selectedModel.unit || '';
}
}
-
+
// 璁剧疆鎸囨爣閫夐」
testStandardOptions.value = testStandardRes.data || [];
-
+
// 璁剧疆 testStandardId 骞跺姞杞藉弬鏁板垪琛�
nextTick(() => {
if (savedTestStandardId) {
// 纭繚绫诲瀷鍖归厤锛坕tem.id 鍙兘鏄暟瀛楁垨瀛楃涓诧級
- const matchedOption = testStandardOptions.value.find(item =>
+ const matchedOption = testStandardOptions.value.find(item =>
item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
);
if (matchedOption) {
@@ -313,6 +357,28 @@
form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || '';
}
+const handleQualifiedQuantityChange = (value) => {
+ if (value === null || value === undefined) {
+ form.value.qualifiedQuantity = 0;
+ return;
+ }
+ const quantity = parseFloat(form.value.quantity) || 0;
+ const qualified = parseFloat(value) || 0;
+ form.value.qualifiedQuantity = qualified > quantity?quantity:qualified;
+ form.value.unqualifiedQuantity = Math.max(0, quantity - qualified);
+};
+
+const handleUnqualifiedQuantityChange = (value) => {
+ if (value === null || value === undefined) {
+ form.value.unqualifiedQuantity = 0;
+ return;
+ }
+ const quantity = parseFloat(form.value.quantity) || 0;
+ const unqualified = parseFloat(value) || 0;
+ form.value.unqualifiedQuantity = unqualified > quantity?quantity:unqualified;
+ form.value.qualifiedQuantity = Math.max(0, quantity - unqualified);
+};
+
const findNodeById = (nodes, productId) => {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].value === productId) {
@@ -337,7 +403,7 @@
if (children && children.length > 0) {
newItem.children = convertIdToValue(children);
}
-
+
return newItem;
});
}
@@ -345,26 +411,26 @@
const submitForm = () => {
proxy.$refs.formRef.validate(valid => {
if (valid) {
- form.value.inspectType = 2
- if (operationType.value === "add") {
- tableData.value.forEach((item) => {
- delete item.id
- })
- }
- const data = {...form.value, qualityInspectParams: tableData.value}
+ form.value.inspectType = 2;
+ if (operationType.value === "add") {
+ tableData.value.forEach((item) => {
+ delete item.id;
+ });
+ }
+ const data = { ...form.value, qualityInspectParams: tableData.value };
if (operationType.value === "add") {
qualityInspectAdd(data).then(res => {
proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
closeDia();
- })
+ });
} else {
qualityInspectUpdate(data).then(res => {
proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
closeDia();
- })
+ });
}
}
- })
+ });
}
const getList = () => {
if (!currentProductId.value) {
@@ -375,15 +441,15 @@
let params = {
productId: currentProductId.value,
inspectType: 2
- }
- qualityInspectDetailByProductId(params).then(res => {
- // 淇濆瓨涓嬫媺妗嗛�夐」鏁版嵁
- testStandardOptions.value = res.data || [];
- // 娓呯┖琛ㄦ牸鏁版嵁锛岀瓑寰呯敤鎴烽�夋嫨鎸囨爣
- tableData.value = [];
- // 娓呯┖鎸囨爣閫夋嫨
- form.value.testStandardId = '';
- })
+ };
+ qualityInspectDetailByProductId(params).then(res => {
+ // 淇濆瓨涓嬫媺妗嗛�夐」鏁版嵁
+ testStandardOptions.value = res.data || [];
+ // 娓呯┖琛ㄦ牸鏁版嵁锛岀瓑寰呯敤鎴烽�夋嫨鎸囨爣
+ tableData.value = [];
+ // 娓呯┖鎸囨爣閫夋嫨
+ form.value.testStandardId = '';
+ });
}
// 鎸囨爣閫夋嫨鍙樺寲澶勭悊
@@ -395,17 +461,21 @@
tableLoading.value = true;
getQualityTestStandardParamByTestStandardId(testStandardId).then(res => {
tableData.value = res.data || [];
+ tableData.value = tableData.value.map(item => ({
+ ...item,
+ id: null
+ }));
}).catch(error => {
console.error('鑾峰彇鏍囧噯鍙傛暟澶辫触:', error);
tableData.value = [];
}).finally(() => {
tableLoading.value = false;
- })
+ });
}
const getQualityInspectParamList = (id) => {
- qualityInspectParamInfo(id).then(res => {
- tableData.value = res.data;
- })
+ qualityInspectParamInfo(id).then(res => {
+ tableData.value = res.data;
+ });
}
// 鍏抽棴寮规
const closeDia = () => {
@@ -414,8 +484,8 @@
testStandardOptions.value = [];
form.value.testStandardId = '';
dialogFormVisible.value = false;
- emit('close')
-};
+ emit('close');
+}
defineExpose({
openDialog,
});
@@ -423,4 +493,4 @@
<style scoped>
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/qualityManagement/finalInspection/index.vue b/src/views/qualityManagement/finalInspection/index.vue
index a2d1acc..bdb71c4 100644
--- a/src/views/qualityManagement/finalInspection/index.vue
+++ b/src/views/qualityManagement/finalInspection/index.vue
@@ -123,8 +123,18 @@
prop: "unit",
},
{
- label: "鏁伴噺",
+ label: "鎬绘暟閲�",
prop: "quantity",
+ width: 100
+ },
+ {
+ label: "鍚堟牸鏁伴噺",
+ prop: "qualifiedQuantity",
+ width: 100
+ },
+ {
+ label: "涓嶅悎鏍兼暟閲�",
+ prop: "unqualifiedQuantity",
width: 100
},
{
@@ -142,7 +152,7 @@
} else if (params == '鍚堟牸') {
return "success";
} else {
- return null;
+ return 'danger';
}
},
},
@@ -181,6 +191,13 @@
}
},
{
+ name: "鏌ョ湅",
+ type: "text",
+ clickFun: (row) => {
+ openForm("view", row);
+ },
+ },
+ {
name: "闄勪欢",
type: "text",
clickFun: (row) => {
diff --git a/src/views/qualityManagement/nonconformingManagement/components/formDia.vue b/src/views/qualityManagement/nonconformingManagement/components/formDia.vue
index 0c6562c..e747d04 100644
--- a/src/views/qualityManagement/nonconformingManagement/components/formDia.vue
+++ b/src/views/qualityManagement/nonconformingManagement/components/formDia.vue
@@ -35,10 +35,10 @@
</el-col>
<el-col :span="12">
<el-form-item label="瑙勬牸鍨嬪彿锛�" prop="model">
- <el-select v-model="form.model" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'edit'"
- filterable readonly @change="handleChangeModel">
- <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
- </el-select>
+ <el-select v-model="form.productModelId" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'edit'"
+ filterable readonly @change="handleChangeModel">
+ <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
+ </el-select>
</el-form-item>
</el-col>
</el-row>
@@ -149,7 +149,7 @@
productId: "",
model: "",
unit: "",
- quantity: "",
+ quantity: undefined,
checkCompany: "",
checkResult: "",
inspectType: '',
@@ -157,6 +157,7 @@
dealResult: '',
dealName: '',
dealTime: '',
+ productModelId: undefined,
},
rules: {
checkTime: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" },],
@@ -199,8 +200,9 @@
productId: '',
model: '',
unit: '',
- quantity: '',
+ quantity: undefined,
productName: '',
+ productModelId: undefined,
};
} else {
form.value = {};
@@ -223,6 +225,12 @@
modelList({ id: value }).then((res) => {
modelOptions.value = res;
})
+};
+const handleChangeModel = (value) => {
+ const selectedModel = modelOptions.value.find(item => item.id === value);
+ if (selectedModel) {
+ form.value.model = selectedModel.model;
+ }
};
const findNodeById = (nodes, productId) => {
for (let i = 0; i < nodes.length; i++) {
@@ -285,4 +293,4 @@
<style scoped>
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/qualityManagement/nonconformingManagement/index.vue b/src/views/qualityManagement/nonconformingManagement/index.vue
index 6306397..fc9d5d2 100644
--- a/src/views/qualityManagement/nonconformingManagement/index.vue
+++ b/src/views/qualityManagement/nonconformingManagement/index.vue
@@ -98,7 +98,7 @@
} else if (params == '鍚堟牸') {
return "success";
} else {
- return null;
+ return 'danger';
}
},
},
diff --git a/src/views/qualityManagement/processInspection/components/formDia.vue b/src/views/qualityManagement/processInspection/components/formDia.vue
index 88c1e20..635360f 100644
--- a/src/views/qualityManagement/processInspection/components/formDia.vue
+++ b/src/views/qualityManagement/processInspection/components/formDia.vue
@@ -1,7 +1,7 @@
<template>
<div>
<el-dialog v-model="dialogFormVisible"
- :title="operationType === 'add' ? '鏂板杩囩▼妫�楠�' : '缂栬緫杩囩▼妫�楠�'"
+ :title="operationType === 'add' ? '鏂板杩囩▼妫�楠�' : operationType === 'view' ? '鏌ョ湅杩囩▼妫�楠�' : '缂栬緫杩囩▼妫�楠�'"
width="70%"
@close="closeDia">
<el-form :model="form"
@@ -16,7 +16,7 @@
<el-select v-model="form.process"
placeholder="璇烽�夋嫨宸ュ簭"
clearable
- :disabled="processQuantityDisabled"
+ :disabled="isViewMode || processQuantityDisabled"
style="width: 100%">
<el-option v-for="item in processList"
:key="item.name"
@@ -35,7 +35,7 @@
@change="getModels"
:data="productOptions"
:render-after-expand="false"
- :disabled="operationType === 'edit'"
+ :disabled="isViewMode || operationType === 'edit'"
style="width: 100%" />
</el-form-item>
</el-col>
@@ -47,7 +47,7 @@
<el-select v-model="form.productModelId"
placeholder="璇烽�夋嫨"
clearable
- :disabled="operationType === 'edit'"
+ :disabled="isViewMode || operationType === 'edit'"
filterable
readonly
@change="handleChangeModel">
@@ -65,7 +65,8 @@
placeholder="璇烽�夋嫨鎸囨爣"
clearable
@change="handleTestStandardChange"
- style="width: 100%">
+ style="width: 100%"
+ :disabled="isViewMode">
<el-option v-for="item in testStandardOptions"
:key="item.id"
:label="item.standardName || item.standardNo"
@@ -93,7 +94,37 @@
placeholder="璇疯緭鍏�"
clearable
:precision="2"
- :disabled="processQuantityDisabled" />
+ :disabled="isViewMode || processQuantityDisabled" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚堟牸鏁伴噺锛�"
+ prop="qualifiedQuantity">
+ <el-input-number :step="0.01"
+ :min="0"
+ style="width: 100%"
+ v-model="form.qualifiedQuantity"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :precision="2"
+ @change="handleQualifiedQuantityChange"
+ :disabled="isViewMode" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="涓嶅悎鏍兼暟閲忥細"
+ prop="unqualifiedQuantity">
+ <el-input-number :step="0.01"
+ :min="0"
+ style="width: 100%"
+ v-model="form.unqualifiedQuantity"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :precision="2"
+ @change="handleUnqualifiedQuantityChange"
+ :disabled="isViewMode" />
</el-form-item>
</el-col>
</el-row>
@@ -103,17 +134,20 @@
prop="checkCompany">
<el-input v-model="form.checkCompany"
placeholder="璇疯緭鍏�"
- clearable />
+ clearable
+ :disabled="isViewMode" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="妫�娴嬬粨鏋滐細"
prop="checkResult">
- <el-select v-model="form.checkResult">
+ <el-select v-model="form.checkResult" :disabled="isViewMode">
<el-option label="鍚堟牸"
value="鍚堟牸" />
<el-option label="涓嶅悎鏍�"
value="涓嶅悎鏍�" />
+ <el-option label="閮ㄥ垎鍚堟牸"
+ value="閮ㄥ垎鍚堟牸" />
</el-select>
</el-form-item>
</el-col>
@@ -124,7 +158,8 @@
prop="checkName">
<el-select v-model="form.checkName"
placeholder="璇烽�夋嫨"
- clearable>
+ clearable
+ :disabled="isViewMode">
<el-option v-for="item in userList"
:key="item.nickName"
:label="item.nickName"
@@ -141,7 +176,8 @@
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
clearable
- style="width: 100%" />
+ style="width: 100%"
+ :disabled="isViewMode" />
</el-form-item>
</el-col>
</el-row>
@@ -153,14 +189,18 @@
height="400">
<template #slot="{ row }">
<el-input v-model="row.testValue"
- clearable />
+ clearable
+ :disabled="isViewMode" />
</template>
</PIMTable>
<template #footer>
<div class="dialog-footer">
- <el-button type="primary"
- @click="submitForm">纭</el-button>
- <el-button @click="closeDia">鍙栨秷</el-button>
+ <template v-if="!isViewMode">
+ <el-button type="primary"
+ @click="submitForm">纭</el-button>
+ <el-button @click="closeDia">鍙栨秷</el-button>
+ </template>
+ <el-button v-else @click="closeDia">鍏抽棴</el-button>
</div>
</template>
</el-dialog>
@@ -189,6 +229,7 @@
import { userListNoPage } from "@/api/system/user.js";
import { qualityInspectParamInfo } from "@/api/qualityManagement/qualityInspectParam.js";
import { list } from "@/api/productionManagement/productionProcess";
+ import qualified from "@/views/inventoryManagement/stockManagement/Qualified.vue";
const { proxy } = getCurrentInstance();
const emit = defineEmits(["close"]);
@@ -206,6 +247,8 @@
testStandardId: "",
unit: "",
quantity: "",
+ qualifiedQuantity: "",
+ unqualifiedQuantity: "",
checkCompany: "",
checkResult: "",
},
@@ -215,17 +258,19 @@
checkName: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
productId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- testStandardId: [
- { required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change" },
- ],
+ testStandardId: [{ required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change" }],
unit: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ qualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ unqualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
checkCompany: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
checkResult: [{ required: true, message: "璇疯緭鍏�", trigger: "change" }],
},
});
const userList = ref([]);
const { form, rules } = toRefs(data);
+ // 鏄惁涓烘煡鐪嬫ā寮�
+ const isViewMode = computed(() => operationType.value === 'view');
// 缂栬緫鏃讹細productMainId 鎴� purchaseLedgerId 浠讳竴鏈夊�煎垯宸ュ簭銆佹暟閲忕疆鐏�
const processQuantityDisabled = computed(() => {
const v = form.value || {};
@@ -299,7 +344,7 @@
tableData.value = [];
// 鍏堢‘淇濅骇鍝佹爲宸插姞杞斤紝鍚﹀垯缂栬緫鏃朵骇鍝�/瑙勬牸鍨嬪彿鏃犳硶鍙嶆樉
await getProductOptions();
- if (operationType.value === "edit") {
+ if (operationType.value === "edit" || operationType.value === "view") {
// 鍏堜繚瀛� testStandardId锛岄伩鍏嶈娓呯┖
const savedTestStandardId = row.testStandardId;
// 鍏堣缃〃鍗曟暟鎹紝浣嗘殏鏃舵竻绌� testStandardId锛岀瓑閫夐」鍔犺浇瀹屾垚鍚庡啀璁剧疆
@@ -400,6 +445,28 @@
modelOptions.value.find(item => item.id == value)?.unit || "";
};
+ const handleQualifiedQuantityChange = (value) => {
+ if (value === null || value === undefined) {
+ form.value.qualifiedQuantity = 0;
+ return;
+ }
+ const quantity = parseFloat(form.value.quantity) || 0;
+ const qualified = parseFloat(value) || 0;
+ form.value.qualifiedQuantity = qualified > quantity?quantity:qualified;
+ form.value.unqualifiedQuantity = Math.max(0, quantity - qualified);
+ };
+
+ const handleUnqualifiedQuantityChange = (value) => {
+ if (value === null || value === undefined) {
+ form.value.unqualifiedQuantity = 0;
+ return;
+ }
+ const quantity = parseFloat(form.value.quantity) || 0;
+ const unqualified = parseFloat(value) || 0;
+ form.value.unqualifiedQuantity = unqualified > quantity?quantity:unqualified;
+ form.value.qualifiedQuantity = Math.max(0, quantity - unqualified);
+ };
+
const findNodeById = (nodes, productId) => {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].value === productId) {
@@ -440,6 +507,17 @@
delete item.id;
});
}
+ // 纭繚鏁伴噺涓嶄负null
+ const quantity = parseFloat(form.value.quantity) || 0;
+ const qualified = parseFloat(form.value.qualifiedQuantity) || 0;
+ const unqualified = parseFloat(form.value.unqualifiedQuantity) || 0;
+
+ // 楠岃瘉鏁伴噺鍏崇郴
+ if (qualified + unqualified !== quantity) {
+ proxy.$modal.msgError("鍚堟牸鏁伴噺涓庝笉鍚堟牸鏁伴噺涔嬪拰蹇呴』绛変簬鎬绘暟閲�");
+ return;
+ }
+
const data = {
...form.value,
process: processName, // 淇濈暀 process 瀛楁浠ュ吋瀹瑰悗绔�
@@ -491,6 +569,10 @@
getQualityTestStandardParamByTestStandardId(testStandardId)
.then(res => {
tableData.value = res.data || [];
+ tableData.value = tableData.value.map(item => ({
+ ...item,
+ id: null
+ }));
})
.catch(error => {
console.error("鑾峰彇鏍囧噯鍙傛暟澶辫触:", error);
@@ -520,4 +602,4 @@
</script>
<style scoped>
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/qualityManagement/processInspection/index.vue b/src/views/qualityManagement/processInspection/index.vue
index e5504b6..178e81a 100644
--- a/src/views/qualityManagement/processInspection/index.vue
+++ b/src/views/qualityManagement/processInspection/index.vue
@@ -122,8 +122,18 @@
prop: "unit",
},
{
- label: "鏁伴噺",
+ label: "鎬绘暟閲�",
prop: "quantity",
+ width: 100
+ },
+ {
+ label: "鍚堟牸鏁伴噺",
+ prop: "qualifiedQuantity",
+ width: 100
+ },
+ {
+ label: "涓嶅悎鏍兼暟閲�",
+ prop: "unqualifiedQuantity",
width: 100
},
{
@@ -141,7 +151,7 @@
} else if (params == '鍚堟牸') {
return "success";
} else {
- return null;
+ return 'danger';
}
},
},
@@ -178,6 +188,13 @@
}
return false;
}
+ },
+ {
+ name: "鏌ョ湅",
+ type: "text",
+ clickFun: (row) => {
+ openForm("view", row);
+ },
},
{
name: "闄勪欢",
@@ -363,13 +380,13 @@
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const downloadUrl = window.URL.createObjectURL(blob)
-
+
const link = document.createElement('a')
link.href = downloadUrl
link.download = '杩囩▼妫�楠屾姤鍛�.docx'
document.body.appendChild(link)
link.click()
-
+
document.body.removeChild(link)
window.URL.revokeObjectURL(downloadUrl)
})
diff --git a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
index 7e373bf..8bcc72b 100644
--- a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -2,7 +2,7 @@
<div>
<el-dialog
v-model="dialogFormVisible"
- :title="operationType === 'add' ? '鏂板鍘熸潗鏂欐楠�' : '缂栬緫鍘熸潗鏂欐楠�'"
+ :title="operationType === 'add' ? '鏂板鍘熸潗鏂欐楠�' : operationType === 'view' ? '鏌ョ湅鍘熸潗鏂欐楠�' : '缂栬緫鍘熸潗鏂欐楠�'"
width="70%"
@close="closeDia"
>
@@ -14,7 +14,7 @@
v-model="form.supplier"
placeholder="璇烽�夋嫨"
clearable
- :disabled="supplierQuantityDisabled"
+ :disabled="isViewMode || supplierQuantityDisabled"
>
<el-option
v-for="item in supplierList"
@@ -35,7 +35,7 @@
@change="getModels"
:data="productOptions"
:render-after-expand="false"
- :disabled="operationType === 'edit'"
+ :disabled="isViewMode || operationType === 'edit'"
style="width: 100%"
/>
</el-form-item>
@@ -44,7 +44,7 @@
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
- <el-select v-model="form.productModelId" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'edit'"
+ <el-select v-model="form.productModelId" placeholder="璇烽�夋嫨" clearable :disabled="isViewMode || operationType === 'edit'"
filterable readonly @change="handleChangeModel">
<el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
</el-select>
@@ -58,6 +58,7 @@
clearable
@change="handleTestStandardChange"
style="width: 100%"
+ :disabled="isViewMode"
>
<el-option
v-for="item in testStandardOptions"
@@ -78,21 +79,39 @@
<el-col :span="12">
<el-form-item label="鏁伴噺锛�" prop="quantity">
<el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="璇疯緭鍏�"
- clearable :precision="2" :disabled="supplierQuantityDisabled"/>
+ clearable :precision="2" :disabled="isViewMode || supplierQuantityDisabled"/>
</el-form-item>
</el-col>
</el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鍚堟牸鏁伴噺锛�" prop="qualifiedQuantity">
+ <el-input-number :step="0.01" :min="0" :max="form.quantity || 0" style="width: 100%"
+ v-model="form.qualifiedQuantity" placeholder="璇疯緭鍏�" :precision="2"
+ @change="onQualifiedChange" :disabled="isViewMode"/>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="涓嶅悎鏍兼暟閲忥細" prop="unqualifiedQuantity">
+ <el-input-number :step="0.01" :min="0" :max="form.quantity || 0" style="width: 100%"
+ v-model="form.unqualifiedQuantity" placeholder="璇疯緭鍏�" :precision="2"
+ @change="onUnqualifiedChange" :disabled="isViewMode"/>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="妫�娴嬪崟浣嶏細" prop="checkCompany">
- <el-input v-model="form.checkCompany" placeholder="璇疯緭鍏�" clearable/>
+ <el-input v-model="form.checkCompany" placeholder="璇疯緭鍏�" clearable :disabled="isViewMode"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="妫�娴嬬粨鏋滐細" prop="checkResult">
- <el-select v-model="form.checkResult">
+ <el-select v-model="form.checkResult" :disabled="isViewMode">
<el-option label="鍚堟牸" value="鍚堟牸"/>
<el-option label="涓嶅悎鏍�" value="涓嶅悎鏍�"/>
+ <el-option label="閮ㄥ垎鍚堟牸" value="閮ㄥ垎鍚堟牸"/>
</el-select>
</el-form-item>
</el-col>
@@ -100,7 +119,7 @@
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="妫�楠屽憳锛�" prop="checkName">
- <el-select v-model="form.checkName" placeholder="璇烽�夋嫨" clearable style="width: 100%">
+ <el-select v-model="form.checkName" placeholder="璇烽�夋嫨" clearable style="width: 100%" :disabled="isViewMode">
<el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
</el-select>
</el-form-item>
@@ -115,6 +134,7 @@
format="YYYY-MM-DD"
clearable
style="width: 100%"
+ :disabled="isViewMode"
/>
</el-form-item>
</el-col>
@@ -131,13 +151,16 @@
height="400"
>
<template #slot="{ row }">
- <el-input v-model="row.testValue" clearable/>
+ <el-input v-model="row.testValue" clearable :disabled="isViewMode"/>
</template>
</PIMTable>
<template #footer>
<div class="dialog-footer">
- <el-button type="primary" @click="submitForm">纭</el-button>
- <el-button @click="closeDia">鍙栨秷</el-button>
+ <template v-if="!isViewMode">
+ <el-button type="primary" @click="submitForm">纭</el-button>
+ <el-button @click="closeDia">鍙栨秷</el-button>
+ </template>
+ <el-button v-else @click="closeDia">鍏抽棴</el-button>
</div>
</template>
</el-dialog>
@@ -182,6 +205,8 @@
testStandardId: [{required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change"}],
unit: [{required: false, message: "璇疯緭鍏�", trigger: "blur"}],
quantity: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+ qualifiedQuantity: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+ unqualifiedQuantity: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
checkCompany: [{required: false, message: "璇疯緭鍏�", trigger: "blur"}],
checkResult: [{required: true, message: "璇烽�夋嫨妫�娴嬬粨鏋�", trigger: "change"}],
},
@@ -220,6 +245,9 @@
const testStandardOptions = ref([]); // 鎸囨爣閫夋嫨涓嬫媺妗嗘暟鎹�
const modelOptions = ref([]);
const userList = ref([]); // 妫�楠屽憳涓嬫媺鍒楄〃
+
+// 鏄惁涓烘煡鐪嬫ā寮�
+const isViewMode = computed(() => operationType.value === 'view');
// 缂栬緫鏃讹細productMainId 鎴� purchaseLedgerId 浠讳竴鏈夊�煎垯渚涘簲鍟嗐�佹暟閲忕疆鐏�
const supplierQuantityDisabled = computed(() => {
@@ -260,7 +288,7 @@
tableData.value = [];
// 鍏堢‘淇濅骇鍝佹爲宸插姞杞斤紝鍚﹀垯缂栬緫鏃朵骇鍝�/瑙勬牸鍨嬪彿鏃犳硶鍙嶆樉
await getProductOptions();
- if (operationType.value === 'edit') {
+ if (operationType.value === 'edit' || operationType.value === 'view') {
// 鍏堜繚瀛� testStandardId锛岄伩鍏嶈娓呯┖
const savedTestStandardId = row.testStandardId;
form.value = {...row}
@@ -294,7 +322,7 @@
// 濡傛灉缂栬緫鏁版嵁涓湁 testStandardId锛屽垯璁剧疆骞跺姞杞藉搴旂殑鍙傛暟
if (savedTestStandardId) {
// 纭繚绫诲瀷鍖归厤锛坕tem.id 鍙兘鏄暟瀛楁垨瀛楃涓诧級
- const matchedOption = testStandardOptions.value.find(item =>
+ const matchedOption = testStandardOptions.value.find(item =>
item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
);
if (matchedOption) {
@@ -435,6 +463,10 @@
tableLoading.value = true;
getQualityTestStandardParamByTestStandardId(testStandardId).then(res => {
tableData.value = res.data || [];
+ tableData.value = tableData.value.map(item => ({
+ ...item,
+ id: null
+ }));
}).catch(error => {
console.error('鑾峰彇鏍囧噯鍙傛暟澶辫触:', error);
tableData.value = [];
@@ -448,6 +480,32 @@
tableData.value = res.data;
})
}
+// 鑷姩璁$畻鍚堟牸鏁伴噺鍙樺寲鏃剁殑涓嶅悎鏍兼暟閲�
+const onQualifiedChange = (value) => {
+ if (form.value.quantity !== undefined && form.value.quantity !== null) {
+ const maxUnqualified = form.value.quantity - value;
+ if (maxUnqualified >= 0) {
+ form.value.unqualifiedQuantity = maxUnqualified;
+ } else {
+ form.value.qualifiedQuantity = form.value.quantity;
+ form.value.unqualifiedQuantity = 0;
+ }
+ }
+};
+
+// 鑷姩璁$畻涓嶅悎鏍兼暟閲忓彉鍖栨椂鐨勫悎鏍兼暟閲�
+const onUnqualifiedChange = (value) => {
+ if (form.value.quantity !== undefined && form.value.quantity !== null) {
+ const maxQualified = form.value.quantity - value;
+ if (maxQualified >= 0) {
+ form.value.qualifiedQuantity = maxQualified;
+ } else {
+ form.value.unqualifiedQuantity = form.value.quantity;
+ form.value.qualifiedQuantity = 0;
+ }
+ }
+};
+
// 鍏抽棴寮规
const closeDia = () => {
proxy.resetForm("formRef");
@@ -464,4 +522,4 @@
<style scoped>
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/qualityManagement/rawMaterialInspection/index.vue b/src/views/qualityManagement/rawMaterialInspection/index.vue
index cc2c151..b6adb40 100644
--- a/src/views/qualityManagement/rawMaterialInspection/index.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -124,8 +124,23 @@
prop: "unit",
},
{
- label: "鏁伴噺",
+ label: "鎬绘暟閲�",
prop: "quantity",
+ width: 100
+ },
+ {
+ label: "鍚堟牸鏁伴噺",
+ prop: "qualifiedQuantity",
+ width: 100
+ },
+ {
+ label: "涓嶅悎鏍兼暟閲�",
+ prop: "unqualifiedQuantity",
+ width: 100
+ },
+ {
+ label: "妫�娴嬪崟浣�",
+ prop: "checkCompany",
width: 120
},
{
@@ -143,7 +158,7 @@
} else if (params === '鍚堟牸') {
return "success";
} else {
- return null;
+ return 'danger';
}
},
},
@@ -182,6 +197,13 @@
}
},
{
+ name: "鏌ョ湅",
+ type: "text",
+ clickFun: (row) => {
+ openForm("view", row);
+ },
+ },
+ {
name: "闄勪欢",
type: "text",
clickFun: (row) => {
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
index ccd7504..f4e49b6 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
@@ -23,7 +23,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import * as echarts from 'echarts'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
@@ -151,6 +151,13 @@
fetchData()
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
index 0f3ec84..6e39eb1 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
@@ -27,7 +27,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import { productTurnoverDays } from '@/api/viewIndex.js'
@@ -82,6 +82,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
index 0937b32..055fb66 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
@@ -24,7 +24,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import { salesPurchaseStorageProductCount } from '@/api/viewIndex.js'
const statItems = ref([])
@@ -52,6 +52,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
index 669c826..65a72fe 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
@@ -22,7 +22,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed, inject, watch } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
@@ -205,6 +205,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
initBackground()
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
index 8fcaa42..f5dac31 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
@@ -21,7 +21,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed, inject, watch } from 'vue'
import { productSalesAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
@@ -175,6 +175,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
initBackground()
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/index.vue b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
index 065b59d..f81e482 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/index.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
@@ -43,7 +43,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick, provide } from 'vue'
import autofit from 'autofit.js'
import LeftBottom from './components/left-bottom.vue'
import CenterCenter from './components/center-center.vue'
@@ -65,6 +65,12 @@
// 鐢ㄦ埛store
const userStore = useUserStore()
+
+/** 涓� dataDashboard 鍏辩敤娉ㄥ叆鍚嶏紝瀛愮粍浠讹紙鍚鐢ㄧ殑 right-top/right-bottom锛夋瘡鍒嗛挓鍒锋柊 */
+const DASHBOARD_REFRESH_MS = 60 * 1000
+const dataDashboardRefreshTick = ref(0)
+provide('dataDashboardRefreshTick', dataDashboardRefreshTick)
+let dashboardPollTimer = null
// 璁$畻缂╂斁姣斾緥
const calculateScale = () => {
@@ -140,9 +146,17 @@
window.addEventListener('fullscreenchange', handleFullscreenChange)
window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
window.addEventListener('MSFullscreenChange', handleFullscreenChange)
+
+ dashboardPollTimer = setInterval(() => {
+ dataDashboardRefreshTick.value++
+ }, DASHBOARD_REFRESH_MS)
})
onBeforeUnmount(() => {
+ if (dashboardPollTimer) {
+ clearInterval(dashboardPollTimer)
+ dashboardPollTimer = null
+ }
window.removeEventListener('resize', handleResize)
window.removeEventListener('fullscreenchange', handleFullscreenChange)
window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue
index a4824a2..c28c2fa 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue
@@ -20,7 +20,7 @@
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, computed, inject, watch } from 'vue'
import { deptStaffDistribution } from '@/api/viewIndex.js'
import PanelHeader from '../PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
@@ -148,6 +148,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ getDeptStaffDistribution()
+ })
+}
+
onMounted(() => {
getDeptStaffDistribution()
})
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
index 950038e..a7d0174 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
@@ -110,7 +110,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick, inject, watch } from 'vue'
import { homeTodos, summaryStatistics } from '@/api/viewIndex.js'
import { getLedgerPage } from '@/api/equipmentManagement/ledger.js'
import { getRepairPage } from '@/api/equipmentManagement/repair.js'
@@ -175,8 +175,23 @@
})
}
+const destroyTodoListScroll = () => {
+ const todoListEl = refTodoList.value
+ if (todoListEl) {
+ if (todoListEl._animationFrame) {
+ cancelAnimationFrame(todoListEl._animationFrame)
+ todoListEl._animationFrame = null
+ }
+ if (todoListEl._pauseTimer) {
+ clearInterval(todoListEl._pauseTimer)
+ todoListEl._pauseTimer = null
+ }
+ }
+}
+
// 鍒濆鍖栧緟鍔炰簨椤瑰垪琛ㄦ粴鍔ㄥ姛鑳�
const initTodoListScroll = () => {
+ destroyTodoListScroll()
const todoListEl = refTodoList.value
// 寮哄埗鍚敤婊氬姩锛屼笉妫�鏌ヤ换浣曟潯浠�
if (todoListEl) {
@@ -259,6 +274,7 @@
// 寰呭姙浜嬮」
const todoInfoS = () => {
+ destroyTodoListScroll()
homeTodos().then((res) => {
todoList.value = res.data
// 鍦ㄨ幏鍙栧埌寰呭姙浜嬮」鏁版嵁鍚庯紝鍒濆鍖栨粴鍔ㄥ姛鑳�
@@ -268,25 +284,25 @@
})
}
-onMounted(() => {
+const refreshCenterTopData = () => {
getNum()
getLedgerNum()
todoInfoS()
+}
+
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ refreshCenterTopData()
+ })
+}
+
+onMounted(() => {
+ refreshCenterTopData()
})
onBeforeUnmount(() => {
- // 娓呯悊寰呭姙浜嬮」鍒楄〃鐨勫姩鐢诲拰瀹氭椂鍣�
- const todoListEl = refTodoList.value
- if (todoListEl) {
- if (todoListEl._animationFrame) {
- cancelAnimationFrame(todoListEl._animationFrame)
- todoListEl._animationFrame = null
- }
- if (todoListEl._pauseTimer) {
- clearInterval(todoListEl._pauseTimer)
- todoListEl._pauseTimer = null
- }
- }
+ destroyTodoListScroll()
})
</script>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
index 58c83d8..bab6024 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
@@ -40,7 +40,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from '../PanelHeader.vue'
import DateTypeSwitch from '../DateTypeSwitch.vue'
@@ -192,6 +192,13 @@
getCustomerRevenueAnalysis()
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ getCustomerRevenueAnalysis()
+ })
+}
+
onMounted(() => {
fetchCustomerOptions()
})
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
index 5b7e29e..16791de 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
@@ -21,7 +21,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount } from 'vue'
+import { ref, onMounted, onBeforeUnmount, inject, watch } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from '../PanelHeader.vue'
import { productCategoryDistribution } from '@/api/viewIndex.js'
@@ -207,6 +207,13 @@
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ loadData()
+ })
+}
+
onMounted(() => {
loadData()
initBackground()
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
index bbcd5f0..6fcd80f 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
@@ -22,7 +22,7 @@
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, computed, inject, watch } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from '../PanelHeader.vue'
import DateTypeSwitch from '../DateTypeSwitch.vue'
@@ -308,6 +308,13 @@
fetchCustomerRanking()
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchCustomerRanking()
+ })
+}
+
onMounted(() => {
fetchCustomerRanking()
})
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
index 21696fa..96e4548 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
@@ -21,7 +21,7 @@
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, computed, inject, watch } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from '../PanelHeader.vue'
import DateTypeSwitch from '../DateTypeSwitch.vue'
@@ -331,6 +331,13 @@
fetchSupplierRanking()
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchSupplierRanking()
+ })
+}
+
onMounted(() => {
fetchSupplierRanking()
})
diff --git a/src/views/reportAnalysis/dataDashboard/index.vue b/src/views/reportAnalysis/dataDashboard/index.vue
index 67a700f..ff53a7b 100644
--- a/src/views/reportAnalysis/dataDashboard/index.vue
+++ b/src/views/reportAnalysis/dataDashboard/index.vue
@@ -44,7 +44,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick, provide } from 'vue'
import autofit from 'autofit.js'
import LeftTop from './components/basic/left-top.vue'
import LeftBottom from './components/basic/left-bottom.vue'
@@ -65,6 +65,12 @@
// 鐢ㄦ埛store
const userStore = useUserStore()
+
+// 澶у睆鎺ュ彛杞闂撮殧
+const DASHBOARD_REFRESH_MS = 60 * 1000
+const dataDashboardRefreshTick = ref(0)
+provide('dataDashboardRefreshTick', dataDashboardRefreshTick)
+let dashboardPollTimer = null
// 璁$畻缂╂斁姣斾緥
const calculateScale = () => {
@@ -140,9 +146,17 @@
window.addEventListener('fullscreenchange', handleFullscreenChange)
window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
window.addEventListener('MSFullscreenChange', handleFullscreenChange)
+
+ dashboardPollTimer = setInterval(() => {
+ dataDashboardRefreshTick.value++
+ }, DASHBOARD_REFRESH_MS)
})
onBeforeUnmount(() => {
+ if (dashboardPollTimer) {
+ clearInterval(dashboardPollTimer)
+ dashboardPollTimer = null
+ }
window.removeEventListener('resize', handleResize)
window.removeEventListener('fullscreenchange', handleFullscreenChange)
window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
index 46a870a..f4f4024 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
@@ -51,7 +51,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick, inject, watch } from 'vue'
import { getProgressStatistics } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
@@ -132,17 +132,22 @@
}, 150)
}
-const initProgressTableScroll = () => {
- const tableContainer = progressTableRef.value
- if (!tableContainer) return
+const stopProgressTableScroll = () => {
if (progressTableScrollTimer.value) {
cancelAnimationFrame(progressTableScrollTimer.value)
progressTableScrollTimer.value = null
}
- if (tableContainer._pauseTimer) {
+ const tableContainer = progressTableRef.value
+ if (tableContainer?._pauseTimer) {
clearInterval(tableContainer._pauseTimer)
tableContainer._pauseTimer = null
}
+}
+
+const initProgressTableScroll = () => {
+ const tableContainer = progressTableRef.value
+ if (!tableContainer) return
+ stopProgressTableScroll()
const tbody = tableContainer.querySelector('tbody')
if (!tbody) return
const originalCount = progressTableData.value.length
@@ -198,6 +203,7 @@
const progressStatisticsInfo = () => {
getProgressStatistics()
.then((res) => {
+ stopProgressTableScroll()
if (!res || !res.data) return
const obj = {
totalOrderCount: res.data.totalOrderCount || 0,
@@ -224,14 +230,19 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ progressStatisticsInfo()
+ })
+}
+
onMounted(() => {
progressStatisticsInfo()
})
onBeforeUnmount(() => {
- if (progressTableScrollTimer.value) {
- cancelAnimationFrame(progressTableScrollTimer.value)
- }
+ stopProgressTableScroll()
if (tableScrollTimeout.value) clearTimeout(tableScrollTimeout.value)
const tableContainer = progressTableRef.value
if (tableContainer?._pauseTimer) {
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-center.vue b/src/views/reportAnalysis/productionAnalysis/components/center-center.vue
index 96bcada..a65f4f8 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/center-center.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-center.vue
@@ -27,7 +27,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import * as echarts from 'echarts'
import Echarts from '@/components/Echarts/echarts.vue'
import { inputOutputAnalysis } from '@/api/viewIndex.js'
@@ -146,6 +146,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-top.vue b/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
index 7201828..a806150 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
@@ -24,7 +24,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import { orderCount } from '@/api/viewIndex.js'
const statItems = ref([])
@@ -52,6 +52,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
index 9f6a8c1..b7c7358 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
@@ -22,7 +22,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
@@ -143,6 +143,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ workInProcessTurnoverInfo()
+ })
+}
+
onMounted(() => {
workInProcessTurnoverInfo()
})
diff --git a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
index fd52b1b..37c82f0 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
@@ -23,7 +23,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed, inject, watch } from 'vue'
import { processOutputAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
@@ -170,6 +170,13 @@
fetchData()
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
initBackground()
diff --git a/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
index 28de03b..62356e7 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
@@ -22,7 +22,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import { productionAccountingAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import DateTypeSwitch from './DateTypeSwitch.vue'
@@ -157,6 +157,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/productionAnalysis/components/right-top.vue b/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
index d3a9eb9..5ec836f 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
@@ -22,7 +22,7 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, inject, watch } from 'vue'
import { workOrderEfficiencyAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
@@ -152,6 +152,13 @@
})
}
+const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
+if (dataDashboardRefreshTick) {
+ watch(dataDashboardRefreshTick, () => {
+ fetchData()
+ })
+}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/productionAnalysis/index.vue b/src/views/reportAnalysis/productionAnalysis/index.vue
index e179150..7e03bbd 100644
--- a/src/views/reportAnalysis/productionAnalysis/index.vue
+++ b/src/views/reportAnalysis/productionAnalysis/index.vue
@@ -44,7 +44,7 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick, provide } from 'vue'
import autofit from 'autofit.js'
import LeftBottom from './components/left-bottom.vue'
import CenterCenter from './components/center-center.vue'
@@ -66,6 +66,12 @@
// 鐢ㄦ埛store
const userStore = useUserStore()
+
+/** 涓庡叾瀹冮┚椹惰埍鍏辩敤娉ㄥ叆鍚嶏紝瀛愮粍浠舵瘡鍒嗛挓鍒锋柊鎺ュ彛鏁版嵁 */
+const DASHBOARD_REFRESH_MS = 60 * 1000
+const dataDashboardRefreshTick = ref(0)
+provide('dataDashboardRefreshTick', dataDashboardRefreshTick)
+let dashboardPollTimer = null
// 璁$畻缂╂斁姣斾緥
const calculateScale = () => {
@@ -141,9 +147,17 @@
window.addEventListener('fullscreenchange', handleFullscreenChange)
window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
window.addEventListener('MSFullscreenChange', handleFullscreenChange)
+
+ dashboardPollTimer = setInterval(() => {
+ dataDashboardRefreshTick.value++
+ }, DASHBOARD_REFRESH_MS)
})
onBeforeUnmount(() => {
+ if (dashboardPollTimer) {
+ clearInterval(dashboardPollTimer)
+ dashboardPollTimer = null
+ }
window.removeEventListener('resize', handleResize)
window.removeEventListener('fullscreenchange', handleFullscreenChange)
window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
diff --git a/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue b/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue
index a0146ba..86acdf8 100644
--- a/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue
+++ b/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue
@@ -218,7 +218,7 @@
approveDeptName: "",
approveReason: "",
checkResult: "",
- storageBlobDTOs: [],
+ storageBlobDTOS: [],
approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
startDate: "", // 璇峰亣寮�濮嬫椂闂�
endDate: "", // 璇峰亣缁撴潫鏃堕棿
@@ -298,7 +298,7 @@
approveProcessGetInfo({ id: row.approveId, approveReason: "1" }).then(
res => {
form.value = { ...res.data };
- form.value.storageBlobDTOs = res.data.storageBlobVOS;
+ form.value.storageBlobDTOS = res.data.storageBlobVOS;
// 鍙嶆樉瀹℃壒浜�
if (res.data && res.data.approveUserIds) {
const userIds = res.data.approveUserIds.split(",");
@@ -388,7 +388,7 @@
return;
}
}
- form.value.storageBlobDTOs = fileList.value;
+ form.value.storageBlobDTOS = fileList.value;
proxy.$refs.formRef.validate(valid => {
if (valid) {
if (operationType.value === "add" || currentApproveStatus.value == 3) {
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index e7a73a5..be8a2d7 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -1578,7 +1578,7 @@
selectedQuotation.value = null;
let userLists = await userListNoPage();
userList.value = userLists.data;
- listCustomer({ current: -1, size: -1 }).then(res => {
+ listCustomer({ current: -1, size: -1, type: 0 }).then(res => {
customerOption.value = res.data.records;
});
form.value.entryPerson = userStore.id;
@@ -1705,6 +1705,8 @@
taxExclusiveTotalPrice: taxExclusiveTotalPrice,
invoiceType: "澧炴櫘绁�",
isProduction: true,
+ productId: p.productId,
+ productModelId: p.productModelId
};
});
@@ -2652,7 +2654,7 @@
// 鍙戣揣鐘舵�佸繀椤绘槸"寰呭彂璐�"鎴�"瀹℃牳鎷掔粷"
const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
- return statusStr === "寰呭彂璐�" || statusStr === "瀹℃牳鎷掔粷";
+ return statusStr === "寰呭彂璐�" || statusStr === "瀹℃牳鎷掔粷" || statusStr === "閮ㄥ垎鍙戣揣";
};
// 鎵撳紑闄勪欢寮圭獥
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index 2237b72..fce764f 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -187,9 +187,9 @@
</el-table-column>
<el-table-column prop="specification" label="瑙勬牸鍨嬪彿" width="200">
<template #default="scope">
- <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
+ <el-form-item :prop="`products.${scope.$index}.productModelId`" class="product-table-form-item">
<el-select
- v-model="scope.row.specificationId"
+ v-model="scope.row.productModelId"
placeholder="璇烽�夋嫨"
clearable
@change="getProductModel($event, scope.row)"
@@ -239,10 +239,10 @@
</template>
<div class="form-content">
<el-form-item label="澶囨敞" prop="remark">
- <el-input
- type="textarea"
- v-model="form.remark"
- placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙閫夊~锛�"
+ <el-input
+ type="textarea"
+ v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙閫夊~锛�"
:rows="4"
maxlength="500"
show-word-limit
@@ -270,7 +270,7 @@
<span style="font-size: 18px; color: #e6a23c; font-weight: bold;">楼{{ currentQuotation.totalAmount?.toFixed(2) }}</span>
</el-descriptions-item>
</el-descriptions>
-
+
<div style="margin: 20px 0;">
<h4>浜у搧鏄庣粏</h4>
<el-table :data="currentQuotation.products" border style="width: 100%">
@@ -354,7 +354,7 @@
const productRowRules = {
productId: [{ required: true, message: '璇烽�夋嫨浜у搧鍚嶇О', trigger: 'change' }],
- specificationId: [{ required: true, message: '璇烽�夋嫨瑙勬牸鍨嬪彿', trigger: 'change' }],
+ productModelId: [{ required: true, message: '璇烽�夋嫨瑙勬牸鍨嬪彿', trigger: 'change' }],
unit: [{ required: true, message: '璇峰~鍐欏崟浣�', trigger: 'blur' }],
unitPrice: [{ required: true, message: '璇峰~鍐欏崟浠�', trigger: 'change' }]
}
@@ -362,7 +362,7 @@
const r = { ...baseRules }
;(form.products || []).forEach((_, i) => {
r[`products.${i}.productId`] = productRowRules.productId
- r[`products.${i}.specificationId`] = productRowRules.specificationId
+ r[`products.${i}.productModelId`] = productRowRules.productModelId
r[`products.${i}.unit`] = productRowRules.unit
r[`products.${i}.unitPrice`] = productRowRules.unitPrice
})
@@ -433,7 +433,7 @@
if (children && children.length > 0) {
newItem.children = convertIdToValue(children);
}
-
+
return newItem;
});
}
@@ -457,7 +457,7 @@
row.productId = '';
row.product = '';
row.modelOptions = [];
- row.specificationId = '';
+ row.productModelId = '';
row.specification = '';
row.unit = '';
return;
@@ -478,13 +478,13 @@
if (!row) return;
// 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
if (!value) {
- row.specificationId = '';
+ row.productModelId = '';
row.specification = '';
row.unit = '';
return;
}
- // 鏇存柊 specificationId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
- row.specificationId = value;
+ // 鏇存柊 productModelId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
+ row.productModelId = value;
const modelOptions = row.modelOptions || [];
const index = modelOptions.findIndex((item) => item.id === value);
if (index !== -1) {
@@ -523,7 +523,7 @@
products: row.products ? row.products.map(product => ({
productId: product.productId || '',
product: product.product || product.productName || '',
- specificationId: product.specificationId || '',
+ productModelId: product.productModelId || '',
specification: product.specification || '',
quantity: product.quantity || 0,
unit: product.unit || '',
@@ -560,32 +560,32 @@
const resolvedProductId = product.productId
? Number(product.productId)
: findNodeIdByLabel(productOptions.value, productName) || ''
-
+
// 濡傛灉鏈変骇鍝両D锛屽姞杞藉搴旂殑瑙勬牸鍨嬪彿鍒楄〃
let modelOptions = [];
- let resolvedSpecificationId = product.specificationId || '';
-
+ let resolvedProductModelId = product.productModelId || '';
+
if (resolvedProductId) {
try {
const res = await modelList({ id: resolvedProductId });
modelOptions = res || [];
-
- // 濡傛灉杩斿洖鐨勬暟鎹病鏈� specificationId锛屼絾鏈� specification 鍚嶇О锛屾牴鎹悕绉版煡鎵� ID
- if (!resolvedSpecificationId && product.specification) {
+
+ // 濡傛灉杩斿洖鐨勬暟鎹病鏈� productModelId锛屼絾鏈� specification 鍚嶇О锛屾牴鎹悕绉版煡鎵� ID
+ if (!resolvedProductModelId && product.specification) {
const foundModel = modelOptions.find(item => item.model === product.specification);
if (foundModel) {
- resolvedSpecificationId = foundModel.id;
+ resolvedProductModelId = foundModel.id;
}
}
} catch (error) {
console.error('鍔犺浇瑙勬牸鍨嬪彿澶辫触:', error);
}
}
-
+
return {
productId: resolvedProductId,
product: productName,
- specificationId: resolvedSpecificationId,
+ productModelId: resolvedProductModelId,
specification: product.specification || '',
quantity: product.quantity || 0,
unit: product.unit || '',
@@ -649,8 +649,7 @@
productId: '',
product: '',
productName: '',
- specificationId: '',
- specification: '',
+ productModelId: '',
quantity: 1,
unit: '',
unitPrice: 0,
@@ -755,7 +754,7 @@
products: item.products ? item.products.map(product => ({
productId: product.productId || '',
product: product.product || product.productName || '',
- specificationId: product.specificationId || '',
+ productModelId: product.productModelId || '',
specification: product.specification || '',
quantity: product.quantity || 0,
unit: product.unit || '',
@@ -803,16 +802,16 @@
padding: 10px 0;
max-height: calc(100vh - 200px);
overflow-y: auto;
-
+
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
-
+
&::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
-
+
&:hover {
background: #a8a8a8;
}
@@ -829,17 +828,17 @@
margin-bottom: 24px;
border-radius: 8px;
transition: all 0.3s ease;
-
+
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
}
-
+
:deep(.el-card__header) {
padding: 16px 20px;
background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
border-bottom: 1px solid #ebeef5;
}
-
+
:deep(.el-card__body) {
padding: 20px;
}
@@ -849,19 +848,19 @@
display: flex;
align-items: center;
gap: 8px;
-
+
.card-icon {
font-size: 18px;
color: #409eff;
}
-
+
.card-title {
font-weight: 600;
font-size: 16px;
color: #303133;
flex: 1;
}
-
+
.header-btn {
margin-left: auto;
}
@@ -885,20 +884,20 @@
.product-table {
:deep(.el-table__header) {
background-color: #f5f7fa;
-
+
th {
background-color: #f5f7fa !important;
color: #606266;
font-weight: 600;
}
}
-
+
:deep(.el-table__row) {
&:hover {
background-color: #f5f7fa;
}
}
-
+
:deep(.el-table__cell) {
padding: 12px 0;
}
--
Gitblit v1.9.3