| | |
| | | "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" |
| | | } |
| | |
| | | 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}`); |
| | | } |
| | | } |
| | | |
| | | // ç®åå½ä»¤è¡åæ°è§£æ |
| | | 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", |
| | | }; |
| | | } |
| | |
| | | #app {
|
| | | .main-container {
|
| | | min-height: 100%;
|
| | | transition: margin-left 0.28s;
|
| | | margin-left: $base-sidebar-width;
|
| | | position: relative;
|
| | | background: transparent;
|
| | | }
|
| | |
|
| | | .sidebarHide {
|
| | | margin-left: 0 !important;
|
| | | }
|
| | |
|
| | | .sidebar-container {
|
| | | transition: width 0.28s;
|
| | | width: $base-sidebar-width !important;
|
| | | height: 100%;
|
| | | position: fixed;
|
| | | font-size: 0px;
|
| | | top: 0;
|
| | | bottom: 0;
|
| | | left: 0;
|
| | | z-index: 1001;
|
| | | overflow: hidden;
|
| | | padding: 12px 0 16px 16px;
|
| | | background: transparent;
|
| | | box-shadow: none;
|
| | |
|
| | | // reset element-ui css
|
| | | .horizontal-collapse-transition {
|
| | | transition: 0s width ease-in-out, 0s padding-left ease-in-out,
|
| | | 0s padding-right ease-in-out;
|
| | | }
|
| | |
|
| | | .scrollbar-wrapper {
|
| | | overflow-x: hidden !important;
|
| | | }
|
| | |
|
| | | .el-scrollbar__bar.is-vertical {
|
| | | right: 0px;
|
| | | }
|
| | |
|
| | | .el-scrollbar {
|
| | | height: 100%;
|
| | | }
|
| | |
|
| | | &.has-logo {
|
| | | .el-scrollbar {
|
| | | height: calc(100% - 72px);
|
| | | margin-top: 10px;
|
| | | }
|
| | | }
|
| | |
|
| | | .is-horizontal {
|
| | | display: none;
|
| | | }
|
| | |
|
| | | a {
|
| | | display: inline-block;
|
| | | width: 100%;
|
| | | overflow: hidden;
|
| | | }
|
| | |
|
| | | .svg-icon {
|
| | | margin-right: 16px;
|
| | | }
|
| | |
|
| | | .el-menu { |
| | | border: none; |
| | | height: 100%; |
| | | width: 100% !important; |
| | | padding: 10px 8px 18px; |
| | | border-radius: 22px; |
| | | background: var(--menu-surface); |
| | | backdrop-filter: blur(18px); |
| | | box-shadow: var(--shadow-sm); |
| | | #app { |
| | | .main-container { |
| | | min-height: 100%; |
| | | transition: margin-left 0.28s; |
| | | margin-left: $base-sidebar-width; |
| | | position: relative; |
| | | background: transparent; |
| | | } |
| | | |
| | | .sidebarHide { |
| | | margin-left: 0 !important; |
| | | } |
| | | |
| | | .sidebar-container { |
| | | transition: width 0.28s; |
| | | width: $base-sidebar-width !important; |
| | | height: 100%; |
| | | position: fixed; |
| | | font-size: 0px; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | z-index: 1001; |
| | | overflow: hidden; |
| | | padding: 12px 0 16px 16px; |
| | | background: transparent; |
| | | box-shadow: none; |
| | | |
| | | // reset element-ui css |
| | | .horizontal-collapse-transition { |
| | | transition: 0s width ease-in-out, 0s padding-left ease-in-out, |
| | | 0s padding-right ease-in-out; |
| | | } |
| | |
|
| | | .el-menu-item,
|
| | | .menu-title {
|
| | | overflow: hidden !important;
|
| | | text-overflow: ellipsis !important;
|
| | | white-space: nowrap !important;
|
| | | }
|
| | |
|
| | | .el-menu-item .el-menu-tooltip__trigger {
|
| | | display: inline-block !important;
|
| | | }
|
| | |
|
| | | // menu hover |
| | | .submenu-title-noDropdown, |
| | | .el-sub-menu__title { |
| | | &:hover { |
| | | background-color: var(--menu-hover) !important; |
| | | border-radius: 14px; |
| | | |
| | | .scrollbar-wrapper { |
| | | overflow-x: hidden !important; |
| | | } |
| | | |
| | | .el-scrollbar__bar.is-vertical { |
| | | right: 0px; |
| | | } |
| | | |
| | | .el-scrollbar { |
| | | height: 100%; |
| | | } |
| | | |
| | | &.has-logo { |
| | | .el-scrollbar { |
| | | height: calc(100% - 72px); |
| | | margin-top: 10px; |
| | | } |
| | | } |
| | | & .theme-light .is-active > .el-sub-menu__title {
|
| | | color: var(--current-color) !important;
|
| | | }
|
| | |
|
| | | |
| | | .is-horizontal { |
| | | display: none; |
| | | } |
| | | |
| | | a { |
| | | display: inline-block; |
| | | width: 100%; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .svg-icon { |
| | | margin-right: 16px; |
| | | } |
| | | |
| | | .el-menu { |
| | | border: 1px solid var(--surface-border) !important; |
| | | height: 100%; |
| | | width: 100% !important; |
| | | padding: 12px 10px 20px; |
| | | border-radius: var(--radius-lg); |
| | | background: var(--menu-surface); |
| | | backdrop-filter: blur(20px); |
| | | box-shadow: var(--shadow-sm); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .el-menu-item, |
| | | .menu-title { |
| | | overflow: hidden !important; |
| | | text-overflow: ellipsis !important; |
| | | white-space: nowrap !important; |
| | | } |
| | | |
| | | .el-menu-item .el-menu-tooltip__trigger { |
| | | display: inline-block !important; |
| | | } |
| | | |
| | | // menu hover - ä¼ååçæ¬åææ |
| | | .submenu-title-noDropdown, |
| | | .el-sub-menu__title { |
| | | transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
| | | border: none !important; |
| | | |
| | | &:hover { |
| | | background-color: var(--menu-hover) !important; |
| | | border-radius: var(--radius-sm); |
| | | transform: translateX(2px); |
| | | } |
| | | } |
| | | |
| | | & .theme-light .is-active > .el-sub-menu__title, |
| | | & .theme-dark .is-active > .el-sub-menu__title { |
| | | color: var(--menu-active-text) !important; |
| | | background: var(--menu-active-bg) !important; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
| | | border: none !important; |
| | | } |
| | | |
| | | & .nest-menu .el-sub-menu > .el-sub-menu__title, |
| | | & .el-sub-menu .el-menu-item { |
| | | min-width: 0 !important; |
| | | margin: 0 12px 6px; |
| | | width: calc(100% - 24px); |
| | | padding-left: 8px !important; |
| | | padding-right: 8px !important; |
| | | margin: 0 10px 5px; |
| | | width: calc(100% - 20px); |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | border-radius: var(--radius-xs); |
| | | transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
| | | color: var(--sidebar-text); |
| | | border: none !important; |
| | | |
| | | &:hover { |
| | | background-color: var(--menu-hover) !important; |
| | | transform: translateX(2px); |
| | | } |
| | | |
| | | &.is-active { |
| | | background-color: var(--menu-active-bg) !important; |
| | | border-radius: 14px; |
| | | background: var(--menu-active-bg) !important; |
| | | border-radius: var(--radius-sm); |
| | | color: var(--menu-active-text) !important; |
| | | font-weight: 500; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | } |
| | |
|
| | | & .theme-light .nest-menu .el-sub-menu > .el-sub-menu__title, |
| | | & .theme-light .el-sub-menu .el-menu-item { |
| | | //background-color: transparent; |
| | | |
| | | & .theme-light .nest-menu .el-sub-menu > .el-sub-menu__title, |
| | | & .theme-light .el-sub-menu .el-menu-item, |
| | | & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, |
| | | & .theme-dark .el-sub-menu .el-menu-item { |
| | | &:hover { |
| | | background-color: var(--menu-hover) !important; |
| | | border-radius: 14px; |
| | | border-radius: var(--radius-xs); |
| | | } |
| | | } |
| | | } |
| | |
|
| | | |
| | | .hideSidebar { |
| | | .sidebar-container { |
| | | width: 68px !important; |
| | |
| | | .main-container { |
| | | margin-left: 84px; |
| | | } |
| | |
|
| | | |
| | | .submenu-title-noDropdown { |
| | | padding: 0 !important; |
| | | position: relative; |
| | |
| | | width: 0; |
| | | overflow: hidden; |
| | | visibility: hidden; |
| | | display: inline-block;
|
| | | }
|
| | | & > i {
|
| | | height: 0;
|
| | | width: 0;
|
| | | overflow: hidden;
|
| | | visibility: hidden;
|
| | | display: inline-block;
|
| | | display: inline-block; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
|
| | | .el-menu--collapse .el-menu .el-sub-menu {
|
| | | min-width: $base-sidebar-width !important;
|
| | | }
|
| | |
|
| | | // mobile responsive
|
| | | .mobile {
|
| | | .main-container {
|
| | | margin-left: 0px;
|
| | | }
|
| | |
|
| | | .sidebar-container {
|
| | | transition: transform 0.28s;
|
| | | width: $base-sidebar-width !important;
|
| | | }
|
| | |
|
| | | &.hideSidebar {
|
| | | .sidebar-container {
|
| | | pointer-events: none;
|
| | | transition-duration: 0.3s;
|
| | | transform: translate3d(-$base-sidebar-width, 0, 0);
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | .withoutAnimation {
|
| | | .main-container,
|
| | | .sidebar-container {
|
| | | transition: none;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | // when menu collapsed
|
| | | .el-menu--vertical {
|
| | | & > .el-menu {
|
| | | .svg-icon {
|
| | | margin-right: 16px;
|
| | | }
|
| | | }
|
| | |
|
| | | |
| | | .el-menu--collapse .el-menu .el-sub-menu { |
| | | min-width: $base-sidebar-width !important; |
| | | } |
| | | |
| | | // mobile responsive |
| | | .mobile { |
| | | .main-container { |
| | | margin-left: 0px; |
| | | } |
| | | |
| | | .sidebar-container { |
| | | transition: transform 0.28s; |
| | | width: $base-sidebar-width !important; |
| | | } |
| | | |
| | | &.hideSidebar { |
| | | .sidebar-container { |
| | | pointer-events: none; |
| | | transition-duration: 0.3s; |
| | | transform: translate3d(-$base-sidebar-width, 0, 0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .withoutAnimation { |
| | | .main-container, |
| | | .sidebar-container { |
| | | transition: none; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // when menu collapsed |
| | | .el-menu--vertical { |
| | | & > .el-menu { |
| | | .svg-icon { |
| | | margin-right: 14px; |
| | | } |
| | | } |
| | | |
| | | .nest-menu .el-sub-menu > .el-sub-menu__title, |
| | | .el-menu-item { |
| | | min-width: 0 !important; |
| | | margin: 0 12px 6px; |
| | | width: calc(100% - 24px); |
| | | padding-left: 8px !important; |
| | | padding-right: 8px !important; |
| | | margin: 0 10px 5px; |
| | | width: calc(100% - 20px); |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | border-radius: var(--radius-xs); |
| | | transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
| | | color: var(--sidebar-text); |
| | | border: none !important; |
| | | |
| | | &:hover { |
| | | // you can use $sub-menuHover |
| | | background-color: var(--menu-hover) !important; |
| | | transform: translateX(2px); |
| | | } |
| | | |
| | | &.is-active { |
| | | background-color: var(--menu-active-bg) !important; |
| | | border-radius: 14px; |
| | | background: var(--menu-active-bg) !important; |
| | | color: var(--menu-active-text) !important; |
| | | border-radius: var(--radius-sm); |
| | | font-weight: 500; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | } |
| | |
|
| | | // the scroll bar appears when the sub-menu is too long
|
| | | > .el-menu--popup {
|
| | | max-height: 100vh;
|
| | | overflow-y: auto;
|
| | | padding: 8px;
|
| | | border-radius: 18px;
|
| | | border: 1px solid var(--surface-border);
|
| | | box-shadow: var(--shadow-md);
|
| | |
|
| | | &::-webkit-scrollbar-track-piece {
|
| | | background: #dfe7e1;
|
| | | }
|
| | |
|
| | | &::-webkit-scrollbar {
|
| | | width: 6px;
|
| | | }
|
| | |
|
| | | &::-webkit-scrollbar-thumb {
|
| | | background: #9aa79e;
|
| | | border-radius: 20px;
|
| | | }
|
| | | }
|
| | | }
|
| | | |
| | | // the scroll bar appears when the sub-menu is too long |
| | | > .el-menu--popup { |
| | | max-height: 100vh; |
| | | overflow-y: auto; |
| | | padding: 10px; |
| | | border-radius: var(--radius-md); |
| | | border: 1px solid var(--surface-border); |
| | | box-shadow: var(--shadow-md); |
| | | background: var(--menu-surface); |
| | | backdrop-filter: blur(20px); |
| | | |
| | | &::-webkit-scrollbar-track-piece { |
| | | background: var(--surface-muted); |
| | | } |
| | | |
| | | &::-webkit-scrollbar { |
| | | width: 5px; |
| | | } |
| | | |
| | | &::-webkit-scrollbar-thumb { |
| | | background: var(--accent-light); |
| | | border-radius: 10px; |
| | | } |
| | | } |
| | | } |
| | |
| | | $yellow: #fec171; |
| | | $panGreen: #30b08f; |
| | | |
| | | // menu palette |
| | | $menuText: #677287; |
| | | $menuActiveText: #1f7a72; |
| | | $menuBg: #f4f7f4; |
| | | $menuHover: #e7eeea; |
| | | // menu palette - 使ç¨ä¸»é¢è² |
| | | $menuText: #5a6478; |
| | | $menuActiveText: #ffffff; |
| | | $menuBg: #f8fafb; |
| | | $menuHover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.08); |
| | | |
| | | // light theme |
| | | $menuLightBg: #f4f7f4; |
| | | $menuLightHover: #e7eeea; |
| | | $menuLightText: #3b4658; |
| | | $menuLightActiveText: #1f7a72; |
| | | // light theme - 使ç¨ä¸»é¢è² |
| | | $menuLightBg: #f8fafb; |
| | | $menuLightHover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.08); |
| | | $menuLightText: #3d4858; |
| | | $menuLightActiveText: #ffffff; |
| | | |
| | | // layout |
| | | $base-sidebar-width: 216px; |
| | | $sideBarWidth: 216px; |
| | | |
| | | // sidebar |
| | | $base-menu-color: #677287; |
| | | $base-menu-color-active: #1f7a72; |
| | | $base-menu-background: #f4f7f4; |
| | | $base-sub-menu-background: #eef3ef; |
| | | // sidebar - ä¼ååçä¾§è¾¹æ é
è² |
| | | $base-menu-color: #5a6478; |
| | | $base-menu-color-active: #0d9488; |
| | | $base-menu-background: #f8fafb; |
| | | $base-sub-menu-background: #f0f5f4; |
| | | $base-sub-menu-hover: #ffffff; |
| | | |
| | | // component |
| | | $--color-primary: #1f7a72; |
| | | // component - ä¼ååç主é¢è² |
| | | $--color-primary: #0d9488; |
| | | $--color-success: #67c23a; |
| | | $--color-warning: #d89b41; |
| | | $--color-danger: #d25b52; |
| | |
| | | :root { |
| | | --sidebar-bg: #{$menuBg}; |
| | | --sidebar-text: #{$menuText}; |
| | | --sidebar-muted: #93a0b1; |
| | | --menu-hover: #{$menuHover}; |
| | | --menu-active-bg: #dfe9e4; |
| | | --menu-surface: rgba(255, 255, 255, 0.72); |
| | | --sidebar-muted: #5a6478; |
| | | --menu-hover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.08); |
| | | --menu-active-bg: var(--el-color-primary, #0d9488); |
| | | --menu-active-text: #ffffff; |
| | | --menu-surface: #f8fafb; |
| | | |
| | | --app-bg: #eef2ee; |
| | | --app-bg-accent: #dfe8e2; |
| | | --app-bg: #f0f4f3; |
| | | --app-bg-accent: #e0ebe9; |
| | | --surface-base: #ffffff; |
| | | --surface-soft: #f7faf8; |
| | | --surface-muted: #eff4f1; |
| | | --surface-border: #d8e1db; |
| | | --surface-border-strong: #c9d5ce; |
| | | --text-primary: #21313f; |
| | | --text-secondary: #5f6d7e; |
| | | --text-tertiary: #8a98a8; |
| | | --shadow-sm: 0 10px 30px rgba(31, 49, 38, 0.06); |
| | | --shadow-md: 0 18px 50px rgba(31, 49, 38, 0.1); |
| | | --radius-lg: 24px; |
| | | --radius-md: 18px; |
| | | --radius-sm: 12px; |
| | | --surface-soft: #f7faf9; |
| | | --surface-muted: #eef3f2; |
| | | --surface-border: #d5e0de; |
| | | --surface-border-strong: #c5d5d2; |
| | | --text-primary: #1e293b; |
| | | --text-secondary: #4a5568; |
| | | --text-tertiary: #718096; |
| | | --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.06); |
| | | --shadow-md: 0 8px 28px rgba(0, 0, 0, 0.1); |
| | | --shadow-menu: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | --radius-lg: 20px; |
| | | --radius-md: 14px; |
| | | --radius-sm: 10px; |
| | | --radius-xs: 6px; |
| | | |
| | | --navbar-bg: rgba(255, 255, 255, 0.78); |
| | | --navbar-text: #21313f; |
| | | --navbar-hover: rgba(31, 122, 114, 0.08); |
| | | --navbar-bg: rgba(255, 255, 255, 0.85); |
| | | --navbar-text: #1e293b; |
| | | --navbar-hover: rgba(13, 148, 136, 0.08); |
| | | |
| | | --tags-bg: transparent; |
| | | --tags-item-bg: rgba(255, 255, 255, 0.74); |
| | | --tags-item-border: rgba(201, 213, 206, 0.88); |
| | | --tags-item-text: #5f6d7e; |
| | | --tags-item-hover: rgba(31, 122, 114, 0.08); |
| | | --tags-close-hover: rgba(31, 122, 114, 0.18); |
| | | --tags-item-bg: rgba(255, 255, 255, 0.8); |
| | | --tags-item-border: rgba(197, 213, 210, 0.9); |
| | | --tags-item-text: #4a5568; |
| | | --tags-item-hover: rgba(13, 148, 136, 0.1); |
| | | --tags-close-hover: rgba(13, 148, 136, 0.2); |
| | | |
| | | --accent-primary: #0d9488; |
| | | --accent-light: #14b8a6; |
| | | --accent-lighter: #5eead4; |
| | | |
| | | --splitpanes-default-bg: #ffffff; |
| | | } |
| | |
| | | --el-border-color: #434343; |
| | | --el-border-color-light: #434343; |
| | | |
| | | --sidebar-bg: #141414; |
| | | --sidebar-text: #ffffff; |
| | | --menu-hover: #2d2d2d; |
| | | --menu-active-text: #{$menuActiveText}; |
| | | --sidebar-bg: #1a1a1a; |
| | | --sidebar-text: #d0d0d0; |
| | | --sidebar-muted: #888888; |
| | | --menu-hover: rgba(var(--el-color-primary-rgb, 13, 148, 136), 0.12); |
| | | --menu-active-bg: var(--el-color-primary, #0d9488); |
| | | --menu-active-text: #ffffff; |
| | | --menu-surface: #1a1a1a; |
| | | |
| | | --text-primary: #ffffff; |
| | | --text-secondary: #d0d0d0; |
| | | --text-tertiary: #888888; |
| | | --accent-primary: var(--el-color-primary, #0d9488); |
| | | --accent-light: var(--el-color-primary-light-3, #14b8a6); |
| | | |
| | | --navbar-bg: #141414; |
| | | --navbar-text: #ffffff; |
| | |
| | | .sidebar-container { |
| | | .el-menu-item, |
| | | .menu-title { |
| | | color: var(--el-text-color-regular); |
| | | color: var(--sidebar-text); |
| | | } |
| | | |
| | | .el-menu-item.is-active, |
| | | .el-menu-item.is-active .menu-title { |
| | | color: var(--menu-active-text) !important; |
| | | } |
| | | |
| | | & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, |
| | | & .theme-dark .el-sub-menu .el-menu-item { |
| | | background-color: var(--el-bg-color) !important; |
| | | } |
| | | |
| | | & .theme-dark .el-sub-menu .el-menu-item.is-active { |
| | | background-color: var(--menu-active-bg) !important; |
| | | } |
| | | } |
| | | |
| | | .el-menu--horizontal { |
| | |
| | | import { generalAssistant } from './generalAssistant' |
| | | import { purchaseAssistant } from './purchaseAssistant' |
| | | import { productionAssistant } from './productionAssistant' |
| | | import { salesAssistant } from './salesAssistant' |
| | | |
| | | export { generalAssistant, purchaseAssistant, productionAssistant } |
| | | export { generalAssistant, purchaseAssistant, productionAssistant, salesAssistant } |
| | | |
| | | export const assistantRegistry = { |
| | | general: generalAssistant, |
| | | sales: salesAssistant, |
| | | purchase: purchaseAssistant, |
| | | production: productionAssistant |
| | | } |
| | | |
| | | export const builtInAssistants = [generalAssistant, purchaseAssistant, productionAssistant] |
| | | export const builtInAssistants = [generalAssistant, salesAssistant, purchaseAssistant, productionAssistant] |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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: 'æå¯ä»¥åå©ä½ æ¥è¯¢å®¢æ·æ¡£æ¡ãé宿¥ä»·ãéå®å°è´¦ãéå®éè´§ã客æ·å¾æ¥ãåè´§å°è´¦ï¼å¹¶éç¹åæå®¢æ·æµå¤±é£é©å忬¾/æ¥ä»·çç¥ã', |
| | | allowFileUpload: false, |
| | | emptySessionText: 'ææ éå®ä¼è¯', |
| | | quickPrompts: [ |
| | | 'æ¥è¯¢ç§æµ·å®¢æ·æ¡£æ¡å10æ¡', |
| | | 'æ¥è¯¢å
¬æµ·å®¢æ·æ¡£æ¡', |
| | | 'æ¥è¯¢æ¬æé宿¥ä»·', |
| | | 'æ¥è¯¢æ¬æéå®å°è´¦', |
| | | 'æ¥è¯¢è¿30天éå®éè´§', |
| | | 'æ¥è¯¢è¿30天客æ·åæ¬¾å¾æ¥', |
| | | 'æ¥è¯¢æ¬æåè´§å°è´¦', |
| | | 'æ¥çé宿æ ç»è®¡', |
| | | '帮æåå®¢æ·æµå¤±é£é©åæï¼è¿30天ï¼å20æ¡', |
| | | 'çæåæ¬¾ä¸æ¥ä»·çç¥å»ºè®®ï¼ä¼å
é«é£é©å®¢æ·' |
| | | ] |
| | | } |
| | |
| | | </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> |
| | |
| | | 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: 'å®¢æ·æ¡£æ¡', |
| | | 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', |
| | |
| | | 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: '产åæç»', |
| | |
| | | |
| | | 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' |
| | |
| | | payloadHiddenData: null, |
| | | purchaseAnalysisData: null, |
| | | manufacturingData: null, |
| | | salesData: null, |
| | | localUploadFiles: isUser ? mapHistoryFilePathsToSnapshots(msg.filePaths, uuid.value, idx) : [] |
| | | } |
| | | |
| | |
| | | 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) |
| | |
| | | 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 || {}) |
| | | } |
| | |
| | | const getStructuredFallbackText = (parsedData) => { |
| | | if (!parsedData) return 'æ£å¨ä¸ºæ¨å±ç¤ºåæç»æ...' |
| | | 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 '已为æ¨çæå¶é é¢è¦çæ¿ã' |
| | |
| | | payloadTreeData: null, |
| | | payloadHiddenData: null, |
| | | purchaseAnalysisData: null, |
| | | manufacturingData: null |
| | | manufacturingData: null, |
| | | salesData: null |
| | | }) |
| | | |
| | | outputState.value[botMsgIndex] = { |
| | |
| | | payloadTreeData: null, |
| | | payloadHiddenData: null, |
| | | purchaseAnalysisData: null, |
| | | manufacturingData: null |
| | | manufacturingData: null, |
| | | salesData: null |
| | | } |
| | | messages.value.push(botMsg) |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | .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%; |
| | |
| | | width: 100% !important; |
| | | height: 56px !important; |
| | | line-height: 56px; |
| | | background: rgba(255, 255, 255, 0.78); |
| | | background: var(--menu-surface); |
| | | border: 1px solid var(--surface-border); |
| | | border-radius: 22px; |
| | | border-radius: var(--radius-lg); |
| | | text-align: center; |
| | | overflow: hidden; |
| | | box-shadow: var(--shadow-sm); |
| | | backdrop-filter: blur(20px); |
| | | transition: all 0.3s ease; |
| | | |
| | | .sidebar-logo-link { |
| | | height: 100%; |
| | |
| | | <template>
|
| | | <div v-if="!item.hidden">
|
| | | <div v-if="!item.hidden" class="sidebar-item-wrapper">
|
| | | <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
|
| | | <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
|
| | | <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
|
| | | <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>
|
| | | <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>
|
| | | <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" class="menu-icon"/>
|
| | | <template #title>
|
| | | <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span>
|
| | | </template>
|
| | | </el-menu-item>
|
| | | </app-link>
|
| | | </template>
|
| | |
|
| | | <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
|
| | | <template v-if="item.meta" #title>
|
| | | <svg-icon :icon-class="item.meta && item.meta.icon" />
|
| | | <svg-icon :icon-class="item.meta && item.meta.icon" class="menu-icon" />
|
| | | <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
|
| | | </template>
|
| | |
|
| | |
| | | }
|
| | | }
|
| | | </script>
|
| | |
|
| | | <style lang="scss" scoped>
|
| | | .sidebar-item-wrapper {
|
| | | :deep(.menu-icon) {
|
| | | width: 18px;
|
| | | height: 18px;
|
| | | margin-right: 12px;
|
| | | flex-shrink: 0;
|
| | | transition: all 0.25s ease;
|
| | | color: var(--sidebar-text);
|
| | | opacity: 0.8;
|
| | | }
|
| | | |
| | | :deep(.el-menu-item:hover .menu-icon),
|
| | | :deep(.el-sub-menu__title:hover .menu-icon) {
|
| | | color: var(--el-color-primary, var(--accent-primary));
|
| | | opacity: 1;
|
| | | transform: scale(1.1);
|
| | | }
|
| | | |
| | | :deep(.el-menu-item.is-active .menu-icon) {
|
| | | color: var(--menu-active-text) !important;
|
| | | opacity: 1;
|
| | | }
|
| | | |
| | | :deep(.menu-title) {
|
| | | font-weight: 450;
|
| | | transition: all 0.25s ease;
|
| | | color: var(--sidebar-text);
|
| | | }
|
| | | |
| | | :deep(.el-menu-item:hover .menu-title),
|
| | | :deep(.el-sub-menu__title:hover .menu-title) {
|
| | | color: var(--el-color-primary, var(--accent-primary));
|
| | | }
|
| | | |
| | | :deep(.el-menu-item.is-active .menu-title) {
|
| | | color: var(--menu-active-text) !important;
|
| | | }
|
| | | |
| | | :deep(.nest-menu) {
|
| | | .menu-icon {
|
| | | width: 16px;
|
| | | height: 16px;
|
| | | margin-right: 10px;
|
| | | }
|
| | | |
| | | .menu-title {
|
| | | font-size: 13px;
|
| | | }
|
| | | }
|
| | | }
|
| | | </style>
|
| | |
| | | <style lang="scss" scoped> |
| | | .sidebar-container { |
| | | background-color: v-bind(getMenuBackground); |
| | | border-radius: 22px; |
| | | border-radius: var(--radius-lg); |
| | | overflow: hidden; |
| | | |
| | | .scrollbar-wrapper { |
| | |
| | | } |
| | | |
| | | .el-menu { |
| | | border: none; |
| | | border: 1px solid var(--surface-border) !important; |
| | | height: 100%; |
| | | width: 100% !important; |
| | | border-radius: 22px; |
| | | border-radius: var(--radius-lg); |
| | | |
| | | .el-menu-item, |
| | | .el-sub-menu__title { |
| | | margin-bottom: 6px; |
| | | border-radius: 14px; |
| | | margin-bottom: 5px; |
| | | border-radius: var(--radius-xs); |
| | | color: v-bind(getMenuTextColor); |
| | | font-size: 13.5px; |
| | | letter-spacing: 0.2px; |
| | | transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
| | | border: none !important; |
| | | |
| | | &:hover { |
| | | background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important; |
| | | border-radius: 14px; |
| | | background-color: var(--menu-hover) !important; |
| | | border-radius: var(--radius-sm); |
| | | transform: translateX(2px); |
| | | } |
| | | |
| | | .svg-icon { |
| | | transition: all 0.25s ease; |
| | | } |
| | | |
| | | &:hover .svg-icon { |
| | | transform: scale(1.1); |
| | | color: var(--el-color-primary, var(--accent-primary)); |
| | | } |
| | | } |
| | | |
| | | .el-menu-item { |
| | | color: var(--sidebar-text); |
| | | |
| | | &.is-active { |
| | | color: v-bind(theme); |
| | | background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; |
| | | font-weight: 600; |
| | | background: var(--menu-active-bg) !important; |
| | | color: var(--menu-active-text) !important; |
| | | font-weight: 500; |
| | | border-radius: var(--radius-sm); |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
| | | |
| | | .svg-icon { |
| | | color: var(--menu-active-text) !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | 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.7; |
| | | transition: all 0.25s ease; |
| | | } |
| | | |
| | | :deep(.el-sub-menu.is-opened .el-sub-menu__icon-arrow) { |
| | | transform: rotate(180deg); |
| | | } |
| | | |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title) { |
| | | color: v-bind(theme) !important; |
| | | color: var(--menu-active-text) !important; |
| | | font-weight: 600; |
| | | background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important; |
| | | border-radius: 14px; |
| | | margin: 0 10px 6px !important; |
| | | // width: calc(100% - 20px) !important; |
| | | border-radius: var(--radius-sm); |
| | | margin: 0 10px 5px !important; |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | background-clip: padding-box; |
| | | background: var(--menu-active-bg) !important; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
| | | border: none !important; |
| | | } |
| | | |
| | | :deep(.el-menu-item.is-active) { |
| | | margin: 0 10px 6px !important; |
| | | margin: 0 10px 5px !important; |
| | | width: calc(100% - 20px) !important; |
| | | padding-left: 10px !important; |
| | | padding-right: 10px !important; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | background-clip: padding-box; |
| | | border-radius: 14px; |
| | | border-radius: var(--radius-sm); |
| | | } |
| | | |
| | | :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-menu-item.is-active .menu-title), |
| | | :deep(.el-menu-item.is-active .svg-icon) { |
| | | color: v-bind(theme) !important; |
| | | :deep(.el-sub-menu.is-active > .el-sub-menu__title .svg-icon) { |
| | | color: var(--menu-active-text) !important; |
| | | } |
| | | |
| | | :deep(.el-menu-item.is-active .menu-title) { |
| | | color: var(--menu-active-text) !important; |
| | | } |
| | | |
| | | :deep(.el-sub-menu__title:hover), |
| | | :deep(.el-menu-item:hover) { |
| | | border-radius: 14px; |
| | | border-radius: var(--radius-sm); |
| | | } |
| | | |
| | | // åèåå±å¼å¨ç»ä¼å |
| | | :deep(.el-sub-menu .el-menu) { |
| | | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| | | } |
| | | |
| | | // èå项è¿å
¥å¨ç» |
| | | :deep(.el-menu-item), |
| | | :deep(.el-sub-menu__title) { |
| | | animation: menuItemFadeIn 0.3s ease forwards; |
| | | } |
| | | |
| | | @keyframes menuItemFadeIn { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateX(-8px); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: translateX(0); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | <el-input v-model="search" |
| | | style="width: 210px" |
| | | placeholder="è¾å
¥å
³é®åè¿è¡æç´¢" |
| | | @change="searchFilter" |
| | | @input="debouncedSearch" |
| | | @clear="searchFilter" |
| | | clearable |
| | | prefix-icon="Search" /> |
| | |
| | | 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); |
| | | }; |
| | | }; |
| | | // è¿æ»¤ç¶èç¹ / åèç¹ (妿è¾å
¥çåæ°æ¯ç¶èç¹ä¸è½å¹é
ï¼åè¿å该èç¹ä»¥åå
¶ä¸çææåèç¹ï¼å¦æåæ°æ¯åèç¹ï¼åè¿å该èç¹çç¶èç¹ãnameæ¯ä¸æå符ï¼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弿¯ä¸æå符ï¼enNameæ¯è±æå符ã夿å¹é
ä¸è±æè¿æ»¤ |
| | | if (parentData.data.label.indexOf(value) !== -1) { |
| | | return true; |
| | | } |
| | | // å¦åçè¯åå¾ä¸ä¸å±åå¹é
|
| | | parentData = parentData.parent; |
| | | index++; |
| | | } |
| | | // 没å¹é
å°è¿åfalse |
| | | return false; |
| | | }; |
| | | getProductTreeList(); |
| | |
| | | </div> |
| | | </template> |
| | | <template #isEnabled="{ row }"> |
| | | <el-tag :type="row.isEnabled === 1 ? 'success' : 'danger'" size="small"> |
| | | <el-tag :type="row.isEnabled === 1 ? 'success' : 'danger'" |
| | | size="small"> |
| | | {{ row.isEnabled == 1 ? 'æ¯' : 'å¦' }} |
| | | </el-tag> |
| | | </template> |
| | |
| | | label: "æ¯å¦å¯ç¨", |
| | | minWidth: 100, |
| | | dataType: "slot", |
| | | slot: "isEnabled" |
| | | slot: "isEnabled", |
| | | }, |
| | | { |
| | | prop: "frequencyType", |
| | |
| | | }, |
| | | { prop: "registrant", label: "ç»è®°äºº", minWidth: 100 }, |
| | | { prop: "createTime", label: "ç»è®°æ¥æ", minWidth: 100 }, |
| | | { |
| | | prop: "inspectionResult", |
| | | label: "å·¡æ£ç»æ", |
| | | minWidth: 100, |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | return val == 1 ? "æ£å¸¸" : "å¼å¸¸"; |
| | | }, |
| | | formatType: val => { |
| | | return val == 1 ? "success" : "danger"; |
| | | }, |
| | | }, |
| | | { prop: "abnormalDescription", label: "å¼å¸¸æè¿°", minWidth: 100 }, |
| | | ]); |
| | | |
| | | // æä½åé
ç½® |
| | |
| | | <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"]); |
| | | |
| | | // ä¿å计åä¿å
»è®°å½çid |
| | | const planId = ref(); |
| | | const visible = ref(false); |
| | | const loading = ref(false); |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | // ä¿å计åä¿å
»è®°å½çid |
| | | 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 { |
| | |
| | | .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; // ä¿å计åä¿å
»è®°å½çid |
| | | visible.value = true; |
| | | await nextTick(); |
| | | fetchSparePartOptions() |
| | | setForm(row); |
| | | }; |
| | | const open = async (id, row) => { |
| | | planId.value = id; // ä¿å计åä¿å
»è®°å½çid |
| | | visible.value = true; |
| | | await nextTick(); |
| | | fetchSparePartOptions(); |
| | | setForm(row); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | open, |
| | | }); |
| | | defineExpose({ |
| | | open, |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped></style> |
| | |
| | | <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" |
| | |
| | | <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" |
| | |
| | | </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" |
| | |
| | | <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" |
| | |
| | | const fileDialogVisible = ref(false); |
| | | const currentMaintenanceTaskId = ref(null); |
| | | |
| | | // ä»»å¡è®°å½tabï¼å设å¤ä¿å
»é¡µé¢ï¼ç¸å
³åé |
| | | // ä¿å
»è®°å½tabï¼å设å¤ä¿å
»é¡µé¢ï¼ç¸å
³åé |
| | | const filters = reactive({ |
| | | deviceName: "", |
| | | maintenancePlanTime: "", |
| | |
| | | }); |
| | | const multipleList = ref([]); |
| | | |
| | | // 宿¶ä»»å¡ç®¡çtabç¸å
³åé |
| | | // ä¿å
»ä»»å¡tabç¸å
³åé |
| | | const scheduledFilters = reactive({ |
| | | taskName: "", |
| | | status: "", |
| | |
| | | }); |
| | | const scheduledMultipleList = ref([]); |
| | | |
| | | // 宿¶ä»»å¡ç®¡çè¡¨æ ¼åé
ç½® |
| | | // ä¿å
»ä»»å¡è¡¨æ ¼åé
ç½® |
| | | const scheduledColumns = ref([ |
| | | { prop: "taskName", label: "设å¤åç§°" }, |
| | | { |
| | |
| | | }, |
| | | ]); |
| | | |
| | | // ä»»å¡è®°å½è¡¨æ ¼åé
ç½®ï¼å设å¤ä¿å
»è¡¨æ ¼åï¼ |
| | | // ä¿å
»è®°å½è¡¨æ ¼åé
ç½®ï¼å设å¤ä¿å
»è¡¨æ ¼åï¼ |
| | | const columns = ref([ |
| | | { |
| | | label: "设å¤åç§°", |
| | |
| | | } |
| | | }; |
| | | |
| | | // 宿¶ä»»å¡ç®¡çç¸å
³æ¹æ³ |
| | | // ä¿å
»ä»»å¡ç¸å
³æ¹æ³ |
| | | const getScheduledTableData = async () => { |
| | | try { |
| | | const params = { |
| | |
| | | ElMessage.info("导åºå®æ¶ä»»å¡åè½å¾
å®ç°"); |
| | | }; |
| | | |
| | | // ä»»å¡è®°å½ç¸å
³æ¹æ³ï¼å设å¤ä¿å
»é¡µé¢æ¹æ³ï¼ |
| | | // ä¿å
»è®°å½ç¸å
³æ¹æ³ï¼å设å¤ä¿å
»é¡µé¢æ¹æ³ï¼ |
| | | const getTableData = async () => { |
| | | try { |
| | | const params = { |
| | |
| | | }); |
| | | }; |
| | | |
| | | 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 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("è¯·éæ©è§æ ¼"); |
| | |
| | | // return; |
| | | // } |
| | | |
| | | // é彿 ¡éªå项 |
| | | // é彿 ¡éªå项忮µ |
| | | if (item.children && item.children.length > 0) { |
| | | item.children.forEach(child => { |
| | | validateItem(child, false); |
| | |
| | | } |
| | | }; |
| | | |
| | | // éåææé¡¶å±é¡¹ |
| | | // 1. é¦å
æ ¡éªåä¸ç¶çº§ä¸çå屿¶èå·¥åºæ¯å¦å¯ä¸ |
| | | checkProcessUniqueness(dataValue.dataList); |
| | | if (!isValid) return false; |
| | | |
| | | // 2. ç¶åéåæ ¡éªææé¡¶å±é¡¹çåæ®µå¿
å¡«æ
åµ |
| | | dataValue.dataList.forEach(item => { |
| | | validateItem(item, true); |
| | | }); |
| | |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | :loading="materialSaving" |
| | | :disabled="isSaveDisabled" |
| | | @click="handleMaterialSave">ä¿å</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </span> |
| | |
| | | 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; |
| | |
| | | <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"> |
| | |
| | | processForm.isQuality = !!process.isQuality; |
| | | processForm.isProduction = !!process.isProduction; |
| | | processForm.remark = process.remark || ""; |
| | | processForm.deviceLedgerId = Number(process.deviceLedgerId); |
| | | // å¦æè®¾å¤ ID 为 0 æè
å¨è®¾å¤åè¡¨ä¸æ¾ä¸å°ï¼ååæ¾ä¸ºç©ºï¼nullï¼ |
| | | 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; |
| | | }; |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <PageHeader content="ç产订å" /> |
| | | <el-card style="height:82vh;overflow:auto;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | |
| | | } |
| | | } |
| | | 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; |
| | |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢åºåæ£éª' : 'ç¼è¾åºåæ£éª'" |
| | | :title="operationType === 'add' ? 'æ°å¢åºåæ£éª' : operationType === 'view' ? 'æ¥çåºåæ£éª' : 'ç¼è¾åºåæ£éª'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | |
| | | @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> |
| | |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%" |
| | | :disabled="isViewMode" |
| | | > |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | |
| | | </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="processQuantityDisabled"/> |
| | | <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> |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :precision="2" |
| | | @change="handleQualifiedQuantityChange" /> |
| | | @change="handleQualifiedQuantityChange" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :precision="2" |
| | | @change="handleUnqualifiedQuantityChange" /> |
| | | @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-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£éªåï¼" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="è¯·éæ©" clearable> |
| | | <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> |
| | |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isViewMode" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | 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> |
| | |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | // æ¯å¦ä¸ºæ¥çæ¨¡å¼ |
| | | const isViewMode = computed(() => operationType.value === 'view'); |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åå·¥åºãæ°éç½®ç° |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | |
| | | if (operationType.value === 'edit') { |
| | | if (operationType.value === 'edit' || operationType.value === 'view') { |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // å
è®¾ç½®è¡¨åæ°æ®ï¼ä½ææ¶æ¸
空 testStandardIdï¼çé项å è½½å®æåå设置 |
| | |
| | | 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 = []; |
| | |
| | | } |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openForm("view", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "éä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | |
| | | </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> |
| | |
| | | productId: "", |
| | | model: "", |
| | | unit: "", |
| | | quantity: "", |
| | | quantity: undefined, |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | inspectType: '', |
| | |
| | | dealResult: '', |
| | | dealName: '', |
| | | dealTime: '', |
| | | productModelId: undefined, |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: false, message: "请è¾å
¥", trigger: "blur" },], |
| | |
| | | productId: '', |
| | | model: '', |
| | | unit: '', |
| | | quantity: '', |
| | | quantity: undefined, |
| | | productName: '', |
| | | productModelId: undefined, |
| | | }; |
| | | } else { |
| | | form.value = {}; |
| | |
| | | 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++) { |
| | |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | | </style> |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢è¿ç¨æ£éª' : 'ç¼è¾è¿ç¨æ£éª'" |
| | | :title="operationType === 'add' ? 'æ°å¢è¿ç¨æ£éª' : operationType === 'view' ? 'æ¥çè¿ç¨æ£éª' : 'ç¼è¾è¿ç¨æ£éª'" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | |
| | | <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" |
| | |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="operationType === 'edit'" |
| | | :disabled="isViewMode || operationType === 'edit'" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-select v-model="form.productModelId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | :disabled="isViewMode || operationType === 'edit'" |
| | | filterable |
| | | readonly |
| | | @change="handleChangeModel"> |
| | |
| | | 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" |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :precision="2" |
| | | :disabled="processQuantityDisabled" /> |
| | | :disabled="isViewMode || processQuantityDisabled" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :precision="2" |
| | | @change="handleQualifiedQuantityChange" /> |
| | | @change="handleQualifiedQuantityChange" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :precision="2" |
| | | @change="handleUnqualifiedQuantityChange" /> |
| | | @change="handleUnqualifiedQuantityChange" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | 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="ä¸åæ ¼" |
| | |
| | | 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" |
| | |
| | | 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> |
| | |
| | | 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> |
| | |
| | | }); |
| | | const userList = ref([]); |
| | | const { form, rules } = toRefs(data); |
| | | // æ¯å¦ä¸ºæ¥çæ¨¡å¼ |
| | | const isViewMode = computed(() => operationType.value === 'view'); |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åå·¥åºãæ°éç½®ç° |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | |
| | | tableData.value = []; |
| | | // å
ç¡®ä¿äº§åæ å·²å è½½ï¼å¦åç¼è¾æ¶äº§å/è§æ ¼åå·æ æ³åæ¾ |
| | | await getProductOptions(); |
| | | if (operationType.value === "edit") { |
| | | if (operationType.value === "edit" || operationType.value === "view") { |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // å
è®¾ç½®è¡¨åæ°æ®ï¼ä½ææ¶æ¸
空 testStandardIdï¼çé项å è½½å®æåå设置 |
| | |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | tableData.value = res.data || []; |
| | | tableData.value = tableData.value.map(item => ({ |
| | | ...item, |
| | | id: null |
| | | })); |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ ååæ°å¤±è´¥:", error); |
| | |
| | | } |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openForm("view", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "éä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢åæææ£éª' : 'ç¼è¾åæææ£éª'" |
| | | :title="operationType === 'add' ? 'æ°å¢åæææ£éª' : operationType === 'view' ? 'æ¥çåæææ£éª' : 'ç¼è¾åæææ£éª'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | |
| | | v-model="form.supplier" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | :disabled="supplierQuantityDisabled" |
| | | :disabled="isViewMode || supplierQuantityDisabled" |
| | | > |
| | | <el-option |
| | | v-for="item in supplierList" |
| | |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="operationType === 'edit'" |
| | | :disabled="isViewMode || operationType === 'edit'" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | |
| | | <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> |
| | |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%" |
| | | :disabled="isViewMode" |
| | | > |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | |
| | | <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-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"/> |
| | | @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"/> |
| | | @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-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> |
| | |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isViewMode" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | 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> |
| | |
| | | const modelOptions = ref([]); |
| | | const userList = ref([]); // æ£éªå䏿å表 |
| | | |
| | | // æ¯å¦ä¸ºæ¥çæ¨¡å¼ |
| | | const isViewMode = computed(() => operationType.value === 'view'); |
| | | |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åä¾åºåãæ°éç½®ç° |
| | | const supplierQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | |
| | | tableData.value = []; |
| | | // å
ç¡®ä¿äº§åæ å·²å è½½ï¼å¦åç¼è¾æ¶äº§å/è§æ ¼åå·æ æ³åæ¾ |
| | | await getProductOptions(); |
| | | if (operationType.value === 'edit') { |
| | | if (operationType.value === 'edit' || operationType.value === 'view') { |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | form.value = {...row} |
| | |
| | | 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 = []; |
| | |
| | | } |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openForm("view", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "éä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |