| | |
| | |
|
| | | # ä¸å°ä¼ä¸æ°åå转åäºçº§å¥é¤å
/å¼åç¯å¢
|
| | | VITE_APP_BASE_API = '/dev-api'
|
| | |
|
| | | VITE_BASE_API = 'http://localhost:9003'
|
| | |
| | | yarn-error.log* |
| | | **/*.log |
| | | |
| | | .claude/ |
| | | |
| | | tests/**/coverage/ |
| | | tests/e2e/reports |
| | | selenium-debug.log |
| | |
| | | *.njsproj |
| | | *.sln |
| | | *.local |
| | | |
| | | # Claude Code |
| | | .claude/ |
| | | |
| | | package-lock.json |
| | | yarn.lock |
| | |
| | | "element-plus": "2.7.7", |
| | | "file-saver": "2.0.5", |
| | | "fuse.js": "6.6.2", |
| | | "html2canvas": "^1.4.1", |
| | | "js-beautify": "1.14.11", |
| | | "js-cookie": "3.0.5", |
| | | "jsencrypt": "3.3.2", |
| | |
| | | }) |
| | | } |
| | | |
| | | // æµå
¥å
¬æµ· |
| | | // åé客æ·å°é宿¡£æ¡ |
| | | export function backCustomer(id) { |
| | | return request({ |
| | | url: '/basic/customer/back/' + id, |
| | |
| | | import request from "@/utils/request.js"; |
| | | |
| | | /** å®¡æ¹æ¨¡æ¿ç±»åçéç¨æä¸¾ï¼TypeEnumsï¼ */ |
| | | export function getTypeEnums() { |
| | | return request({ |
| | | url: '/basic/enum/TypeEnums', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | export function findAllStockRecordTypeOptions() { |
| | | return request({ |
| | | url: '/basic/enum/stockRecordType', |
| | |
| | | import request from "@/utils/request"; |
| | | import { getToken } from '@/utils/auth'; |
| | | |
| | | /** |
| | | * ç¥è¯åºç®¡çæ¥å£ |
| | | * ä¼ åè§è: |
| | | * - GET请æ±: ä½¿ç¨ params |
| | | * - POST请æ±: ä½¿ç¨ data |
| | | * - DELETE请æ±: ä½¿ç¨ data |
| | | * - æµå¼è¯·æ±: ä½¿ç¨ Fetch API |
| | | */ |
| | | |
| | | // ==================== ç¥è¯åºCRUDæ¥å£ ==================== |
| | | |
| | | /** |
| | | * æ¥è¯¢ç¥è¯åºå表 |
| | | * @param {Object} query - æ¥è¯¢åæ° |
| | | * @param {number} query.current - å½å页ç (å¿
å¡«) |
| | | * @param {number} query.size - æ¯é¡µæ¡æ°(å¿
å¡«) |
| | | * @param {string} [query.title] - ç¥è¯æ é¢(å¯é,æ¨¡ç³æç´¢) |
| | | * @param {string} [query.type] - ç¥è¯ç±»å(å¯é,精确å¹é
) |
| | | * @returns {Promise} |
| | | */ |
| | | // æ¥è¯¢ç¥è¯åºå表 |
| | | export function listKnowledgeBase(query) { |
| | | return request({ |
| | | url: "/knowledgeBase/getList", |
| | | method: "get", |
| | | params: query, // GET请æ±ä½¿ç¨params |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * æ°å¢ç¥è¯åº |
| | | * @param {Object} data - ç¥è¯åºæ°æ® |
| | | * @param {string} data.title - ç¥è¯æ é¢(å¿
å¡«) |
| | | * @param {string} data.type - ç¥è¯ç±»å(å¿
å¡«) |
| | | * @param {string} [data.scenario] - éç¨åºæ¯(å¯é) |
| | | * @param {string} [data.efficiency] - è§£å³æç(å¯é) |
| | | * @param {string} data.problem - é®é¢æè¿°(å¿
å¡«) |
| | | * @param {string} data.solution - è§£å³æ¹æ¡(å¿
å¡«) |
| | | * @param {string} [data.keyPoints] - å
³é®è¦ç¹(å¯é) |
| | | * @param {string} [data.creator] - å建人(å¯é) |
| | | * @param {number} [data.usageCount=0] - ä½¿ç¨æ¬¡æ°(å¯é) |
| | | * @returns {Promise} |
| | | */ |
| | | // æ¥è¯¢ç¥è¯åºè¯¦ç» |
| | | // export function getKnowledgeBase(knowledgeBaseId) { |
| | | // return request({ |
| | | // url: "/collaborativeApproval/knowledgeBase/" + knowledgeBaseId, |
| | | // method: "get", |
| | | // }); |
| | | // } |
| | | |
| | | // æ°å¢ç¥è¯åº |
| | | export function addKnowledgeBase(data) { |
| | | return request({ |
| | | url: "/knowledgeBase/add", |
| | | method: "post", |
| | | data: data, // POST请æ±ä½¿ç¨data |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * ä¿®æ¹ç¥è¯åº |
| | | * @param {Object} data - ç¥è¯åºæ°æ® |
| | | * @param {number} data.id - ç¥è¯åºID(å¿
å¡«) |
| | | * @param {string} data.title - ç¥è¯æ é¢(å¿
å¡«) |
| | | * @param {string} data.type - ç¥è¯ç±»å(å¿
å¡«) |
| | | * @returns {Promise} |
| | | */ |
| | | // ä¿®æ¹ç¥è¯åº |
| | | export function updateKnowledgeBase(data) { |
| | | return request({ |
| | | url: "/knowledgeBase/update", |
| | | method: "post", |
| | | data: data, // POST请æ±ä½¿ç¨data |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * å é¤ç¥è¯åº(æ¯ææ¹éå é¤) |
| | | * @param {number[]} ids - ç¥è¯åºIDæ°ç» |
| | | * @returns {Promise} |
| | | */ |
| | | export function delKnowledgeBase(ids) { |
| | | // å é¤ç¥è¯åº |
| | | export function delKnowledgeBase(query) { |
| | | return request({ |
| | | url: "/knowledgeBase/delete", |
| | | method: "delete", |
| | | data: ids, // DELETE请æ±ä½¿ç¨dataä¼ éæ°ç» |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // ==================== æä»¶ç®¡çæ¥å£ ==================== |
| | | |
| | | /** |
| | | * æ¥è¯¢ç¥è¯åºæä»¶åéåç¶æ |
| | | * @param {number} knowledgeBaseId - ç¥è¯åºID |
| | | * @returns {Promise} è¿åæä»¶å表ååéåç¶æ |
| | | */ |
| | | export function getVectorStatus(knowledgeBaseId) { |
| | | // æ¹éå é¤ç¥è¯åº |
| | | export function delKnowledgeBaseBatch(knowledgeBaseIds) { |
| | | return request({ |
| | | url: `/knowledgeBase/vector/status/${knowledgeBaseId}`, |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * ä¿åç¥è¯åºæä»¶å
³è(触ååéå) |
| | | * @param {Object} data - æä»¶å
³èæ°æ® |
| | | * @param {number} data.knowledgeBaseId - ç¥è¯åºID(å¿
å¡«) |
| | | * @param {number[]} data.storageBlobIds - æä»¶blob IDæ°ç»(å¿
å¡«) |
| | | * @returns {Promise} |
| | | */ |
| | | export function saveKnowledgeBaseFiles(data) { |
| | | return request({ |
| | | url: "/knowledgeBase/file/save", |
| | | method: "post", |
| | | data: data, // POST请æ±ä½¿ç¨data |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * å é¤ç¥è¯åºæä»¶ |
| | | * @param {number[]} ids - åéè®°å½IDæ°ç» |
| | | * @returns {Promise} |
| | | */ |
| | | export function deleteKnowledgeBaseFile(ids) { |
| | | return request({ |
| | | url: "/knowledgeBase/file/delete", |
| | | url: "/knowledgeBase/batch", |
| | | method: "delete", |
| | | data: ids, // DELETE请æ±ä½¿ç¨dataä¼ éæ°ç» |
| | | data: knowledgeBaseIds, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * éæ°åéåæä»¶ |
| | | * @param {number} vectorId - åéè®°å½ID |
| | | * @returns {Promise} |
| | | */ |
| | | export function reprocessVector(vectorId) { |
| | | return request({ |
| | | url: `/knowledgeBase/vector/reprocess/${vectorId}`, |
| | | method: "post", |
| | | }); |
| | | } |
| | | |
| | | // ==================== ç¥è¯é®çæ¥å£ ==================== |
| | | |
| | | /** |
| | | * ç¥è¯åºé®ç(æµå¼) |
| | | * å端æ¥å£: POST /ai/knowledge/chat |
| | | * ååºç±»å: text/stream;charset=utf-8 (Spring Flux<String>) |
| | | * |
| | | * @param {Object} data - é®çåæ° |
| | | * @param {number} data.knowledgeBaseId - ç¥è¯åºID(å¿
å¡«) |
| | | * @param {string} data.memoryId - ä¼è¯ID(å¿
å¡«,ç¨äºä¿æä¸ä¸æ) |
| | | * @param {string} data.question - ç¨æ·é®é¢(å¿
å¡«) |
| | | * @returns {Promise<Response>} è¿åFetch Response对象 |
| | | */ |
| | | export function knowledgeChat(data) { |
| | | const token = getToken(); |
| | | return fetch(import.meta.env.VITE_APP_BASE_API + '/ai/knowledge/chat', { |
| | | method: 'POST', |
| | | headers: { |
| | | 'Content-Type': 'application/json', |
| | | 'Authorization': 'Bearer ' + token |
| | | }, |
| | | body: JSON.stringify(data) |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢ç¥è¯åºé®çåå² |
| | | * @param {string} memoryId - ä¼è¯ID |
| | | * @returns {Promise} |
| | | */ |
| | | export function getKnowledgeHistory(memoryId) { |
| | | return request({ |
| | | url: `/ai/knowledge/history/${memoryId}`, |
| | | method: "get", |
| | | }); |
| | | } |
| | |
| | | params: query, |
| | | }) |
| | | } |
| | | |
| | | // å®åå详æ
|
| | | export function getAfterSalesServiceById(id) { |
| | | return request({ |
| | | url: '/afterSalesService/getById', |
| | | method: 'get', |
| | | params: { id } |
| | | }) |
| | | } |
| | |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | /** æ ¹æ®å®¢æ·idæ¥è¯¢å¼ç¥¨ç³è¯·å°è´¦ */ |
| | | export function getAccountInvoiceApplicationList(query) { |
| | | return request({ |
| | | url: '/accountInvoiceApplication/getAccountInvoiceApplicationList', |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // æ¹éå审åºåºè®°å½ |
| | | export const batchUnapproveStockOutRecords = (data) => { |
| | | return request({ |
| | | url: "/stockOutRecord/reAudit", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | |
| | | }); |
| | | } |
| | | |
| | | export function staffSalaryMainCalculateByEmployeeId(data) { |
| | | return request({ |
| | | url: "/staffSalaryMain/calculateByEmployeeId", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function staffSalaryMainAdd(data) { |
| | | return request({ |
| | | url: "/staffSalaryMain/add", |
| | |
| | | }); |
| | | } |
| | | |
| | | // è·åå¯ç¨é¨é¨åè¡¨ï¼æ ¹æ®æä»½æ¥è¯¢ï¼æé¤å·²åæ¾å·¥èµè¡¨çé¨é¨ï¼ |
| | | export function staffSalaryMainAvailableDepts(salaryMonth) { |
| | | return request({ |
| | | url: "/staffSalaryMain/availableDepts", |
| | | method: "get", |
| | | params: { salaryMonth }, |
| | | }); |
| | | } |
| | |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | /** 仿¬¾å°è´¦ - 仿¬¾ç»è®°å表 */ |
| | | export function registrationList(params) { |
| | | return request({ |
| | | url: "/purchase/report/registration", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | } |
| | |
| | | params: query, |
| | | }); |
| | | } |
| | | // éè´éè´§å¯ééè´å°è´¦ |
| | | export function purchaseReturnableList(query) { |
| | | return request({ |
| | | url: "/purchase/ledger/returnableList", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ¥è¯¢ååå· |
| | | export function getSalesNo(query) { |
| | | return request({ |
| | |
| | | }); |
| | | } |
| | | |
| | | export function createPurchaseNo(entryDate) { |
| | | export function createPurchaseNo() { |
| | | return request({ |
| | | url: "/purchase/ledger/createPurchaseNo", |
| | | method: "get", |
| | | params: { entryDate }, |
| | | }); |
| | | } |
| | | export function updateApprovalStatus(query) { |
| | |
| | | params: id, |
| | | }); |
| | | } |
| | | |
| | | export function batchGeneratePurchaseInboundSteps(query) { |
| | | // è·åéè´åæå°æ°æ® |
| | | export function getPrintData(id) { |
| | | return request({ |
| | | url: "/purchase/ledger/batchInsertPurchaseSteps", |
| | | method: "post", |
| | | data: query, |
| | | url: '/purchase/ledger/printData/' + id, |
| | | method: 'get', |
| | | }); |
| | | } |
| | |
| | | params: query, |
| | | }); |
| | | } |
| | | // è·åéå®åæå°æ°æ® |
| | | export function getPrintData(id) { |
| | | return request({ |
| | | url: '/sales/ledger/printData/' + id, |
| | | method: 'get', |
| | | }); |
| | | } |
| | |
| | | |
| | | export const productionOrderProgress = (params = {}) => { |
| | | const safePageNum = Math.max(1, Number(params.pageNum || 1)); |
| | | const safeTab = ["all", "inProgress", "completed", "end"].includes(params.tab) |
| | | const safePageSize = Math.min(50, Math.max(1, Number(params.pageSize || 10))); |
| | | const safeTab = ["all", "inProgress", "completed", "paused"].includes(params.tab) |
| | | ? params.tab |
| | | : "all"; |
| | | return request({ |
| | |
| | | ...params, |
| | | tab: safeTab, |
| | | pageNum: safePageNum, |
| | | pageSize: params.pageSize, |
| | | pageSize: safePageSize, |
| | | }, |
| | | headers: { |
| | | handleAuthError: false, |
| | |
| | | & .nest-menu .el-sub-menu > .el-sub-menu__title, |
| | | & .el-sub-menu .el-menu-item { |
| | | min-width: 0 !important; |
| | | width: calc(100% - 32px) !important; |
| | | margin: 0 16px 6px !important; |
| | | height: 40px; |
| | | line-height: 40px; |
| | | padding-left: 12px !important; |
| | | padding-right: 12px !important; |
| | | border-radius: 8px; |
| | | width: calc(100% - 24px) !important; |
| | | margin: 0 12px 8px !important; |
| | | height: 46px; |
| | | line-height: 46px; |
| | | padding-left: 14px !important; |
| | | padding-right: 14px !important; |
| | | border-radius: 12px; |
| | | transition: all 0.24s ease; |
| | | color: var(--sidebar-text); |
| | | border: none !important; |
| | | background: transparent; |
| | | font-size: 13px; |
| | | border: 1px solid rgba(255, 255, 255, 0.06) !important; |
| | | background: linear-gradient(128deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.01)); |
| | | |
| | | &:hover { |
| | | background: rgba(var(--el-color-primary-rgb, 37, 99, 235), 0.12) !important; |
| | | transform: translateX(4px); |
| | | background: linear-gradient(128deg, rgba(var(--el-color-primary-rgb, 37, 99, 235), 0.24), rgba(var(--el-color-primary-rgb, 37, 99, 235), 0.07)) !important; |
| | | border-color: rgba(var(--el-color-primary-rgb, 37, 99, 235), 0.3) !important; |
| | | transform: translateX(2px); |
| | | } |
| | | |
| | | &.is-active { |
| | | background: rgba(var(--el-color-primary-rgb, 37, 99, 235), 0.85) !important; |
| | | background: var(--menu-active-bg, linear-gradient(135deg, var(--el-color-primary), var(--el-color-primary-light-3))) !important; |
| | | background-size: 180% 180%; |
| | | color: #fff !important; |
| | | font-weight: 500; |
| | | box-shadow: 0 4px 12px rgba(var(--el-color-primary-rgb, 37, 99, 235), 0.25); |
| | | box-shadow: var(--menu-active-glow, 0 10px 24px rgba(var(--el-color-primary-rgb, 37, 99, 235), 0.34)); |
| | | animation: sidebarActiveFlow 4.6s ease infinite; |
| | | } |
| | | } |
| | | |
| | | // åèå容卿 ·å¼ - å¢å 缩è¿åè§è§å±æ¬¡ |
| | | & .el-sub-menu .el-menu { |
| | | padding: 4px 0 8px; |
| | | margin-left: 8px; |
| | | border-left: 2px solid rgba(255, 255, 255, 0.08); |
| | | } |
| | | } |
| | | |
| | |
| | | allowFileUpload: false, |
| | | emptySessionText: 'ææ è´¢å¡ä¼è¯', |
| | | quickPrompts: [ |
| | | 'æ¥çç»è¥é©¾é©¶è±', |
| | | 'æ¥çæ¬æç»è¥é©¾é©¶è±', |
| | | 'çææ¬å¨ç»è¥å¨æ¥ï¼å©æ¶¦ä¸ç°éæµï¼', |
| | | 'åææ¬æå©æ¶¦ä¸éåå ', |
| | | 'æ¥è¯¢è¿30å¤©äºæè®¢å', |
| | | 'è¿30天åªä¸ªå®¢æ·å©æ¶¦è´¡ç®æé«', |
| | | 'æ¥çæ¬æç»è¥é©¾é©¶è±', |
| | | 'æ¥è¯¢è¿30å¤©äºæè®¢å', |
| | | 'åæè¿30天åºåèµéå ç¨', |
| | | '颿µæªæ¥3个æç°éæµ', |
| | | 'æ¥è¯¢åºæ¶å款é£é©', |
| | | 'åªä¸ªå·¥åºææ¬æé«' |
| | | ] |
| | | } |
| | |
| | | allowFileUpload: false, |
| | | emptySessionText: 'ææ ç产ä¼è¯', |
| | | quickPrompts: [ |
| | | 'æ¥è¯¢ç产ç°åºæ°æ®', |
| | | 'æ¥è¯¢çäº§è®¡åæ§è¡æ
åµ', |
| | | 'æ¥è¯¢å·¥åæ§è¡æ
åµ', |
| | | 'æ¥è¯¢è®¾å¤è¿è¡æ
åµ', |
| | | 'æ¥è¯¢è´¨éå¼å¸¸æ°æ®', |
| | | 'æ¥è¯¢ç©æç¸å
³æ°æ®', |
| | | 'æ¥çå¶é é¢è¦çæ¿', |
| | | 'åææ¬æå¶é ç»è¥æ
åµ', |
| | | 'åæè¿7天ç产å¼å¸¸', |
| | | 'çæå¶é åç建议' |
| | | 'æ¥è¯¢æ¬æç产计å', |
| | | 'æ¥çæè¿10æ¡å·¥å', |
| | | 'æ¥è®¾å¤A-01çç»´ä¿®æ
åµ', |
| | | 'æ¥è´¨éä¸åæ ¼è®°å½', |
| | | 'æ¥ä½åºåç©æ', |
| | | 'æ¥è¿7天å¼å¸¸å¤ç', |
| | | 'çæå¶é é¢è¦çæ¿', |
| | | 'åææ¬æçäº§å®æçåå¼å¸¸ç', |
| | | 'ç»åºå·¥å龿å设å¤å¾
ä¿®çåç建议' |
| | | ] |
| | | } |
| | |
| | | fileAnalyzeUrl: '/purchase-ai/analyze-files', |
| | | emptySessionText: 'ææ éè´ä¼è¯', |
| | | quickPrompts: [ |
| | | 'æ¥è¯¢éè´å°è´¦å表', |
| | | 'æ¥è¯¢æè¿10æ¡éè´å°è´¦', |
| | | 'ç»è®¡æ¬æéè´æ°æ®', |
| | | 'æ¥è¯¢æ¬æéè´ç©æé颿è¡', |
| | | 'æ¥è¯¢æªå
¥åºéè´è®¢å', |
| | | 'æ¥è¯¢éè´å°è´§å¼å¸¸', |
| | | 'æ¥è¯¢å¾
仿¬¾éè´å', |
| | | 'æ¥è¯¢æ¬æéè´éè´§æ
åµ', |
| | | 'æ¥è¯¢æä¸ªéè´å°è´¦è¯¦æ
', |
| | | 'åæä¾åºåéè´é颿å' |
| | | 'æ¬æéè´é颿åååçç©ææåªäºï¼', |
| | | 'åªäºéè´è®¢åè¿æªå
¥åºï¼', |
| | | 'æè¿7天ä¾åºåå°è´§å¼å¸¸æåªäºï¼', |
| | | '帮æç»è®¡å¾
仿¬¾éè´åï¼', |
| | | 'ååºæ¬æéè´éè´§æ
åµ' |
| | | ] |
| | | } |
| | |
| | | allowFileUpload: false, |
| | | emptySessionText: 'ææ éå®ä¼è¯', |
| | | quickPrompts: [ |
| | | 'æ¥è¯¢ç§æµ·å®¢æ·æ¡£æ¡å10æ¡', |
| | | 'æ¥è¯¢å
¬æµ·å®¢æ·æ¡£æ¡', |
| | | 'æ¥è¯¢é宿¡£æ¡å10æ¡', |
| | | 'æ¥è¯¢å®¢æ·æ¡£æ¡', |
| | | 'æ¥è¯¢æ¬æé宿¥ä»·', |
| | | 'æ¥è¯¢æ¬æéå®å°è´¦', |
| | | 'æ¥è¯¢è¿30天éå®éè´§', |
| | |
| | | clearInitTimer() |
| | | }) |
| | | |
| | | defineExpose({ |
| | | resize: windowResizeListener, |
| | | }) |
| | | |
| | | // Watch all reactive props that affect the chart |
| | | watch( |
| | | () => [props.xAxis, props.yAxis, props.series, props.legend, props.tooltip, props.visualMap], |
| | |
| | | :highlight-current-row="highlightCurrentRow" |
| | | :row-class-name="rowClassName" |
| | | :row-style="rowStyle" |
| | | :cell-style="cellStyle" |
| | | :row-key="rowKey" |
| | | :style="tableStyle" |
| | | tooltip-effect="dark" |
| | | :tooltip-options="{ popperOptions: { strategy: 'absolute' } }" |
| | | :tooltip-options="{ appendTo: 'body' }" |
| | | :expand-row-keys="expandRowKeys" |
| | | :show-summary="isShowSummary" |
| | | :summary-method="summaryMethod" |
| | |
| | | <el-button v-show="o.type != 'upload'" |
| | | v-if="o.showHide ? o.showHide(scope.row) : true" |
| | | :disabled="isOperationDisabled(o, scope.row)" |
| | | :loading="isOperationLoading(o, scope.row)" |
| | | :plain="o.plain" |
| | | type="primary" |
| | | :style="{ |
| | |
| | | type: [Object, Function], |
| | | default: () => ({}), |
| | | }, |
| | | cellStyle: { |
| | | type: [Object, Function], |
| | | default: () => ({}), |
| | | }, |
| | | tableData: { |
| | | type: Array, |
| | | default: () => [], |
| | |
| | | : !!operation.disabled; |
| | | }; |
| | | |
| | | const isOperationLoading = (operation, row) => { |
| | | if (!operation?.loading) return false; |
| | | return typeof operation.loading === "function" |
| | | ? !!operation.loading(row) |
| | | : !!operation.loading; |
| | | }; |
| | | |
| | | const parseHexToRgb = hex => { |
| | | const normalized = String(hex || "") |
| | | .trim() |
| | |
| | | }, |
| | | pageSizes: { |
| | | type: Array, |
| | | default: () => [10, 20, 30, 50, 100] |
| | | default: () => [10, 20, 30, 50, 100, 200, 500] |
| | | }, |
| | | pagerCount: { |
| | | type: Number, |
| | |
| | | size: paramPage.value.size, |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | const existingParamIds = new Set( |
| | | (props.paramList || []) |
| | | .map(item => Number(item.paramId || item.technologyOperationParamId)) |
| | | .filter(id => Number.isFinite(id) && id > 0) |
| | | ); |
| | | filteredParamList.value = (res.data?.records || []).filter( |
| | | item => !existingParamIds.has(Number(item.id)) |
| | | ); |
| | | filteredParamList.value = res.data?.records || []; |
| | | paramPage.value.total = res.data.total || 0; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | |
| | | addProcessRouteItemParamOrder({ |
| | | productionOrderId: Number(props.orderId), |
| | | productionOrderRoutingOperationId: props.process.id, |
| | | technologyRoutingOperationParamId: props.process.id, |
| | | paramId: selectedParam.value.id, |
| | | standardValue: selectedParam.value.standardValue || "", |
| | | isRequired: selectedParam.value.isRequired || 0, |
| | |
| | |
|
| | | :deep(.nest-menu) {
|
| | | .menu-icon {
|
| | | width: 18px;
|
| | | height: 18px;
|
| | | margin-right: 8px;
|
| | | opacity: 0.7;
|
| | | width: 22px; |
| | | height: 22px; |
| | | margin-right: 10px; |
| | | }
|
| | |
|
| | | .menu-title { |
| | | font-size: 13px;
|
| | | opacity: 0.85;
|
| | | }
|
| | | |
| | | .el-menu-item.is-active {
|
| | | .menu-icon {
|
| | | opacity: 1;
|
| | | }
|
| | | .menu-title {
|
| | | opacity: 1;
|
| | | }
|
| | | }
|
| | | .menu-title { font-size: 13px; } |
| | | }
|
| | | }
|
| | | </style>
|
| | |
| | | size: Cookies.get("size") || "default", |
| | | }); |
| | | app._context.components.ElDialog.props.closeOnClickModal.default = false; |
| | | const ElTable = app._context.components.ElTable; |
| | | if (ElTable?.props?.tooltipOptions) { |
| | | if (ElTable.props.tooltipOptions === Object) { |
| | | ElTable.props.tooltipOptions = { |
| | | type: Object, |
| | | default: () => ({ |
| | | appendTo: "body", |
| | | }), |
| | | }; |
| | | } else if (typeof ElTable.props.tooltipOptions === "object") { |
| | | ElTable.props.tooltipOptions.default = |
| | | ElTable.props.tooltipOptions.default || |
| | | (() => ({ |
| | | appendTo: "body", |
| | | })); |
| | | } |
| | | } |
| | | |
| | | app.mount("#app"); |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | // æå¦æ¼ç¤ºæ¨¡å |
| | | { |
| | | path: "/productionManagement/teachingDemo", |
| | | component: Layout, |
| | | hidden: true, |
| | | children: [ |
| | | { |
| | | path: "", |
| | | component: () => import("@/views/productionManagement/teachingDemo/index.vue"), |
| | | name: "TeachingDemo", |
| | | meta: { title: "å·¥èºè·¯çº¿ä¸BOMæå¦æ¼ç¤º", icon: "education" }, |
| | | }, |
| | | { |
| | | path: "bom", |
| | | component: () => import("@/views/productionManagement/teachingDemo/BomDemo.vue"), |
| | | name: "BomDemo", |
| | | meta: { title: "BOMç»ææ¼ç¤º", activeMenu: "/productionManagement/teachingDemo" }, |
| | | }, |
| | | { |
| | | path: "processRoute", |
| | | component: () => import("@/views/productionManagement/teachingDemo/ProcessRouteDemo.vue"), |
| | | name: "ProcessRouteDemo", |
| | | meta: { title: "å·¥èºè·¯çº¿æ¼ç¤º", activeMenu: "/productionManagement/teachingDemo" }, |
| | | }, |
| | | { |
| | | path: "combined", |
| | | component: () => import("@/views/productionManagement/teachingDemo/CombinedDemo.vue"), |
| | | name: "CombinedDemo", |
| | | meta: { title: "è卿¼ç¤º", activeMenu: "/productionManagement/teachingDemo" }, |
| | | }, |
| | | { |
| | | path: "factory", |
| | | component: () => import("@/views/productionManagement/teachingDemo/FactoryDemo.vue"), |
| | | name: "FactoryDemo", |
| | | meta: { title: "å¨æå·¥åæ¼ç¤º", activeMenu: "/productionManagement/teachingDemo" }, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | path: "/user", |
| | | component: Layout, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import html2canvas from 'html2canvas' |
| | | |
| | | function esc(s) { |
| | | if (!s) return '' |
| | | return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') |
| | | } |
| | | |
| | | function fmtDate(v) { |
| | | if (!v) return '' |
| | | const d = new Date(v) |
| | | const y = d.getFullYear() |
| | | const m = String(d.getMonth() + 1).padStart(2, '0') |
| | | const day = String(d.getDate()).padStart(2, '0') |
| | | return y + '-' + m + '-' + day |
| | | } |
| | | |
| | | function fmtMoney(v) { |
| | | if (v == null) return '0.00' |
| | | return Number(v).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) |
| | | } |
| | | |
| | | function digitUppercase(n) { |
| | | const digits = ['é¶', '壹', 'è´°', 'å', 'è', 'ä¼', 'é', 'æ', 'æ', 'ç'] |
| | | const radices = ['', 'æ¾', 'ä½°', 'ä»', 'ä¸', 'æ¾', 'ä½°', 'ä»', '亿'] |
| | | let num = Math.abs(n) |
| | | let result = '' |
| | | const intPart = Math.floor(num) |
| | | const intStr = String(intPart) |
| | | const len = intStr.length |
| | | if (intPart === 0) { |
| | | result = 'é¶' |
| | | } else { |
| | | let zeroFlag = false |
| | | for (let i = 0; i < len; i++) { |
| | | const d = parseInt(intStr[i]) |
| | | const pos = len - 1 - i |
| | | if (d === 0) { |
| | | zeroFlag = true |
| | | if (pos % 4 === 0) { |
| | | result += radices[pos] |
| | | zeroFlag = false |
| | | } |
| | | } else { |
| | | if (zeroFlag) { |
| | | result += 'é¶' |
| | | zeroFlag = false |
| | | } |
| | | result += digits[d] + radices[pos] |
| | | } |
| | | } |
| | | if (result.endsWith('é¶')) result = result.slice(0, -1) |
| | | } |
| | | result += 'å
' |
| | | const decPart = Math.round((num - intPart) * 100) |
| | | if (decPart === 0) { |
| | | result += 'æ´' |
| | | } else { |
| | | const jiao = Math.floor(decPart / 10) |
| | | const fen = decPart % 10 |
| | | if (jiao > 0) result += digits[jiao] + 'è§' |
| | | if (fen > 0) result += digits[fen] + 'å' |
| | | } |
| | | return result |
| | | } |
| | | |
| | | function coreCss() { |
| | | return `*{margin:0;padding:0;box-sizing:border-box}body{font-family:"Microsoft YaHei","SimSun",sans-serif;padding:30px 36px;color:#333;background:#fff}.title{text-align:center;font-size:18px;font-weight:bold;letter-spacing:6px;margin-bottom:4px}.sub{text-align:center;font-size:12px;color:#999;margin-bottom:18px}.info-top{display:flex;justify-content:space-between;font-size:13px;margin-bottom:14px;padding-bottom:10px;border-bottom:1px dashed #ccc}.info-top .left,.info-top .right{line-height:2}.info-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 36px;font-size:13px;margin-bottom:14px;padding-bottom:8px;border-bottom:1px solid #ccc}.info-grid .l{color:#666}.info-grid .v{font-weight:500}table{width:100%;border-collapse:collapse;font-size:13px;margin-bottom:14px}th,td{border:1px solid #333;padding:5px 6px;text-align:center}th{background:#eee;font-weight:500}td.r{text-align:right}.summary{text-align:right;font-size:13px;margin-bottom:16px;line-height:2.2}.summary .cn{font-weight:bold;color:#cf1322}` |
| | | } |
| | | |
| | | function productRows(products) { |
| | | if (!products || !products.length) return '' |
| | | let html = '' |
| | | products.forEach((p, i) => { |
| | | const name = p.productCategory || '' |
| | | const spec = p.specificationModel || '' |
| | | html += '<tr>' |
| | | html += '<td>' + (i + 1) + '</td>' |
| | | html += '<td style="text-align:left">' + esc(name) |
| | | if (spec) html += '<br><span style="color:#888;font-size:12px">' + esc(spec) + '</span>' |
| | | html += '</td>' |
| | | html += '<td>' + esc(p.unit || '') + '</td>' |
| | | html += '<td class="r">' + (p.quantity != null ? Number(p.quantity).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : '') + '</td>' |
| | | html += '<td class="r">' + fmtMoney(p.taxInclusiveUnitPrice) + '</td>' |
| | | html += '<td class="r">' + fmtMoney(p.taxInclusiveTotalPrice) + '</td>' |
| | | html += '<td>' + (p.taxRate != null ? p.taxRate + '%' : '') + '</td>' |
| | | html += '<td>' + esc(p.invoiceType || '') + '</td>' |
| | | html += '</tr>' |
| | | }) |
| | | return html |
| | | } |
| | | |
| | | function calcTotals(products) { |
| | | let total = 0, totalNoTax = 0 |
| | | if (products) { |
| | | products.forEach(p => { |
| | | if (p.taxInclusiveTotalPrice != null) total += Number(p.taxInclusiveTotalPrice) |
| | | if (p.taxExclusiveTotalPrice != null) totalNoTax += Number(p.taxExclusiveTotalPrice) |
| | | }) |
| | | } |
| | | return { total, totalNoTax } |
| | | } |
| | | |
| | | function buildSalesHtml(companyName, ledger, forPrint) { |
| | | const products = ledger.productData || [] |
| | | const { total, totalNoTax } = calcTotals(products) |
| | | const toolbar = forPrint ? '<div class="toolbar"><button onclick="window.print()">æå° / å¦å为 PDF</button></div>' : '' |
| | | const printCss = forPrint ? '@media print{body{padding:15px 24px}.toolbar{display:none}}' : '' |
| | | const printScript = forPrint ? '<script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script>' : '' |
| | | |
| | | return `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"> |
| | | <title>éå®å - ${esc(ledger.salesContractNo)} - ${esc(companyName)}</title> |
| | | <style>.toolbar{text-align:center;margin-bottom:16px}.toolbar button{padding:6px 20px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px}.toolbar button:hover{background:#40a9ff}${coreCss()}${printCss}</style></head><body> |
| | | ${toolbar} |
| | | <div class="title">${esc(companyName)}</div> |
| | | <div class="sub">éå®åå / éå®å</div> |
| | | <div class="info-top"> |
| | | <div class="left"><div><strong>éæ¹åç§°ï¼</strong>${esc(companyName)}</div></div> |
| | | <div class="right"><div><strong>ååç¼å·ï¼</strong>${esc(ledger.salesContractNo)}</div><div><strong>ç¾è®¢æ¥æï¼</strong>${fmtDate(ledger.executionDate)}</div></div> |
| | | </div> |
| | | <div class="info-grid"> |
| | | <div><span class="l">客æ·åç§°ï¼</span><span class="v">${esc(ledger.customerName)}</span></div> |
| | | <div><span class="l">项ç®åç§°ï¼</span><span class="v">${esc(ledger.projectName)}</span></div> |
| | | <div><span class="l">ä¸å¡åï¼</span><span class="v">${esc(ledger.salesman)}</span></div> |
| | | <div><span class="l">仿¬¾æ¹å¼ï¼</span><span class="v">${esc(ledger.paymentMethod)}</span></div> |
| | | <div><span class="l">å½å
¥æ¥æï¼</span><span class="v">${fmtDate(ledger.entryDate)}</span></div> |
| | | <div><span class="l">äº¤è´§æ¥æï¼</span><span class="v">${fmtDate(ledger.deliveryDate)}</span></div> |
| | | ${ledger.remarks ? `<div style="grid-column:1/-1"><span class="l">夿³¨ï¼</span><span class="v">${esc(ledger.remarks)}</span></div>` : ''} |
| | | </div> |
| | | <table><thead><tr><th>åºå·</th><th>产ååç§° / è§æ ¼åå·</th><th>åä½</th><th>æ°é</th><th>å«ç¨åä»·</th><th>å«ç¨æ»ä»·</th><th>ç¨ç</th><th>å票类å</th></tr></thead><tbody> |
| | | ${productRows(products)} |
| | | </tbody></table> |
| | | <div class="summary"> |
| | | <div>ä¸å«ç¨å计ï¼<strong>${fmtMoney(totalNoTax)}</strong></div> |
| | | <div>å«ç¨å计ï¼å¤§åï¼ï¼<span class="cn">${digitUppercase(total)}</span></div> |
| | | <div>å«ç¨å计ï¼å°åï¼ï¼<strong>Â¥${fmtMoney(total)}</strong></div> |
| | | </div> |
| | | ${printScript} |
| | | </body></html>` |
| | | } |
| | | |
| | | function buildPurchaseHtml(companyName, ledger, products, forPrint) { |
| | | const { total, totalNoTax } = calcTotals(products) |
| | | const contractAmount = ledger.contractAmount != null ? Number(ledger.contractAmount) : total |
| | | const toolbar = forPrint ? '<div class="toolbar"><button onclick="window.print()">æå° / å¦å为 PDF</button></div>' : '' |
| | | const printCss = forPrint ? '@media print{body{padding:15px 24px}.toolbar{display:none}}' : '' |
| | | const printScript = forPrint ? '<script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script>' : '' |
| | | |
| | | return `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"> |
| | | <title>éè´å - ${esc(ledger.purchaseContractNumber)} - ${esc(companyName)}</title> |
| | | <style>.toolbar{text-align:center;margin-bottom:16px}.toolbar button{padding:6px 20px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px}.toolbar button:hover{background:#40a9ff}${coreCss()}${printCss}</style></head><body> |
| | | ${toolbar} |
| | | <div class="title">${esc(companyName)}</div> |
| | | <div class="info-top"> |
| | | <div class="left"><div><strong>è´æ¹åç§°ï¼</strong>${esc(companyName)}</div></div> |
| | | <div class="right"><div><strong>ååç¼å·ï¼</strong>${esc(ledger.purchaseContractNumber)}</div><div><strong>ç¾è®¢æ¥æï¼</strong>${fmtDate(ledger.executionDate)}</div></div> |
| | | </div> |
| | | <div class="info-grid"> |
| | | <div><span class="l">ä¾åºååç§°ï¼</span><span class="v">${esc(ledger.supplierName)}</span></div> |
| | | <div><span class="l">项ç®åç§°ï¼</span><span class="v">${esc(ledger.projectName)}</span></div> |
| | | <div><span class="l">å½å
¥äººï¼</span><span class="v">${esc(ledger.recorderName)}</span></div> |
| | | <div><span class="l">仿¬¾æ¹å¼ï¼</span><span class="v">${esc(ledger.paymentMethod)}</span></div> |
| | | <div><span class="l">å½å
¥æ¥æï¼</span><span class="v">${fmtDate(ledger.entryDate)}</span></div> |
| | | ${ledger.salesContractNo ? `<div><span class="l">å
³èéå®ååå·ï¼</span><span class="v">${esc(ledger.salesContractNo)}</span></div>` : ''} |
| | | ${ledger.remarks ? `<div style="grid-column:1/-1"><span class="l">夿³¨ï¼</span><span class="v">${esc(ledger.remarks)}</span></div>` : ''} |
| | | </div> |
| | | <table><thead><tr><th>åºå·</th><th>产ååç§° / è§æ ¼åå·</th><th>åä½</th><th>æ°é</th><th>å«ç¨åä»·</th><th>å«ç¨æ»ä»·</th><th>ç¨ç</th><th>å票类å</th></tr></thead><tbody> |
| | | ${productRows(products)} |
| | | </tbody></table> |
| | | <div class="summary"> |
| | | <div>ä¸å«ç¨å计ï¼<strong>${fmtMoney(totalNoTax)}</strong></div> |
| | | <div>å«ç¨å计ï¼å¤§åï¼ï¼<span class="cn">${digitUppercase(total)}</span></div> |
| | | <div>å«ç¨å计ï¼å°åï¼ï¼<strong>Â¥${fmtMoney(total)}</strong></div> |
| | | <div>ååéé¢ï¼å¤§åï¼ï¼<span class="cn">${digitUppercase(contractAmount)}</span></div> |
| | | <div>ååéé¢ï¼å°åï¼ï¼<strong>Â¥${fmtMoney(contractAmount)}</strong></div> |
| | | </div> |
| | | ${printScript} |
| | | </body></html>` |
| | | } |
| | | |
| | | export function buildSalesDocument(companyName, ledger) { |
| | | return buildSalesHtml(companyName, ledger, false) |
| | | } |
| | | |
| | | export function buildPurchaseDocument(companyName, ledger, products) { |
| | | return buildPurchaseHtml(companyName, ledger, products, false) |
| | | } |
| | | |
| | | function buildSalesPrintHtml(companyName, ledger) { |
| | | return buildSalesHtml(companyName, ledger, true) |
| | | } |
| | | |
| | | function buildPurchasePrintHtml(companyName, ledger, products) { |
| | | return buildPurchaseHtml(companyName, ledger, products, true) |
| | | } |
| | | |
| | | export function openPrintWindow(html) { |
| | | const w = window.open('', '_blank', 'width=900,height=700') |
| | | if (!w) return |
| | | w.document.write(html) |
| | | w.document.close() |
| | | } |
| | | |
| | | export async function downloadAsJpg(html, filename) { |
| | | const iframe = document.createElement('iframe') |
| | | iframe.style.position = 'fixed' |
| | | iframe.style.left = '0' |
| | | iframe.style.top = '0' |
| | | iframe.style.width = '900px' |
| | | iframe.style.height = '600px' |
| | | iframe.style.opacity = '0' |
| | | iframe.style.pointerEvents = 'none' |
| | | iframe.style.zIndex = '99999' |
| | | document.body.appendChild(iframe) |
| | | |
| | | return new Promise((resolve, reject) => { |
| | | const iframeDoc = iframe.contentDocument || iframe.contentWindow.document |
| | | iframeDoc.open() |
| | | iframeDoc.write(html) |
| | | iframeDoc.close() |
| | | |
| | | const doCapture = async () => { |
| | | try { |
| | | const body = iframeDoc.body |
| | | iframe.style.height = Math.max(body.scrollHeight, 600) + 'px' |
| | | await new Promise(r => setTimeout(r, 100)) |
| | | const canvas = await html2canvas(body, { |
| | | scale: 2, |
| | | useCORS: true, |
| | | backgroundColor: '#ffffff', |
| | | windowWidth: 900 |
| | | }) |
| | | document.body.removeChild(iframe) |
| | | canvas.toBlob(blob => { |
| | | const url = URL.createObjectURL(blob) |
| | | const a = document.createElement('a') |
| | | a.href = url |
| | | a.download = filename |
| | | a.click() |
| | | URL.revokeObjectURL(url) |
| | | resolve() |
| | | }, 'image/jpeg', 0.95) |
| | | } catch (e) { |
| | | document.body.removeChild(iframe) |
| | | reject(e) |
| | | } |
| | | } |
| | | |
| | | iframe.onload = () => setTimeout(doCapture, 300) |
| | | setTimeout(doCapture, 1000) |
| | | }) |
| | | } |
| | | |
| | | export function downloadSalesJpg(companyName, ledger, filename) { |
| | | const html = buildSalesHtml(companyName, ledger, false) |
| | | return downloadAsJpg(html, filename) |
| | | } |
| | | |
| | | export function downloadPurchaseJpg(companyName, ledger, products, filename) { |
| | | const html = buildPurchaseHtml(companyName, ledger, products, false) |
| | | return downloadAsJpg(html, filename) |
| | | } |
| | | |
| | | export function printSales(companyName, ledger) { |
| | | const html = buildSalesPrintHtml(companyName, ledger) |
| | | openPrintWindow(html) |
| | | } |
| | | |
| | | export function printPurchase(companyName, ledger, products) { |
| | | const html = buildPurchasePrintHtml(companyName, ledger, products) |
| | | openPrintWindow(html) |
| | | } |
| | | |
| | | export function printSalesMulti(companyName, orders) { |
| | | let bodies = '' |
| | | orders.forEach((ledger, i) => { |
| | | const html = buildSalesHtml(companyName, ledger, true) |
| | | const bodyMatch = html.match(/<body>([\s\S]*)<\/body>/) |
| | | if (bodyMatch) { |
| | | const body = bodyMatch[1].replace(/<script>[\s\S]*?<\/script>/g, '') |
| | | bodies += '<div class="print-page' + (i < orders.length - 1 ? ' page-break' : '') + '">' + body + '</div>' |
| | | } |
| | | }) |
| | | const combined = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"> |
| | | <title>éå®å - ${esc(companyName)}</title> |
| | | <style> |
| | | *{margin:0;padding:0;box-sizing:border-box} |
| | | body{font-family:"Microsoft YaHei","SimSun",sans-serif;padding:20px 36px;color:#333;background:#fff} |
| | | .toolbar{text-align:center;margin-bottom:20px} |
| | | .toolbar button{padding:6px 24px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px} |
| | | .toolbar button:hover{background:#40a9ff} |
| | | ${coreCss()} |
| | | .print-page{page-break-inside:avoid} |
| | | .page-break{page-break-after:always} |
| | | @media print{body{padding:15px 24px}.toolbar{display:none}} |
| | | </style></head><body> |
| | | <div class="toolbar"><button onclick="window.print()">æå°å
¨é¨ (å
±${orders.length}份)</button></div> |
| | | ${bodies} |
| | | <script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script> |
| | | </body></html>` |
| | | openPrintWindow(combined) |
| | | } |
| | | |
| | | export function printPurchaseMulti(companyName, orders) { |
| | | let bodies = '' |
| | | orders.forEach((item, i) => { |
| | | const html = buildPurchaseHtml(companyName, item.ledger, item.products, true) |
| | | const bodyMatch = html.match(/<body>([\s\S]*)<\/body>/) |
| | | if (bodyMatch) { |
| | | const body = bodyMatch[1].replace(/<script>[\s\S]*?<\/script>/g, '') |
| | | bodies += '<div class="print-page' + (i < orders.length - 1 ? ' page-break' : '') + '">' + body + '</div>' |
| | | } |
| | | }) |
| | | const combined = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"> |
| | | <title>éè´å - ${esc(companyName)}</title> |
| | | <style> |
| | | *{margin:0;padding:0;box-sizing:border-box} |
| | | body{font-family:"Microsoft YaHei","SimSun",sans-serif;padding:20px 36px;color:#333;background:#fff} |
| | | .toolbar{text-align:center;margin-bottom:20px} |
| | | .toolbar button{padding:6px 24px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px} |
| | | .toolbar button:hover{background:#40a9ff} |
| | | ${coreCss()} |
| | | .print-page{page-break-inside:avoid} |
| | | .page-break{page-break-after:always} |
| | | @media print{body{padding:15px 24px}.toolbar{display:none}} |
| | | </style></head><body> |
| | | <div class="toolbar"><button onclick="window.print()">æå°å
¨é¨ (å
±${orders.length}份)</button></div> |
| | | ${bodies} |
| | | <script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script> |
| | | </body></html>` |
| | | openPrintWindow(combined) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** éé¢ç±»å段é»è®¤æå¤§å°æ°ä½æ° */ |
| | | export const AMOUNT_DECIMAL_SCALE = 6 |
| | | |
| | | /** |
| | | * æ ¼å¼åæ°åï¼æå¤ä¿ç scale ä½å°æ°ï¼å¹¶å»é¤æ«å°¾å¤ä½ç 0 |
| | | */ |
| | | export function formatDecimal(value, scale = AMOUNT_DECIMAL_SCALE, fallback = '0') { |
| | | if (value === null || value === undefined || value === '') { |
| | | return fallback |
| | | } |
| | | const num = Number(value) |
| | | if (Number.isNaN(num)) { |
| | | return fallback |
| | | } |
| | | return parseFloat(num.toFixed(scale)).toString() |
| | | } |
| | | |
| | | /** 带货å¸ç¬¦å·çéé¢å±ç¤º */ |
| | | export function formatCurrency(value, scale = AMOUNT_DECIMAL_SCALE) { |
| | | return `Â¥${formatDecimal(value, scale)}` |
| | | } |
| | | |
| | | /** el-table-column formatter */ |
| | | export function tableAmountFormatter(row, column, cellValue) { |
| | | return formatDecimal(cellValue) |
| | | } |
| | | |
| | | /** 计ç®åä¿çæå®ä½å°æ° */ |
| | | export function roundAmount(value, scale = AMOUNT_DECIMAL_SCALE) { |
| | | if (value === null || value === undefined || value === '') { |
| | | return 0 |
| | | } |
| | | const num = Number(value) |
| | | if (Number.isNaN(num)) { |
| | | return 0 |
| | | } |
| | | return parseFloat(num.toFixed(scale)) |
| | | } |
| | | |
| | | /** æå»º summarizeTable éé¢åæ ¼å¼é
ç½® */ |
| | | export function buildAmountSummaryFormat(fields, extraFormat = {}) { |
| | | const format = { ...extraFormat } |
| | | fields.forEach(field => { |
| | | format[field] = { decimalPlaces: AMOUNT_DECIMAL_SCALE } |
| | | }) |
| | | return format |
| | | } |
| | |
| | | import { formatDecimal, AMOUNT_DECIMAL_SCALE } from './numberFormat' |
| | | |
| | | /** |
| | | * éç¨çè¡¨æ ¼åè®¡æ¹æ³ |
| | | * @param {Object} param - å
å«è¡¨æ ¼åé
ç½®åæ°æ®æºç对象 |
| | |
| | | // 妿æå®äºä¸éè¦ä¿çå°æ°ï¼åç´æ¥è½¬æ¢ä¸ºæ´æ° |
| | | sums[index] = Math.round(sum).toString(); |
| | | } else { |
| | | // é»è®¤ä¿ç两ä½å°æ° |
| | | sums[index] = parseFloat(sum).toFixed( |
| | | sums[index] = formatDecimal( |
| | | sum, |
| | | specialFormat[prop]?.decimalPlaces ?? 2 |
| | | ); |
| | | } |
| | | } else { |
| | | sums[index] = ""; |
| | | sums[index] = ''; |
| | | } |
| | | } else { |
| | | sums[index] = ""; |
| | | sums[index] = ''; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | // ä¸å«ç¨æ»ä»·è®¡ç® |
| | | const calculateTaxExclusiveTotalPrice = (taxInclusiveTotalPrice, taxRate) => { |
| | | const taxRateDecimal = taxRate / 100; |
| | | return (taxInclusiveTotalPrice / (1 + taxRateDecimal)).toFixed(2); |
| | | return formatDecimal( |
| | | Number(taxInclusiveTotalPrice) / (1 + taxRateDecimal), |
| | | AMOUNT_DECIMAL_SCALE |
| | | ); |
| | | }; |
| | | // å«ç¨æ»ä»·è®¡ç® |
| | | const calculateTaxIncludeTotalPrice = (taxInclusiveUnitPrice, quantity) => { |
| | | return (taxInclusiveUnitPrice * quantity).toFixed(2); |
| | | return formatDecimal( |
| | | Number(taxInclusiveUnitPrice) * Number(quantity), |
| | | AMOUNT_DECIMAL_SCALE |
| | | ); |
| | | }; |
| | | // 导åºå½æ°ä¾å
¶ä»æä»¶ä½¿ç¨ |
| | | export { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form" style="margin-bottom: 20px;"> |
| | | <div class="search_form" |
| | | style="margin-bottom: 20px;"> |
| | | <div> |
| | | <span class="search_title">客æ·åç§°ï¼</span> |
| | | <el-input v-model="searchForm.customerName" |
| | |
| | | @change="handleQuery"> |
| | | <el-option label="é¶å®å®¢æ·" |
| | | value="é¶å®å®¢æ·" /> |
| | | <el-option label="è¿éå客æ·" |
| | | value="è¿éå客æ·" /> |
| | | <el-option label="ç»éå客æ·" |
| | | value="ç»éå客æ·" /> |
| | | </el-select> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | |
| | | clearable> |
| | | <el-option label="é¶å®å®¢æ·" |
| | | value="é¶å®å®¢æ·" /> |
| | | <el-option label="è¿éå客æ·" |
| | | value="è¿éå客æ·" /> |
| | | <el-option label="ç»éå客æ·" |
| | | value="ç»éå客æ·" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitReminderForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitReminderForm">确认</el-button> |
| | | <el-button @click="closeReminderDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitNegotiationForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitNegotiationForm">确认</el-button> |
| | | <el-button @click="closeNegotiationDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | <span class="info-value">{{ detailForm.companyAddress }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <!-- <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">é¶è¡åºæ¬æ·ï¼</span> |
| | | <span class="info-value">{{ detailForm.basicBankAccount }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-col> --> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <!-- <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">é¶è¡è´¦å·ï¼</span> |
| | |
| | | <span class="info-value">{{ detailForm.bankCode }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-row> --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | |
| | | </div> |
| | | </div> |
| | | <!-- æ´½è°è¿åº¦è®°å½ --> |
| | | <div class="detail-section"> |
| | | <!-- <div class="detail-section"> |
| | | <div class="section-header"> |
| | | <h3 class="section-title">æ´½è°è¿åº¦è®°å½</h3> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openNegotiationDialog(detailForm)"> |
| | | æ·»å è¿åº¦ |
| | | </el-button> |
| | | </div> |
| | | <el-table :data="negotiationRecords" |
| | | border |
| | |
| | | <Paperclip /> |
| | | </el-icon> |
| | | éä»¶ |
| | | <!-- {{ row.fileList && row.fileList.length > 0 ? row.fileList.length : 'ä¸ä¼ ' }} --> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | class="no-records"> |
| | | ææ æ´½è°è¿åº¦è®°å½ |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDetailDialog">å
³é</el-button> |
| | |
| | | addReturnVisit, |
| | | getReturnVisit, |
| | | } from "@/api/basicData/customerFile.js"; |
| | | import {listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer, backCustomer} from "@/api/basicData/customer.js"; |
| | | import { |
| | | listCustomer, |
| | | getCustomer, |
| | | addCustomer, |
| | | updateCustomer, |
| | | delCustomer, |
| | | backCustomer, |
| | | } from "@/api/basicData/customer.js"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | |
| | | prop: "contactPhone", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "è·è¿è¿åº¦", |
| | | prop: "followUpLevel", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "è·è¿æ¶é´", |
| | | prop: "followUpTime", |
| | | width: 120, |
| | | }, |
| | | // { |
| | | // label: "è·è¿è¿åº¦", |
| | | // prop: "followUpLevel", |
| | | // width: 120, |
| | | // }, |
| | | // { |
| | | // label: "è·è¿æ¶é´", |
| | | // prop: "followUpTime", |
| | | // width: 120, |
| | | // }, |
| | | { |
| | | label: "é¶è¡åºæ¬æ·", |
| | | prop: "basicBankAccount", |
| | |
| | | label: "ç»´æ¤äºº", |
| | | prop: "maintainer", |
| | | }, |
| | | { |
| | | /*{ |
| | | label: "å®¢æ·æ¥æº", |
| | | prop: "type", |
| | | dataType: "tag", |
| | |
| | | } |
| | | return "success"; |
| | | }, |
| | | }, |
| | | },*/ |
| | | { |
| | | label: "ç»´æ¤æ¶é´", |
| | | prop: "maintenanceTime", |
| | |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | showHide: row => !isCustomerEditLocked(row), |
| | | clickFun: row => { |
| | | if (isCustomerEditLocked(row)) { |
| | | proxy.$modal.msgWarning("已被é宿¨¡åå¼ç¨çç§æµ·å®¢æ·ä¸è½ç¼è¾"); |
| | | return; |
| | | } |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "æ·»å æ´½è°è¿åº¦", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openNegotiationDialog(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å访æé", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openReminderDialog(row); |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "æ·»å æ´½è°è¿åº¦", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // openNegotiationDialog(row); |
| | | // }, |
| | | // }, |
| | | // { |
| | | // name: "å访æé", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // openReminderDialog(row); |
| | | // }, |
| | | // }, |
| | | { |
| | | name: "详æ
", |
| | | type: "text", |
| | |
| | | searchForm: { |
| | | customerName: "", |
| | | customerType: "", |
| | | type: 0 |
| | | type: 0, |
| | | }, |
| | | form: { |
| | | customerName: "", |
| | |
| | | bankAccount: "", |
| | | bankCode: "", |
| | | customerType: "", |
| | | type: 0 |
| | | type: 0, |
| | | }, |
| | | rules: { |
| | | customerName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData", |
| | | data: { |
| | | type: 0 |
| | | type: 0, |
| | | }, |
| | | // æä»¶ä¸ä¼ åçåè° |
| | | beforeUpload: file => { |
| | |
| | | } |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (type === "edit" && isCustomerEditLocked(row)) { |
| | | proxy.$modal.msgWarning("已被é宿¨¡åå¼ç¨çç§æµ·å®¢æ·ä¸è½ç¼è¾"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | form.value.maintainer = userStore.nickName; |
| | |
| | | }); |
| | | } |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | const isCustomerEditLocked = row => { |
| | | const salesReferenced = Number(row?.salesReferenceFlag || 0) === 1; |
| | | const customerType = Number(row?.type); |
| | | const usageStatus = Number(row?.usageStatus || 0); |
| | | return salesReferenced && (customerType === 0 || (customerType === 1 && usageStatus === 1)); |
| | | }; |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form" style="margin-bottom: 20px;"> |
| | | <div class="search_form" |
| | | style="margin-bottom: 20px;"> |
| | | <div> |
| | | <span class="search_title">客æ·åç§°ï¼</span> |
| | | <el-input v-model="searchForm.customerName" |
| | |
| | | @change="handleQuery"> |
| | | <el-option label="é¶å®å®¢æ·" |
| | | value="é¶å®å®¢æ·" /> |
| | | <el-option label="è¿éå客æ·" |
| | | value="è¿éå客æ·" /> |
| | | <el-option label="ç»éå客æ·" |
| | | value="ç»éå客æ·" /> |
| | | </el-select> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <!-- <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¶è¡åºæ¬æ·ï¼" |
| | | prop="basicBankAccount"> |
| | |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-row> --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <!-- <el-col :span="12"> |
| | | <el-form-item label="弿·è¡å·ï¼" |
| | | prop="bankCode"> |
| | | <el-input v-model="form.bankCode" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-col> --> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·åç±»ï¼" |
| | | prop="customerType"> |
| | |
| | | clearable> |
| | | <el-option label="é¶å®å®¢æ·" |
| | | value="é¶å®å®¢æ·" /> |
| | | <el-option label="è¿éå客æ·" |
| | | value="è¿éå客æ·" /> |
| | | <el-option label="ç»éå客æ·" |
| | | value="ç»éå客æ·" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="assignDialogVisible" |
| | | title="åé
客æ·" |
| | | width="500px" |
| | | @close="closeAssignDialog"> |
| | | <el-form :model="assignForm" |
| | | :rules="assignRules" |
| | | ref="assignFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="客æ·åç§°"> |
| | | <el-input v-model="assignForm.customerName" |
| | | disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="åé
人å" |
| | | prop="boundId"> |
| | | <el-select v-model="assignForm.boundId" |
| | | placeholder="è¯·éæ©åé
人å" |
| | | style="width: 100%" |
| | | filterable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId || item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitAssignForm">确认</el-button> |
| | | <el-button @click="closeAssignDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="shareDialogVisible" |
| | | title="å
±äº«å®¢æ·" |
| | | width="500px" |
| | | @close="closeShareDialog"> |
| | | <el-form :model="shareForm" |
| | | :rules="shareRules" |
| | | ref="shareFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="客æ·åç§°"> |
| | | <el-input v-model="shareForm.customerName" |
| | | disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="å
±äº«äººå" |
| | | prop="boundIds"> |
| | | <el-select v-model="shareForm.boundIds" |
| | | placeholder="è¯·éæ©å
±äº«äººå" |
| | | style="width: 100%" |
| | | filterable |
| | | multiple |
| | | collapse-tags |
| | | collapse-tags-tooltip> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId || item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitShareForm">确认</el-button> |
| | | <el-button @click="closeShareDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitReminderForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitReminderForm">确认</el-button> |
| | | <el-button @click="closeReminderDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitNegotiationForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitNegotiationForm">确认</el-button> |
| | | <el-button @click="closeNegotiationDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | <span class="info-value">{{ detailForm.companyAddress }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <!-- <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">é¶è¡åºæ¬æ·ï¼</span> |
| | | <span class="info-value">{{ detailForm.basicBankAccount }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-col> --> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <!-- <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">é¶è¡è´¦å·ï¼</span> |
| | |
| | | <span class="info-value">{{ detailForm.bankCode }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-row> --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | |
| | | </div> |
| | | </div> |
| | | <!-- æ´½è°è¿åº¦è®°å½ --> |
| | | <div class="detail-section"> |
| | | <!-- <div class="detail-section"> |
| | | <div class="section-header"> |
| | | <h3 class="section-title">æ´½è°è¿åº¦è®°å½</h3> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openNegotiationDialog(detailForm)"> |
| | | æ·»å è¿åº¦ |
| | | </el-button> |
| | | </div> |
| | | <el-table :data="negotiationRecords" |
| | | border |
| | |
| | | <Paperclip /> |
| | | </el-icon> |
| | | éä»¶ |
| | | <!-- {{ row.fileList && row.fileList.length > 0 ? row.fileList.length : 'ä¸ä¼ ' }} --> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | class="no-records"> |
| | | ææ æ´½è°è¿åº¦è®°å½ |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDetailDialog">å
³é</el-button> |
| | |
| | | } from "@/api/basicData/customerFile.js"; |
| | | import { |
| | | listCustomer, |
| | | addCustomer, |
| | | delCustomer, |
| | | updateCustomer, |
| | | getCustomer, |
| | | assignCustomer, |
| | | recycleCustomer, |
| | | shareCustomer, |
| | | addCustomer, |
| | | updateCustomer, |
| | | delCustomer, |
| | | } from "@/api/basicData/customer.js"; |
| | | |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { getToken } from "@/utils/auth.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const assignDialogVisible = ref(false); |
| | | const assignFormRef = ref(); |
| | | const assignForm = reactive({ |
| | | id: undefined, |
| | | customerName: "", |
| | | boundId: undefined, |
| | | }); |
| | | const assignRules = { |
| | | boundId: [{ required: true, message: "è¯·éæ©åé
人å", trigger: "change" }], |
| | | }; |
| | | const shareDialogVisible = ref(false); |
| | | const shareFormRef = ref(); |
| | | const shareForm = reactive({ |
| | | id: undefined, |
| | | customerName: "", |
| | | boundIds: [], |
| | | }); |
| | | const shareRules = { |
| | | boundIds: [{ required: true, message: "è¯·éæ©å
±äº«äººå", trigger: "change" }], |
| | | }; |
| | | |
| | | // å访æéç¸å
³ |
| | | const reminderDialogVisible = ref(false); |
| | |
| | | }, |
| | | { |
| | | label: "å°ååèç³»æ¹å¼", |
| | | prop: "addressPhone", |
| | | prop: "companyAddress", |
| | | width: 250, |
| | | }, |
| | | { |
| | |
| | | // prop: "followUpTime", |
| | | // width: 120, |
| | | // }, |
| | | { |
| | | label: "é¶è¡åºæ¬æ·", |
| | | prop: "basicBankAccount", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "é¶è¡è´¦å·", |
| | | prop: "bankAccount", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "弿·è¡å·", |
| | | prop: "bankCode", |
| | | width: 220, |
| | | }, |
| | | // { |
| | | // label: "é¶è¡åºæ¬æ·", |
| | | // prop: "basicBankAccount", |
| | | // width: 220, |
| | | // }, |
| | | // { |
| | | // label: "é¶è¡è´¦å·", |
| | | // prop: "bankAccount", |
| | | // width: 220, |
| | | // }, |
| | | // { |
| | | // label: "弿·è¡å·", |
| | | // prop: "bankCode", |
| | | // width: 220, |
| | | // }, |
| | | { |
| | | label: "ç»´æ¤äºº", |
| | | prop: "maintainer", |
| | |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "é¢ç¨äºº", |
| | | prop: "usageUserName", |
| | | width: 120, |
| | | fixed: "right", |
| | | }, |
| | | { |
| | | label: "é¢ç¨ç¶æ", |
| | | prop: "usageStatus", |
| | | dataType: "tag", |
| | | width: 100, |
| | | fixed: "right", |
| | | formatData: value => { |
| | | if (value === 1 || value === "1") { |
| | | return "å·²é¢ç¨"; |
| | | } |
| | | return "æªé¢ç¨"; |
| | | }, |
| | | formatType: value => { |
| | | if (value === 1 || value === "1") { |
| | | return "success"; |
| | | } |
| | | return "info"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å
±äº«äºº", |
| | | prop: "togetherUserNames", |
| | | width: 120, |
| | | fixed: "right", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 200, |
| | | width: 140, |
| | | operation: [ |
| | | { |
| | | name: "åé
", |
| | | type: "text", |
| | | showHide: row => row.usageStatus != 1, |
| | | clickFun: row => { |
| | | openAssignDialog(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "åæ¶", |
| | | type: "text", |
| | | disabled: row => isCustomerRecoveryLocked(row), |
| | | showHide: row => row.usageStatus == 1, |
| | | clickFun: row => { |
| | | if (isCustomerRecoveryLocked(row)) { |
| | | proxy.$modal.msgWarning("å·²é¢ç¨å¹¶ä¸è¢«é宿¨¡åå¼ç¨çå
¬æµ·å®¢æ·ä¸è½åæ¶"); |
| | | return; |
| | | } |
| | | recycle(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å
񄧮", |
| | | type: "text", |
| | | showHide: row => row.usageStatus == 1, |
| | | clickFun: row => { |
| | | openShareDialog(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | disabled: row => isCustomerEditLocked(row), |
| | | clickFun: row => { |
| | | if (isCustomerEditLocked(row)) { |
| | | proxy.$modal.msgWarning("å·²é¢ç¨å¹¶ä¸è¢«é宿¨¡åå¼ç¨çå
¬æµ·å®¢æ·ä¸è½ç¼è¾"); |
| | | return; |
| | | } |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "详æ
", |
| | | // name: "æ·»å æ´½è°è¿åº¦", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // openDetailDialog(row); |
| | | // openNegotiationDialog(row); |
| | | // }, |
| | | // }, |
| | | // { |
| | | // name: "å访æé", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // openReminderDialog(row); |
| | | // }, |
| | | // }, |
| | | { |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openDetailDialog(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | |
| | | searchForm: { |
| | | customerName: "", |
| | | customerType: "", |
| | | type: 1 |
| | | }, |
| | | form: { |
| | | customerName: "", |
| | |
| | | bankAccount: "", |
| | | bankCode: "", |
| | | customerType: "", |
| | | type: 1 |
| | | }, |
| | | rules: { |
| | | customerName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxpayerIdentificationNumber: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | companyAddress: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | companyPhone: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | // taxpayerIdentificationNumber: [ |
| | | // { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | // ], |
| | | // companyAddress: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | // companyPhone: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | // contactPerson: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | // contactPhone: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | maintainer: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData", |
| | | data: { |
| | | type: 1 |
| | | }, |
| | | data: {}, |
| | | // æä»¶ä¸ä¼ åçåè° |
| | | beforeUpload: file => { |
| | | console.log("æä»¶å³å°ä¸ä¼ ", file); |
| | |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const { total, ...queryPage } = page; |
| | | listCustomer({ ...searchForm.value, ...queryPage }).then(res => { |
| | | listCustomer({ ...searchForm.value, ...page }).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | |
| | | } |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (type === "edit" && isCustomerEditLocked(row)) { |
| | | proxy.$modal.msgWarning("å·²é¢ç¨å¹¶ä¸è¢«é宿¨¡åå¼ç¨çå
¬æµ·å®¢æ·ä¸è½ç¼è¾"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | form.value.maintainer = userStore.nickName; |
| | |
| | | }, |
| | | ]; |
| | | form.value.maintenanceTime = getCurrentDate(); |
| | | form.value.type = 1; |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | |
| | | } |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | const isCustomerSalesReferenced = row => Number(row?.salesReferenceFlag || 0) === 1; |
| | | const isCustomerRecoveryLocked = row => Number(row?.usageStatus || 0) === 1 && isCustomerSalesReferenced(row); |
| | | const isCustomerEditLocked = row => isCustomerSalesReferenced(row) && ( |
| | | Number(row?.type) === 0 || (Number(row?.type) === 1 && Number(row?.usageStatus || 0) === 1) |
| | | ); |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | const ensureUserList = () => { |
| | | if (userList.value.length) { |
| | | return Promise.resolve(); |
| | | } |
| | | return userListNoPage().then(res => { |
| | | userList.value = res.data || []; |
| | | }); |
| | | }; |
| | | const openAssignDialog = row => { |
| | | assignForm.id = row.id; |
| | | assignForm.customerName = row.customerName; |
| | | assignForm.boundId = undefined; |
| | | ensureUserList().then(() => { |
| | | assignDialogVisible.value = true; |
| | | }); |
| | | }; |
| | | const closeAssignDialog = () => { |
| | | proxy.resetForm("assignFormRef"); |
| | | assignForm.id = undefined; |
| | | assignForm.customerName = ""; |
| | | assignForm.boundId = undefined; |
| | | assignDialogVisible.value = false; |
| | | }; |
| | | const openShareDialog = row => { |
| | | shareForm.id = row.id; |
| | | shareForm.customerName = row.customerName; |
| | | shareForm.boundIds = row.userIds || []; |
| | | ensureUserList().then(() => { |
| | | shareDialogVisible.value = true; |
| | | }); |
| | | }; |
| | | const closeShareDialog = () => { |
| | | proxy.resetForm("shareFormRef"); |
| | | shareForm.id = undefined; |
| | | shareForm.customerName = ""; |
| | | shareForm.boundIds = []; |
| | | shareDialogVisible.value = false; |
| | | }; |
| | | const submitAssignForm = () => { |
| | | proxy.$refs.assignFormRef.validate(valid => { |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | assignCustomer({ |
| | | id: assignForm.id, |
| | | usageUser: assignForm.boundId, |
| | | }).then(() => { |
| | | proxy.$modal.msgSuccess("åé
æå"); |
| | | closeAssignDialog(); |
| | | getList(); |
| | | }); |
| | | }); |
| | | }; |
| | | const submitShareForm = () => { |
| | | proxy.$refs.shareFormRef.validate(valid => { |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | shareCustomer({ |
| | | id: shareForm.id, |
| | | userIds: shareForm.boundIds, |
| | | }).then(() => { |
| | | proxy.$modal.msgSuccess("å
±äº«æå"); |
| | | closeShareDialog(); |
| | | getList(); |
| | | }); |
| | | }); |
| | | }; |
| | | const recycle = row => { |
| | | ElMessageBox.confirm("ç¡®è®¤åæ¶å®¢æ·â" + row.customerName + "âåï¼", "åæ¶æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | return recycleCustomer({id: row.id}).then(() => { |
| | | proxy.$modal.msgSuccess("åæ¶æå"); |
| | | getList(); |
| | | }) |
| | | }) |
| | | .catch(error => { |
| | | if (error === "cancel" || error === "close") { |
| | | proxy.$modal.msg("已忶"); |
| | | } |
| | | }); |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/basic/customer/export", {type: 1}, "å®¢æ·æ¡£æ¡.xlsx"); |
| | | proxy.download("/basic/customer/export", {}, "å®¢æ·æ¡£æ¡.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® |
| | | const unauthorizedData = selectedRows.value.filter( |
| | | item => item.maintainer !== userStore.nickName |
| | | ); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delCustomer(ids) |
| | | .then(res => { |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }) |
| | |
| | | }; |
| | | } |
| | | |
| | | console.log("æäº¤å访æéæ°æ®:", submitvalue.value); |
| | | |
| | | // è°ç¨æ¥å£ |
| | | addReturnVisit(submitvalue.value) |
| | | .then(res => { |
| | |
| | | negotiationForm.followUpTime = ""; |
| | | negotiationForm.followerUserName = userStore.nickName; // é»è®¤å½åç»å½äºº |
| | | negotiationForm.content = ""; |
| | | // { |
| | | // "customerId": 152, |
| | | // "followUpMethod": "çµè¯æ²é", |
| | | // "followUpLevel": "没ææå", |
| | | // "followUpTime": "2026-03-04T15:30:00", |
| | | // "followerUserName": "管çåè´¦å·", |
| | | // "content": "111" |
| | | // } |
| | | negotiationDialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | |
| | | if (isEdit) { |
| | | // ä¿®æ¹æä½ |
| | | console.log("ä¿®æ¹æ´½è°è¿åº¦æ°æ®:", negotiationForm); |
| | | // è¿éå¯ä»¥è°ç¨æ´æ°æ¥å£ |
| | | // å®é
项ç®ä¸éè¦æ ¹æ®å端æ¥å£è¿è¡è°æ´ |
| | | // 示ä¾ï¼updateCustomerFollow(negotiationForm).then(res => { |
| | | // // æ´æ°æ¬å°æ°æ® |
| | | // const index = negotiationForm.editIndex; |
| | | // negotiationRecords.value[index] = { |
| | | // followUpTime: negotiationForm.followUpTime, |
| | | // followUpMethod: negotiationForm.followUpMethod, |
| | | // followUpLevel: negotiationForm.followUpLevel, |
| | | // followerUserName: negotiationForm.followerUserName, |
| | | // content: negotiationForm.content, |
| | | // id: negotiationForm.id, |
| | | // }; |
| | | // proxy.$modal.msgSuccess("ä¿®æ¹æå"); |
| | | // closeNegotiationDialog(); |
| | | // }); |
| | | updateCustomerFollow(negotiationForm).then(res => { |
| | | // æ´æ°æ¬å°æ°æ® |
| | | getCustomer(negotiationForm.customerId).then(res => { |
| | |
| | | |
| | | // æå¼è¯¦æ
å¼¹çª |
| | | const openDetailDialog = row => { |
| | | // è°ç¨getCustomeræ¥å£è·å客æ·è¯¦æ
|
| | | getCustomer(row.id).then(res => { |
| | | // å¡«å
客æ·åºæ¬ä¿¡æ¯ |
| | | Object.assign(detailForm, res.data); |
| | |
| | | prop="paramCode"> |
| | | <el-input v-model="formData.paramCode" |
| | | disabled |
| | | placeholder="ä¿ååèªå¨çæ" /> |
| | | placeholder="èªå¨çæ" /> |
| | | </el-form-item> |
| | | <el-form-item label="åæ°åç§°" |
| | | prop="paramName"> |
| | |
| | | value="2" /> |
| | | </el-select> |
| | | </el-form-item> --> |
| | | <el-form-item label="å建æ¶é´" |
| | | prop="createTime"> |
| | | <el-date-picker v-model="formCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="åä½" |
| | | prop="unit"> |
| | | <el-input v-model="formData.unit" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, reactive, computed } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { onMounted, ref, reactive } from "vue"; |
| | | import { |
| | | parameterListPage, |
| | | addParameter, |
| | |
| | | import { deptTreeSelect } from "@/api/system/user.js"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { parseTime } from "@/utils/ruoyi"; |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | |
| | | { |
| | | label: "å建æ¶é´", |
| | | prop: "createTime", |
| | | formatData: val => { |
| | | if (!val) return ""; |
| | | return parseTime(val, "{y}-{m}-{d} {h}:{i}:{s}"); |
| | | }, |
| | | }, |
| | | { |
| | | label: "æä½", |
| | |
| | | remark: "", |
| | | isRequired: 0, |
| | | paramFormat: "", |
| | | createTime: "", |
| | | }); |
| | | const rules = reactive({ |
| | | paramName: [{ required: true, message: "请è¾å
¥åæ°åç§°", trigger: "blur" }], |
| | |
| | | }, |
| | | }, |
| | | ], |
| | | }); |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (formData.createTime ? String(formData.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | formData.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | // const productTypes = ref([]); |
| | | const isEdit = ref(false); |
| | |
| | | formData.unit = ""; |
| | | formData.remark = ""; |
| | | formData.isRequired = 0; |
| | | formData.createTime = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | formData.remark = row.remark || ""; |
| | | formData.paramFormat = row.paramFormat || ""; |
| | | formData.isRequired = row.isRequired || 0; |
| | | formData.createTime = row.createTime || ""; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | <el-dialog v-model="visible" title="éæ©äº§å" width="900px" destroy-on-close :close-on-click-modal="false"> |
| | | <el-form :inline="true" :model="query" class="mb-2"> |
| | | <el-form-item label="产ååç§°"> |
| | | <el-input v-model="query.productName" placeholder="è¾å
¥äº§ååç§°" clearable @keyup.enter="onSearch" /> |
| | | <el-input v-model="query.productName" placeholder="è¾å
¥äº§ååç§°" clearable @keyup.enter="onSearch" style="width: 200px" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="产ååå·"> |
| | | <el-input v-model="query.model" placeholder="è¾å
¥äº§ååå·" clearable @keyup.enter="onSearch" /> |
| | | <el-input v-model="query.model" placeholder="è¾å
¥äº§ååå·" clearable @keyup.enter="onSearch" style="width: 200px" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | |
| | | @selection-change="handleSelectionChange" @select="handleSelect"> |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column type="index" label="åºå·" width="60" /> |
| | | <el-table-column prop="productName" label="产ååç§°" min-width="160" /> |
| | | <el-table-column prop="model" label="产ååå·" min-width="200" /> |
| | | <el-table-column prop="unit" label="åä½" min-width="160" /> |
| | | <el-table-column prop="topProductName" label="产å大类" min-width="120" /> |
| | | <el-table-column prop="productName" label="产ååç§°" min-width="140" /> |
| | | <el-table-column prop="model" label="产ååå·" min-width="180" /> |
| | | <el-table-column prop="unit" label="åä½" min-width="100" /> |
| | | </el-table> |
| | | |
| | | <div class="mt-3 flex justify-end"> |
| | |
| | | <el-form-item label="产ååç§°ï¼" |
| | | prop="productName"> |
| | | <el-input v-model="form.productName" |
| | | type="textarea" |
| | | resize="vertical" |
| | | placeholder="请è¾å
¥äº§ååç§°" |
| | | maxlength="20" |
| | | maxlength="50" |
| | | show-word-limit |
| | | clearable |
| | | @keydown.enter.prevent /> |
| | |
| | | :rules="modelRules" |
| | | ref="modelFormRef"> |
| | | <el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产åç¼å·ï¼" |
| | | prop="productCode"> |
| | |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" |
| | | prop="model"> |
| | |
| | | rules: { |
| | | productName: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | { max: 20, message: "产ååç§°ä¸è½è¶
è¿20个å符", trigger: "blur" }, |
| | | { max: 50, message: "产ååç§°ä¸è½è¶
è¿50个å符", trigger: "blur" }, |
| | | ], |
| | | }, |
| | | modelForm: { |
| | |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">ä¾åºåæ¡£æ¡ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.supplierName" |
| | | <el-input v-model="searchForm.supplierName" |
| | | style="width: 240px" |
| | | placeholder="è¾å
¥ä¾åºååç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | ></PIMTable> |
| | | @pagination="pagination"></PIMTable> |
| | | </div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢ä¾åºåä¿¡æ¯' : 'ç¼è¾ä¾åºåä¿¡æ¯'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form |
| | | :model="form" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºååç§°ï¼" prop="supplierName"> |
| | | <el-input |
| | | v-model="form.supplierName" |
| | | <el-form-item label="ä¾åºååç§°ï¼" |
| | | prop="supplierName"> |
| | | <el-input v-model="form.supplierName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item |
| | | label="纳ç¨äººè¯å«å·ï¼" |
| | | prop="taxpayerIdentificationNum" |
| | | > |
| | | <el-input |
| | | v-model="form.taxpayerIdentificationNum" |
| | | <el-form-item label="纳ç¨äººè¯å«å·ï¼" |
| | | prop="taxpayerIdentificationNum"> |
| | | <el-input v-model="form.taxpayerIdentificationNum" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="å
¬å¸å°åï¼" |
| | | prop="companyAddress"> |
| | | <el-input v-model="form.companyAddress" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬å¸å°åï¼" prop="companyAddress"> |
| | | <el-input |
| | | v-model="form.companyAddress" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬å¸çµè¯ï¼" prop="companyPhone"> |
| | | <el-input |
| | | v-model="form.companyPhone" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="弿·è¡ï¼" prop="bankAccountName"> |
| | | <el-input |
| | | v-model="form.bankAccountName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è´¦å·ï¼" prop="bankAccountNum"> |
| | | <el-input |
| | | v-model="form.bankAccountNum" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è系人ï¼" prop="contactUserName"> |
| | | <el-input |
| | | v-model="form.contactUserName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èç³»çµè¯ï¼" prop="contactUserPhone"> |
| | | <el-input |
| | | v-model="form.contactUserPhone" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»´æ¤äººï¼" prop="maintainUserId"> |
| | | <el-select |
| | | v-model="form.maintainUserId" |
| | | <el-form-item label="ç»´æ¤äººï¼" |
| | | prop="maintainUserId"> |
| | | <el-select v-model="form.maintainUserId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | disabled |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | disabled> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | /> |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»´æ¤æ¶é´ï¼" prop="maintainTime"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | <el-form-item label="ç»´æ¤æ¶é´ï¼" |
| | | prop="maintainTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.maintainTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºåç±»åï¼" prop="supplierType"> |
| | | <el-select v-model="form.supplierType" placeholder="è¯·éæ©" clearable> |
| | | <el-option label="ç²" value="ç²" /> |
| | | <el-option label="ä¹" value="ä¹" /> |
| | | <el-option label="ä¸" value="ä¸" /> |
| | | <el-option label="ä¸" value="ä¸" /> |
| | | <!-- <el-col :span="12"> |
| | | <el-form-item label="ä¾åºåç±»åï¼" |
| | | prop="supplierType"> |
| | | <el-select v-model="form.supplierType" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option label="ç²" |
| | | value="ç²" /> |
| | | <el-option label="ä¹" |
| | | value="ä¹" /> |
| | | <el-option label="ä¸" |
| | | value="ä¸" /> |
| | | <el-option label="ä¸" |
| | | value="ä¸" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-col> --> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¯å¦ç½ååï¼" prop="isWhite"> |
| | | <el-select v-model="form.isWhite" placeholder="è¯·éæ©" clearable> |
| | | <el-option label="æ¯" :value="0" /> |
| | | <el-option label="å¦" :value="1" /> |
| | | <el-form-item label="æ¯å¦ç½ååï¼" |
| | | prop="isWhite"> |
| | | <el-select v-model="form.isWhite" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option label="æ¯" |
| | | :value="0" /> |
| | | <el-option label="å¦" |
| | | :value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- ä¾åºå导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | :title="upload.title" |
| | | <el-dialog :title="upload.title" |
| | | v-model="upload.open" |
| | | width="400px" |
| | | append-to-body |
| | | > |
| | | <el-upload |
| | | ref="uploadRef" |
| | | append-to-body> |
| | | <el-upload ref="uploadRef" |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | |
| | | :on-success="handleFileSuccess" |
| | | :on-error="handleFileError" |
| | | :auto-upload="false" |
| | | drag |
| | | > |
| | | drag> |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">å°æä»¶æå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em></div> |
| | | <template #tip> |
| | | <div class="el-upload__tip text-center"> |
| | | <span>ä»
å
许导å
¥xlsãxlsxæ ¼å¼æä»¶ã</span> |
| | | <el-link |
| | | type="primary" |
| | | <el-link type="primary" |
| | | :underline="false" |
| | | style="font-size: 12px; vertical-align: baseline" |
| | | @click="importTemplate" |
| | | >ä¸è½½æ¨¡æ¿</el-link |
| | | > |
| | | @click="importTemplate">ä¸è½½æ¨¡æ¿</el-link> |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitFileForm">ç¡® å®</el-button> |
| | | <el-button type="primary" |
| | | @click="submitFileForm">ç¡® å®</el-button> |
| | | <el-button @click="upload.open = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | { |
| | | label: "ä¾åºååç§°", |
| | | prop: "supplierName", |
| | | width: 250, |
| | | // width: 250, |
| | | }, |
| | | { |
| | | label: "ä¾åºåç±»å", |
| | | prop: "supplierType", |
| | | width: 120, |
| | | }, |
| | | // { |
| | | // label: "ä¾åºåç±»å", |
| | | // prop: "supplierType", |
| | | // width: 120, |
| | | // }, |
| | | { |
| | | label: "纳ç¨äººè¯å«å·", |
| | | prop: "taxpayerIdentificationNum", |
| | | width: 230, |
| | | // width: 230, |
| | | }, |
| | | { |
| | | label: "å
¬å¸å°å", |
| | | prop: "companyAddress", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "èç³»æ¹å¼", |
| | | prop: "companyPhone", |
| | | width:150 |
| | | }, |
| | | { |
| | | label: "弿·è¡", |
| | | prop: "bankAccountName", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "è´¦å·", |
| | | prop: "bankAccountNum", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "è系人", |
| | | prop: "contactUserName", |
| | | }, |
| | | { |
| | | label: "èç³»çµè¯", |
| | | prop: "contactUserPhone", |
| | | width: 150, |
| | | // width: 300, |
| | | }, |
| | | { |
| | | label: "ç»´æ¤äºº", |
| | |
| | | { |
| | | label: "ç»´æ¤æ¶é´", |
| | | prop: "maintainTime", |
| | | width:100 |
| | | // width: 100, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | fixed: "right", |
| | | width: 150, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | //èµè´¨éä»¶ |
| | | name: "èµè´¨æä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openFileDialog(row) |
| | | } |
| | | } |
| | | // { |
| | | // //èµè´¨éä»¶ |
| | | // name: "èµè´¨æä»¶", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // openFileDialog(row); |
| | | // }, |
| | | // }, |
| | | ], |
| | | }, |
| | | ]); |
| | |
| | | supplierName: "", |
| | | taxpayerIdentificationNum: "", |
| | | companyAddress: "", |
| | | companyPhone: "", |
| | | bankAccountName: "", |
| | | bankAccountNum: "", |
| | | contactUserName: "", |
| | | contactUserPhone: "", |
| | | maintainUserId: "", |
| | | maintainTime: "", |
| | | supplierType: "", |
| | |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | companyAddress: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | companyPhone: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | bankAccountName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | bankAccountNum: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | contactUserName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | contactUserPhone: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | maintainUserId: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | maintainTime: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | supplierType: [{ required: true, message: "è¯·éæ©ä¾åºåç±»å", trigger: "change" }], |
| | | // supplierType: [ |
| | | // { required: true, message: "è¯·éæ©ä¾åºåç±»å", trigger: "change" }, |
| | | // ], |
| | | }, |
| | | }); |
| | | const { searchForm, form, rules } = toRefs(data); |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | } |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | listSupplier({ ...searchForm.value, ...page, isWhite: 1 }).then((res) => { |
| | | listSupplier({ ...searchForm.value, ...page, isWhite: 1 }).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | |
| | | } |
| | | /** ä¸è½½æ¨¡æ¿ */ |
| | | function importTemplate() { |
| | | proxy.download("/system/supplier/downloadTemplate", {}, "ä¾åºå导å
¥æ¨¡æ¿.xlsx"); |
| | | proxy.download( |
| | | "/system/supplier/downloadTemplate", |
| | | {}, |
| | | "ä¾åºå导å
¥æ¨¡æ¿.xlsx" |
| | | ); |
| | | } |
| | | |
| | | /**æä»¶ä¸ä¼ ä¸å¤ç */ |
| | |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | |
| | | form.value = {}; |
| | | form.value.maintainUserId = userStore.id; |
| | | form.value.maintainTime = getCurrentDate(); |
| | | userListNoPage().then((res) => { |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (type === "edit") { |
| | | getSupplier(row.id).then((res) => { |
| | | getSupplier(row.id).then(res => { |
| | | form.value = { ...res.data }; |
| | | }); |
| | | } |
| | |
| | | }; |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitEdit(); |
| | |
| | | }; |
| | | // æäº¤æ°å¢ |
| | | const submitAdd = () => { |
| | | addSupplier(form.value).then((res) => { |
| | | addSupplier(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | |
| | | }; |
| | | // æäº¤ä¿®æ¹ |
| | | const submitEdit = () => { |
| | | updateSupplier(form.value).then((res) => { |
| | | updateSupplier(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/system/supplier/export", { isWhite: 1 }, "ä¾åºåæ¡£æ¡.xlsx"); |
| | | proxy.download( |
| | | "/system/supplier/export", |
| | | { isWhite: 1 }, |
| | | "ä¾åºåæ¡£æ¡.xlsx" |
| | | ); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® |
| | | const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName); |
| | | const unauthorizedData = selectedRows.value.filter( |
| | | item => item.maintainUserName !== userStore.nickName |
| | | ); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delSupplier(ids) |
| | | .then((res) => { |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }) |
| | |
| | | <div class="search_form"> |
| | | <div style="margin-bottom: 10px;"> |
| | | <span class="search_title">ä¾åºåæ¡£æ¡ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.supplierName" |
| | | <el-input v-model="searchForm.supplierName" |
| | | style="width: 240px" |
| | | placeholder="è¾å
¥ä¾åºååç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div style="margin-bottom: 10px;"> |
| | | <el-button type="primary" @click="openForm('add')" |
| | | >æ°å¢ä¾åºå</el-button |
| | | > |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢ä¾åºå</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="info" plain icon="Upload" @click="handleImport" |
| | | >导å
¥</el-button |
| | | > |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | <el-button type="info" |
| | | plain |
| | | icon="Upload" |
| | | @click="handleImport">导å
¥</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | ></PIMTable> |
| | | @pagination="pagination"></PIMTable> |
| | | </div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢ä¾åºåä¿¡æ¯' : 'ç¼è¾ä¾åºåä¿¡æ¯'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form |
| | | :model="form" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºååç§°ï¼" prop="supplierName"> |
| | | <el-input |
| | | v-model="form.supplierName" |
| | | <el-form-item label="ä¾åºååç§°ï¼" |
| | | prop="supplierName"> |
| | | <el-input v-model="form.supplierName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item |
| | | label="纳ç¨äººè¯å«å·ï¼" |
| | | prop="taxpayerIdentificationNum" |
| | | > |
| | | <el-input |
| | | v-model="form.taxpayerIdentificationNum" |
| | | <el-form-item label="纳ç¨äººè¯å«å·ï¼" |
| | | prop="taxpayerIdentificationNum"> |
| | | <el-input v-model="form.taxpayerIdentificationNum" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="å
¬å¸å°åï¼" |
| | | prop="companyAddress"> |
| | | <el-input v-model="form.companyAddress" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬å¸å°åï¼" prop="companyAddress"> |
| | | <el-input |
| | | v-model="form.companyAddress" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬å¸çµè¯ï¼" prop="companyPhone"> |
| | | <el-input |
| | | v-model="form.companyPhone" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="弿·è¡ï¼" prop="bankAccountName"> |
| | | <el-input |
| | | v-model="form.bankAccountName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è´¦å·ï¼" prop="bankAccountNum"> |
| | | <el-input |
| | | v-model="form.bankAccountNum" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è系人ï¼" prop="contactUserName"> |
| | | <el-input |
| | | v-model="form.contactUserName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èç³»çµè¯ï¼" prop="contactUserPhone"> |
| | | <el-input |
| | | v-model="form.contactUserPhone" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»´æ¤äººï¼" prop="maintainUserId"> |
| | | <el-select |
| | | v-model="form.maintainUserId" |
| | | <el-form-item label="ç»´æ¤äººï¼" |
| | | prop="maintainUserId"> |
| | | <el-select v-model="form.maintainUserId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | disabled |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | disabled> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | /> |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»´æ¤æ¶é´ï¼" prop="maintainTime"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | <el-form-item label="ç»´æ¤æ¶é´ï¼" |
| | | prop="maintainTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.maintainTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | /> |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºåç±»åï¼" prop="supplierType"> |
| | | <el-select v-model="form.supplierType" placeholder="è¯·éæ©" clearable> |
| | | <el-option label="ç²" value="ç²" /> |
| | | <el-option label="ä¹" value="ä¹" /> |
| | | <el-option label="ä¸" value="ä¸" /> |
| | | <el-option label="ä¸" value="ä¸" /> |
| | | <!-- <el-col :span="12"> |
| | | <el-form-item label="ä¾åºåç±»åï¼" |
| | | prop="supplierType"> |
| | | <el-select v-model="form.supplierType" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option label="ç²" |
| | | value="ç²" /> |
| | | <el-option label="ä¹" |
| | | value="ä¹" /> |
| | | <el-option label="ä¸" |
| | | value="ä¸" /> |
| | | <el-option label="ä¸" |
| | | value="ä¸" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-col> --> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¯å¦ç½ååï¼" prop="isWhite"> |
| | | <el-select v-model="form.isWhite" placeholder="è¯·éæ©" clearable> |
| | | <el-option label="æ¯" :value="0" /> |
| | | <el-option label="å¦" :value="1" /> |
| | | <el-form-item label="æ¯å¦ç½ååï¼" |
| | | prop="isWhite"> |
| | | <el-select v-model="form.isWhite" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option label="æ¯" |
| | | :value="0" /> |
| | | <el-option label="å¦" |
| | | :value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- ä¾åºå导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | :title="upload.title" |
| | | <el-dialog :title="upload.title" |
| | | v-model="upload.open" |
| | | width="400px" |
| | | append-to-body |
| | | > |
| | | <el-upload |
| | | ref="uploadRef" |
| | | append-to-body> |
| | | <el-upload ref="uploadRef" |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | |
| | | :on-success="handleFileSuccess" |
| | | :on-error="handleFileError" |
| | | :auto-upload="false" |
| | | drag |
| | | > |
| | | drag> |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">å°æä»¶æå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em></div> |
| | | <template #tip> |
| | | <div class="el-upload__tip text-center"> |
| | | <span>ä»
å
许导å
¥xlsãxlsxæ ¼å¼æä»¶ã</span> |
| | | <el-link |
| | | type="primary" |
| | | <el-link type="primary" |
| | | :underline="false" |
| | | style="font-size: 12px; vertical-align: baseline" |
| | | @click="importTemplate" |
| | | >ä¸è½½æ¨¡æ¿</el-link |
| | | > |
| | | @click="importTemplate">ä¸è½½æ¨¡æ¿</el-link> |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitFileForm">ç¡® å®</el-button> |
| | | <el-button type="primary" |
| | | @click="submitFileForm">ç¡® å®</el-button> |
| | | <el-button @click="upload.open = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | { |
| | | label: "ä¾åºååç§°", |
| | | prop: "supplierName", |
| | | width: 250, |
| | | // width: 250, |
| | | }, |
| | | { |
| | | label: "ä¾åºåç±»å", |
| | | prop: "supplierType", |
| | | width: 120, |
| | | }, |
| | | // { |
| | | // label: "ä¾åºåç±»å", |
| | | // prop: "supplierType", |
| | | // width: 120, |
| | | // }, |
| | | { |
| | | label: "纳ç¨äººè¯å«å·", |
| | | prop: "taxpayerIdentificationNum", |
| | | width: 230, |
| | | // width: 230, |
| | | }, |
| | | { |
| | | label: "å
¬å¸å°å", |
| | | prop: "companyAddress", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "èç³»æ¹å¼", |
| | | prop: "companyPhone", |
| | | width:150 |
| | | }, |
| | | { |
| | | label: "弿·è¡", |
| | | prop: "bankAccountName", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "è´¦å·", |
| | | prop: "bankAccountNum", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "è系人", |
| | | prop: "contactUserName", |
| | | }, |
| | | { |
| | | label: "èç³»çµè¯", |
| | | prop: "contactUserPhone", |
| | | width: 150, |
| | | // width: 220, |
| | | }, |
| | | { |
| | | label: "ç»´æ¤äºº", |
| | | prop: "maintainUserName", |
| | | // width: 220, |
| | | }, |
| | | |
| | | { |
| | | label: "ç»´æ¤æ¶é´", |
| | | prop: "maintainTime", |
| | | width:100 |
| | | width: 100, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | fixed: "right", |
| | | width: 150, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | //èµè´¨éä»¶ |
| | | name: "èµè´¨æä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openFileDialog(row) |
| | | } |
| | | } |
| | | // { |
| | | // //èµè´¨éä»¶ |
| | | // name: "èµè´¨æä»¶", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // openFileDialog(row); |
| | | // }, |
| | | // }, |
| | | ], |
| | | }, |
| | | ]); |
| | |
| | | supplierName: "", |
| | | taxpayerIdentificationNum: "", |
| | | companyAddress: "", |
| | | companyPhone: "", |
| | | bankAccountName: "", |
| | | bankAccountNum: "", |
| | | contactUserName: "", |
| | | contactUserPhone: "", |
| | | maintainUserId: "", |
| | | maintainTime: "", |
| | | supplierType: "", |
| | |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | companyAddress: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | companyPhone: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | bankAccountName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | bankAccountNum: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | contactUserName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | contactUserPhone: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | maintainUserId: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | maintainTime: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | supplierType: [{ required: true, message: "è¯·éæ©ä¾åºåç±»å", trigger: "change" }], |
| | | // supplierType: [ |
| | | // { required: true, message: "è¯·éæ©ä¾åºåç±»å", trigger: "change" }, |
| | | // ], |
| | | }, |
| | | }); |
| | | const { searchForm, form, rules } = toRefs(data); |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | } |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | listSupplier({ ...searchForm.value, ...page, isWhite: 0 }).then((res) => { |
| | | listSupplier({ ...searchForm.value, ...page, isWhite: 0 }).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | |
| | | } |
| | | /** ä¸è½½æ¨¡æ¿ */ |
| | | function importTemplate() { |
| | | proxy.download("/system/supplier/downloadTemplate", {}, "ä¾åºå导å
¥æ¨¡æ¿.xlsx"); |
| | | proxy.download( |
| | | "/system/supplier/downloadTemplate", |
| | | {}, |
| | | "ä¾åºå导å
¥æ¨¡æ¿.xlsx" |
| | | ); |
| | | } |
| | | |
| | | /**æä»¶ä¸ä¼ ä¸å¤ç */ |
| | |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | |
| | | form.value = {}; |
| | | form.value.maintainUserId = userStore.id; |
| | | form.value.maintainTime = getCurrentDate(); |
| | | userListNoPage().then((res) => { |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (type === "edit") { |
| | | getSupplier(row.id).then((res) => { |
| | | getSupplier(row.id).then(res => { |
| | | form.value = { ...res.data }; |
| | | }); |
| | | } |
| | |
| | | }; |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitEdit(); |
| | |
| | | }; |
| | | // æäº¤æ°å¢ |
| | | const submitAdd = () => { |
| | | addSupplier(form.value).then((res) => { |
| | | addSupplier(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | |
| | | }; |
| | | // æäº¤ä¿®æ¹ |
| | | const submitEdit = () => { |
| | | updateSupplier(form.value).then((res) => { |
| | | updateSupplier(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/system/supplier/export", { isWhite: 0 }, "ä¾åºåæ¡£æ¡.xlsx"); |
| | | proxy.download( |
| | | "/system/supplier/export", |
| | | { isWhite: 0 }, |
| | | "ä¾åºåæ¡£æ¡.xlsx" |
| | | ); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® |
| | | const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName); |
| | | const unauthorizedData = selectedRows.value.filter( |
| | | item => item.maintainUserName !== userStore.nickName |
| | | ); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delSupplier(ids) |
| | | .then((res) => { |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }) |
| | |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="ç³è¯·é¨é¨ï¼"> |
| | | <el-select disabled |
| | | v-model="form.approveDeptId" |
| | | placeholder="éæ©é¨é¨"> |
| | | <el-option v-for="user in productOptions" |
| | | :key="user.deptId" |
| | | :label="user.deptName" |
| | | :value="user.deptId" /> |
| | | </el-select> |
| | | <el-input v-model="form.approveDeptName" |
| | | placeholder="" |
| | | clearable |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | clearable |
| | | type="textarea" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-if="Number(props.approveType) === 4"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="æ¥ééé¢ï¼"> |
| | | <el-input-number v-model="form.price" |
| | | disabled |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | <el-descriptions-item label="æ¥ä»·æ»é¢" |
| | | :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }} |
| | | {{ formatCurrency(currentQuotation.totalAmount) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | |
| | | label="åä½" /> |
| | | <el-table-column prop="unitPrice" |
| | | label="åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template> |
| | | <template #default="scope">{{ formatCurrency(scope.row.unitPrice) }}</template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | |
| | | <el-descriptions-item label="ååéé¢" |
| | | :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | ¥{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }} |
| | | {{ formatCurrency(currentPurchase.contractAmount) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | |
| | | label="æ°é" /> |
| | | <el-table-column prop="taxInclusiveUnitPrice" |
| | | label="å«ç¨åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template> |
| | | <template #default="scope">{{ formatCurrency(scope.row.taxInclusiveUnitPrice) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="taxInclusiveTotalPrice" |
| | | label="å«ç¨æ»ä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template> |
| | | <template #default="scope">{{ formatCurrency(scope.row.taxInclusiveTotalPrice) }}</template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | |
| | | v-if="operationType === 'approval'"> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | :loading="submitLoading" |
| | | @click="submitForm(2)">ä¸éè¿</el-button> |
| | | <el-button type="primary" |
| | | :loading="submitLoading" |
| | | @click="submitForm(1)">éè¿</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | |
| | | import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { getDeliveryDetailByShippingNo } from "@/api/salesManagement/deliveryLedger.js"; |
| | | import ImagePreview from "@/components/AttachmentPreview/image/index.vue"; |
| | | import { formatCurrency } from "@/utils/numberFormat"; |
| | | const emit = defineEmits(["close"]); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | |
| | | const deliveryLoading = ref(false); |
| | | const currentDelivery = ref({}); |
| | | const deliveryProductList = ref([]); |
| | | const submitLoading = ref(false); |
| | | const isQuotationApproval = computed(() => Number(props.approveType) === 6); |
| | | const isPurchaseApproval = computed(() => Number(props.approveType) === 5); |
| | | const isDeliveryApproval = computed(() => Number(props.approveType) === 7); |
| | |
| | | // 夿æ¯å¦ä¸ºæå䏿¥ |
| | | const isLast = |
| | | activities.value.findIndex(a => a.isShen) === activities.value.length - 1; |
| | | updateApproveNode({ ...currentActivity, isLast }).then(() => { |
| | | submitLoading.value = true; |
| | | updateApproveNode({ ...currentActivity, isLast }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | .finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | |
| | | currentQuotation.value = {}; |
| | | purchaseLoading.value = false; |
| | | currentPurchase.value = {}; |
| | | submitLoading.value = false; |
| | | emit("close"); |
| | | }; |
| | | defineExpose({ |
| | |
| | | </div> |
| | | </div> |
| | | </FormDialog> |
| | | |
| | | <!-- æä»¶ç®¡çå¼¹çª --> |
| | | <FormDialog |
| | | v-model="filesDialogVisible" |
| | | title="æä»¶ç®¡ç" |
| | | :width="'900px'" |
| | | @close="closeFilesDialog" |
| | | @confirm="closeFilesDialog" |
| | | @cancel="closeFilesDialog" |
| | | > |
| | | <div class="file-manager"> |
| | | <!-- æä»¶ä¸ä¼ --> |
| | | <div class="upload-section"> |
| | | <el-upload |
| | | :action="uploadUrl" |
| | | :headers="uploadHeaders" |
| | | :on-success="handleUploadSuccess" |
| | | :on-error="handleUploadError" |
| | | :before-upload="beforeUpload" |
| | | name="files" |
| | | multiple |
| | | :show-file-list="false" |
| | | accept=".txt,.md,.docx,.xlsx,.xls,.pdf" |
| | | > |
| | | <el-button type="primary">ä¸ä¼ æä»¶</el-button> |
| | | </el-upload> |
| | | <el-button |
| | | type="success" |
| | | @click="saveFiles" |
| | | :disabled="uploadedBlobIds.length === 0" |
| | | :loading="savingFiles" |
| | | style="margin-left: 10px" |
| | | > |
| | | ä¿åæä»¶å
³è |
| | | </el-button> |
| | | <el-button |
| | | v-if="uploadedBlobIds.length > 0" |
| | | type="text" |
| | | @click="clearUploadedFiles" |
| | | style="margin-left: 10px" |
| | | > |
| | | æ¸
空å¾
ä¿åå表 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- å¾
ä¿åçæä»¶å表 --> |
| | | <div v-if="uploadedBlobIds.length > 0" class="uploaded-list"> |
| | | <div class="uploaded-tip"> |
| | | <el-icon style="color: #409eff"><InfoFilled /></el-icon> |
| | | <span>å·²ä¸ä¼ {{ uploadedBlobIds.length }} 个æä»¶,请ç¹å»"ä¿åæä»¶å
³è"æé®è§¦ååéåå¤ç</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æä»¶å表ä¸åéåç¶æ --> |
| | | <el-table :data="fileList" style="margin-top: 20px" border> |
| | | <el-table-column prop="fileName" label="æä»¶å" show-overflow-tooltip /> |
| | | <el-table-column prop="fileType" label="æä»¶ç±»å" width="100" /> |
| | | <el-table-column label="åéåç¶æ" width="120"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusType(row.vectorStatus)"> |
| | | {{ getStatusText(row.vectorStatus) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="chunkCount" label="åçæ°" width="100" align="center" /> |
| | | <el-table-column label="é误信æ¯" width="200" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.vectorError" style="color: #f56c6c">{{ row.vectorError }}</span> |
| | | <span v-else style="color: #909399">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="createTime" label="ä¸ä¼ æ¶é´" width="180" /> |
| | | <el-table-column label="æä½" width="150" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button |
| | | v-if="row.vectorStatus === 3" |
| | | type="text" |
| | | @click="reprocessFile(row)" |
| | | > |
| | | éæ°å¤ç |
| | | </el-button> |
| | | <el-button type="text" @click="deleteFile(row)" style="color: #f56c6c"> |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </FormDialog> |
| | | |
| | | <!-- ç¥è¯åºé®çå¼¹çª --> |
| | | <FormDialog |
| | | v-model="chatDialogVisible" |
| | | title="ç¥è¯åºé®ç" |
| | | :width="'800px'" |
| | | @close="closeChatDialog" |
| | | @confirm="closeChatDialog" |
| | | @cancel="closeChatDialog" |
| | | > |
| | | <div class="knowledge-chat"> |
| | | <div class="chat-header"> |
| | | <el-tag type="success">å½åç¥è¯åº: {{ currentKnowledgeBase?.title }}</el-tag> |
| | | </div> |
| | | |
| | | <!-- 对è¯åºå --> |
| | | <div class="chat-messages" ref="chatMessagesRef"> |
| | | <div |
| | | v-for="(msg, index) in messages" |
| | | :key="index" |
| | | :class="['message', msg.role]" |
| | | > |
| | | <div class="message-role">{{ msg.role === 'user' ? 'æ' : 'AI婿' }}</div> |
| | | <div class="message-content">{{ msg.content }}</div> |
| | | </div> |
| | | <div v-if="chatLoading" class="message assistant"> |
| | | <div class="message-role">AI婿</div> |
| | | <div class="message-content typing">æ£å¨æèä¸...</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- è¾å
¥æ¡ --> |
| | | <div class="chat-input"> |
| | | <el-input |
| | | v-model="inputQuestion" |
| | | placeholder="请è¾å
¥é®é¢,æå车åé(Ctrl+Enterå¿«æ·åé)" |
| | | @keyup.enter="sendMessage" |
| | | :disabled="chatLoading" |
| | | > |
| | | <template #append> |
| | | <el-button @click="sendMessage" :loading="chatLoading">åé</el-button> |
| | | </template> |
| | | </el-input> |
| | | <div class="chat-actions"> |
| | | <el-button type="text" size="small" @click="clearMessages">æ¸
空对è¯</el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search, InfoFilled } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance, computed, watch, nextTick } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance, computed, watch } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import { |
| | | listKnowledgeBase, |
| | | delKnowledgeBase, |
| | | addKnowledgeBase, |
| | | updateKnowledgeBase, |
| | | getVectorStatus, |
| | | reprocessVector, |
| | | saveKnowledgeBaseFiles, |
| | | deleteKnowledgeBaseFile, |
| | | knowledgeChat |
| | | } from "@/api/collaborativeApproval/knowledgeBase.js"; |
| | | import { listKnowledgeBase, delKnowledgeBase,addKnowledgeBase,updateKnowledgeBase } from "@/api/collaborativeApproval/knowledgeBase.js"; |
| | | import useUserStore from '@/store/modules/user'; |
| | | import { userListNoPageByTenantId } from '@/api/system/user.js'; |
| | | import { getToken } from "@/utils/auth"; |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | |
| | | dialogTitle: "", |
| | | dialogType: "add", |
| | | viewDialogVisible: false, |
| | | currentKnowledge: {}, |
| | | filesDialogVisible: false, |
| | | currentKnowledgeBase: null, |
| | | fileList: [], |
| | | uploadedBlobIds: [], |
| | | savingFiles: false, |
| | | vectorStatusTimer: null, // åéåç¶æè½®è¯¢å®æ¶å¨ |
| | | chatDialogVisible: false, |
| | | messages: [], |
| | | inputQuestion: "", |
| | | chatLoading: false, |
| | | memoryId: "" |
| | | currentKnowledge: {} |
| | | }); |
| | | |
| | | const { |
| | |
| | | dialogTitle, |
| | | dialogType, |
| | | viewDialogVisible, |
| | | currentKnowledge, |
| | | filesDialogVisible, |
| | | currentKnowledgeBase, |
| | | fileList, |
| | | uploadedBlobIds, |
| | | savingFiles, |
| | | vectorStatusTimer, |
| | | chatDialogVisible, |
| | | messages, |
| | | inputQuestion, |
| | | chatLoading, |
| | | memoryId |
| | | currentKnowledge |
| | | } = toRefs(data); |
| | | |
| | | // 表åå¼ç¨ |
| | |
| | | // ç¨æ·ç¸å
³ |
| | | const userStore = useUserStore(); |
| | | const userList = ref([]); |
| | | // èå¤©æ¶æ¯å®¹å¨å¼ç¨ |
| | | const chatMessagesRef = ref(); |
| | | |
| | | // æä»¶ä¸ä¼ ç¸å
³ |
| | | const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/common/upload"; |
| | | const uploadHeaders = { Authorization: "Bearer " + getToken() }; |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const tableColumn = ref([ |
| | |
| | | } |
| | | }, |
| | | { |
| | | label: "æä»¶æ°é", |
| | | prop: "fileCount", |
| | | width: 100, |
| | | align: "center" |
| | | }, |
| | | { |
| | | label: "åçæ°é", |
| | | prop: "totalChunkCount", |
| | | width: 100, |
| | | align: "center" |
| | | }, |
| | | { |
| | | label: "ä½¿ç¨æ¬¡æ°", |
| | | prop: "usageCount", |
| | | width: 100, |
| | |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | } |
| | | }, |
| | | { |
| | | name: "æä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openFilesDialog(row); |
| | | } |
| | | }, |
| | | { |
| | | name: "é®ç", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openChatDialog(row); |
| | | } |
| | | }, |
| | | { |
| | |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | |
| | | // â
GET请æ±ä½¿ç¨paramsä¼ å |
| | | listKnowledgeBase({ |
| | | current: page.value.current, |
| | | size: page.value.size, |
| | | title: searchForm.value.title, |
| | | type: searchForm.value.type |
| | | }) |
| | | listKnowledgeBase({...page.value, ...searchForm.value}) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | page.value.total = res.data.total; |
| | | |
| | | // 妿å½å页æ°è¶
è¿æ»é¡µæ°,éç½®å°ç¬¬1页并鿰æ¥è¯¢ |
| | | // 妿å½å页æ°è¶
è¿æ»é¡µæ°ï¼éç½®å°ç¬¬1页并鿰æ¥è¯¢ |
| | | const maxPage = Math.ceil(res.data.total / page.value.size) || 1; |
| | | if (page.value.current > maxPage && maxPage > 0) { |
| | | page.value.current = 1; |
| | | // éæ°æ¥è¯¢ç¬¬1é¡µæ°æ® |
| | | return getList(); |
| | | } |
| | | |
| | | tableData.value = res.data.records; |
| | | }) |
| | | .catch(err => { |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | console.error("æ¥è¯¢ç¥è¯åºå表失败:", err); |
| | | }); |
| | | }) |
| | | }; |
| | | |
| | | // å页å¤ç |
| | |
| | | const submitForm = async () => { |
| | | try { |
| | | await formRef.value.validate(); |
| | | |
| | | // â
POST请æ±ä½¿ç¨dataä¼ å,æç¡®åæ°ç»æ |
| | | const formData = { |
| | | title: form.value.title, |
| | | type: form.value.type, |
| | | scenario: form.value.scenario || "", |
| | | efficiency: form.value.efficiency || "", |
| | | problem: form.value.problem, |
| | | solution: form.value.solution, |
| | | keyPoints: form.value.keyPoints || "", |
| | | creator: form.value.creator || "", |
| | | usageCount: form.value.usageCount || 0 |
| | | }; |
| | | |
| | | if (dialogType.value === "add") { |
| | | // æ°å¢ç¥è¯ |
| | | addKnowledgeBase(formData).then(res => { |
| | | addKnowledgeBase({...form.value}).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("æ·»å æå"); |
| | | closeKnowledgeDialog(); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | console.error("æ·»å ç¥è¯åºå¤±è´¥:", err); |
| | | ElMessage.error(err.msg || "æ·»å 失败"); |
| | | }); |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } else { |
| | | // æ´æ°ç¥è¯ - æ·»å idåæ° |
| | | updateKnowledgeBase({ |
| | | id: form.value.id, |
| | | ...formData |
| | | }).then(res => { |
| | | updateKnowledgeBase({...form.value}).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("æ´æ°æå"); |
| | | closeKnowledgeDialog(); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | console.error("æ´æ°ç¥è¯åºå¤±è´¥:", err); |
| | | ElMessage.error(err.msg || "æ´æ°å¤±è´¥"); |
| | | }); |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } |
| | | } catch (error) { |
| | | console.error("表åéªè¯å¤±è´¥:", error); |
| | |
| | | return; |
| | | } |
| | | |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤,æ¯å¦ç¡®è®¤å é¤?", "å é¤", { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | // â
DELETE请æ±ä½¿ç¨dataä¼ éIDæ°ç» |
| | | // console.log(selectedIds.value); |
| | | delKnowledgeBase(selectedIds.value).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("å 餿å"); |
| | | selectedIds.value = []; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | console.error("å é¤ç¥è¯åºå¤±è´¥:", err); |
| | | ElMessage.error(err.msg || "å é¤å¤±è´¥"); |
| | | }); |
| | | }) |
| | | }).catch(() => { |
| | | // ç¨æ·åæ¶ |
| | | }); |
| | |
| | | const handleExport = () => { |
| | | proxy.download('/knowledgeBase/export', { ...searchForm.value }, 'ç¥è¯åº.xlsx') |
| | | } |
| | | |
| | | // ============ æä»¶ç®¡çç¸å
³ ============ |
| | | |
| | | // æå¼æä»¶ç®¡çå¼¹çª |
| | | const openFilesDialog = (row) => { |
| | | currentKnowledgeBase.value = row; |
| | | filesDialogVisible.value = true; |
| | | loadFileList(); |
| | | }; |
| | | |
| | | // å è½½æä»¶å表 |
| | | const loadFileList = async () => { |
| | | if (!currentKnowledgeBase.value?.id) return; |
| | | |
| | | try { |
| | | const res = await getVectorStatus(currentKnowledgeBase.value.id); |
| | | fileList.value = res.data || []; |
| | | |
| | | // æ£æ¥æ¯å¦æå¤çä¸çæä»¶,妿æåå¯å¨è½®è¯¢ |
| | | const hasProcessing = res.data.some(item => item.vectorStatus === 1); |
| | | if (hasProcessing && !vectorStatusTimer.value) { |
| | | startVectorStatusPolling(); |
| | | } else if (!hasProcessing && vectorStatusTimer.value) { |
| | | stopVectorStatusPolling(); |
| | | } |
| | | } catch (error) { |
| | | console.error("å è½½æä»¶å表失败:", error); |
| | | ElMessage.error("å è½½æä»¶å表失败"); |
| | | } |
| | | }; |
| | | |
| | | // å¼å§è½®è¯¢åéåç¶æ |
| | | const startVectorStatusPolling = () => { |
| | | vectorStatusTimer.value = setInterval(async () => { |
| | | try { |
| | | const res = await getVectorStatus(currentKnowledgeBase.value.id); |
| | | fileList.value = res.data || []; |
| | | |
| | | // æ£æ¥æ¯å¦è¿æå¤çä¸çæä»¶ |
| | | const hasProcessing = res.data.some(item => item.vectorStatus === 1); |
| | | if (!hasProcessing) { |
| | | stopVectorStatusPolling(); |
| | | ElMessage.success("æææä»¶åéåå¤ç宿"); |
| | | } |
| | | } catch (error) { |
| | | console.error("轮询åéåç¶æå¤±è´¥:", error); |
| | | stopVectorStatusPolling(); |
| | | } |
| | | }, 3000); // æ¯3ç§è½®è¯¢ä¸æ¬¡ |
| | | }; |
| | | |
| | | // åæ¢è½®è¯¢åéåç¶æ |
| | | const stopVectorStatusPolling = () => { |
| | | if (vectorStatusTimer.value) { |
| | | clearInterval(vectorStatusTimer.value); |
| | | vectorStatusTimer.value = null; |
| | | } |
| | | }; |
| | | |
| | | // ä¸ä¼ åæ ¡éª |
| | | const beforeUpload = (file) => { |
| | | const allowedTypes = ['.txt', '.md', '.docx', '.xlsx', '.xls', '.pdf']; |
| | | const fileName = file.name.toLowerCase(); |
| | | const isAllowed = allowedTypes.some(type => fileName.endsWith(type)); |
| | | |
| | | if (!isAllowed) { |
| | | ElMessage.error('åªæ¯æ txtãmdãdocxãxlsxãxlsãpdf æ ¼å¼çæä»¶'); |
| | | return false; |
| | | } |
| | | |
| | | const isLt50M = file.size / 1024 / 1024 < 50; |
| | | if (!isLt50M) { |
| | | ElMessage.error('æä»¶å¤§å°ä¸è½è¶
è¿ 50MB'); |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | }; |
| | | |
| | | // ä¸ä¼ æå |
| | | const handleUploadSuccess = (response, file) => { |
| | | console.log("ä¸ä¼ ååº:", response); // è°è¯æ¥å¿ |
| | | |
| | | if (response.code === 200) { |
| | | // â
å端è¿åçæ¯ List<StorageBlobVO>,æä»¥dataæ¯æ°ç» |
| | | if (Array.isArray(response.data) && response.data.length > 0) { |
| | | // åæ°ç»ç¬¬ä¸ä¸ªå
ç´ çid |
| | | const blobId = response.data[0].id; |
| | | if (blobId) { |
| | | uploadedBlobIds.value.push(blobId); |
| | | ElMessage.success(`æä»¶ ${file.name} ä¸ä¼ æå`); |
| | | } else { |
| | | console.error("ä¸ä¼ ååºä¸æªæ¾å°id:", response.data[0]); |
| | | ElMessage.error("ä¸ä¼ 失败: æªè·åå°æä»¶ID"); |
| | | } |
| | | } else { |
| | | console.error("ä¸ä¼ ååºæ ¼å¼é误:", response); |
| | | ElMessage.error("ä¸ä¼ 失败: ååºæ ¼å¼é误"); |
| | | } |
| | | } else { |
| | | ElMessage.error(response.msg || "ä¸ä¼ 失败"); |
| | | } |
| | | }; |
| | | |
| | | // ä¸ä¼ 失败 |
| | | const handleUploadError = (error, file) => { |
| | | ElMessage.error(`æä»¶ ${file.name} ä¸ä¼ 失败`); |
| | | }; |
| | | |
| | | // ä¿åæä»¶å
³è |
| | | const saveFiles = async () => { |
| | | // åæ°æ ¡éª |
| | | if (!currentKnowledgeBase.value?.id) { |
| | | ElMessage.error("ç¥è¯åºä¿¡æ¯å¼å¸¸"); |
| | | return; |
| | | } |
| | | |
| | | if (uploadedBlobIds.value.length === 0) { |
| | | ElMessage.warning("请å
ä¸ä¼ æä»¶"); |
| | | return; |
| | | } |
| | | |
| | | savingFiles.value = true; |
| | | |
| | | try { |
| | | // â
POST请æ±ä½¿ç¨dataä¼ å,æç¡®åæ°ç»æ |
| | | await saveKnowledgeBaseFiles({ |
| | | knowledgeBaseId: currentKnowledgeBase.value.id, // ç¥è¯åºID |
| | | storageBlobIds: uploadedBlobIds.value // æä»¶blob IDæ°ç» |
| | | }); |
| | | |
| | | ElMessage.success("æä»¶å
³èä¿åæå,æ£å¨åå°å¤çåéå"); |
| | | uploadedBlobIds.value = []; |
| | | |
| | | // å»¶è¿å·æ°æä»¶å表,ç»åå°å¤çæ¶é´ |
| | | setTimeout(() => { |
| | | loadFileList(); |
| | | }, 1000); |
| | | } catch (error) { |
| | | console.error("ä¿åæä»¶å
³è失败:", error); |
| | | ElMessage.error("ä¿åæä»¶å
³è失败"); |
| | | } finally { |
| | | savingFiles.value = false; |
| | | } |
| | | }; |
| | | |
| | | // éæ°å¤çåéåçæä»¶ |
| | | const reprocessFile = async (row) => { |
| | | try { |
| | | await reprocessVector(row.id); |
| | | ElMessage.success("已鿰æäº¤åéåä»»å¡"); |
| | | // å»¶è¿å·æ° |
| | | setTimeout(() => { |
| | | loadFileList(); |
| | | }, 1000); |
| | | } catch (error) { |
| | | console.error("éæ°å¤ç失败:", error); |
| | | ElMessage.error("éæ°å¤ç失败"); |
| | | } |
| | | }; |
| | | |
| | | // æ¸
空å¾
ä¿åçæä»¶å表 |
| | | const clearUploadedFiles = () => { |
| | | uploadedBlobIds.value = []; |
| | | ElMessage.success("å·²æ¸
空å¾
ä¿åæä»¶å表"); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const deleteFile = async (row) => { |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | "ç¡®å®è¦å é¤è¯¥æä»¶å?å é¤åå°æ æ³æ¢å¤åéæ°æ®", |
| | | "å é¤ç¡®è®¤", |
| | | { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning" |
| | | } |
| | | ); |
| | | |
| | | // â
DELETE请æ±ä½¿ç¨dataä¼ éIDæ°ç» |
| | | await deleteKnowledgeBaseFile([row.id]); // 注æ: row.idæ¯åéè®°å½ID,䏿¯storageBlobId |
| | | ElMessage.success("å 餿å"); |
| | | loadFileList(); |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | console.error("å é¤æä»¶å¤±è´¥:", error); |
| | | ElMessage.error("å é¤æä»¶å¤±è´¥"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // ç¶æææ¬æ å° |
| | | const getStatusText = (status) => { |
| | | const map = { |
| | | 0: 'å¾
å¤ç', |
| | | 1: 'å¤çä¸', |
| | | 2: '已宿', |
| | | 3: '失败' |
| | | }; |
| | | return map[status] || 'æªç¥'; |
| | | }; |
| | | |
| | | // ç¶ææ ç¾ç±»åæ å° |
| | | const getStatusType = (status) => { |
| | | const map = { |
| | | 0: 'info', |
| | | 1: 'warning', |
| | | 2: 'success', |
| | | 3: 'danger' |
| | | }; |
| | | return map[status] || 'info'; |
| | | }; |
| | | |
| | | // å
³éæä»¶ç®¡çå¼¹çª |
| | | const closeFilesDialog = () => { |
| | | filesDialogVisible.value = false; |
| | | currentKnowledgeBase.value = null; |
| | | fileList.value = []; |
| | | uploadedBlobIds.value = []; |
| | | stopVectorStatusPolling(); // åæ¢è½®è¯¢ |
| | | getList(); // å·æ°ä¸»å表,æ´æ°æä»¶æ°é |
| | | }; |
| | | |
| | | // ============ ç¥è¯åºé®çç¸å
³ ============ |
| | | |
| | | // çæUUIDçfallbackæ¹æ¡ |
| | | const generateUUID = () => { |
| | | if (typeof crypto !== 'undefined' && crypto.randomUUID) { |
| | | return crypto.randomUUID(); |
| | | } |
| | | // fallback: å
¼å®¹ä¸æ¯æ crypto.randomUUID çç¯å¢ |
| | | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { |
| | | const r = Math.random() * 16 | 0; |
| | | const v = c === 'x' ? r : (r & 0x3 | 0x8); |
| | | return v.toString(16); |
| | | }); |
| | | }; |
| | | |
| | | // æå¼é®çå¼¹çª |
| | | const openChatDialog = (row) => { |
| | | currentKnowledgeBase.value = row; |
| | | chatDialogVisible.value = true; |
| | | memoryId.value = generateUUID(); |
| | | messages.value = []; |
| | | inputQuestion.value = ""; |
| | | }; |
| | | |
| | | // åéæ¶æ¯ |
| | | const sendMessage = async () => { |
| | | // åæ°æ ¡éª |
| | | if (!inputQuestion.value.trim()) { |
| | | ElMessage.warning("请è¾å
¥é®é¢"); |
| | | return; |
| | | } |
| | | |
| | | if (!currentKnowledgeBase.value?.id) { |
| | | ElMessage.error("ç¥è¯åºä¿¡æ¯å¼å¸¸"); |
| | | return; |
| | | } |
| | | |
| | | const question = inputQuestion.value.trim(); |
| | | |
| | | // æ·»å ç¨æ·æ¶æ¯ |
| | | messages.value.push({ |
| | | role: 'user', |
| | | content: question |
| | | }); |
| | | |
| | | inputQuestion.value = ""; |
| | | chatLoading.value = true; |
| | | |
| | | // æ»å¨å°åºé¨ |
| | | await nextTick(); |
| | | scrollToBottom(); |
| | | |
| | | try { |
| | | // â
æµå¼è¯·æ±ä½¿ç¨Fetch API |
| | | const response = await knowledgeChat({ |
| | | knowledgeBaseId: currentKnowledgeBase.value.id, // ç¥è¯åºID |
| | | memoryId: memoryId.value, // ä¼è¯ID |
| | | question: question // ç¨æ·é®é¢ |
| | | }); |
| | | |
| | | if (!response.ok) { |
| | | const errorText = await response.text(); |
| | | throw new Error(errorText || '请æ±å¤±è´¥'); |
| | | } |
| | | |
| | | // â
å端è¿å text/stream;charset=utf-8 |
| | | const reader = response.body.getReader(); |
| | | const decoder = new TextDecoder(); |
| | | let aiContent = ''; |
| | | |
| | | messages.value.push({ role: 'assistant', content: '' }); |
| | | |
| | | while (true) { |
| | | const { done, value } = await reader.read(); |
| | | if (done) break; |
| | | |
| | | const text = decoder.decode(value, { stream: true }); // â
æ·»å streamé项 |
| | | aiContent += text; |
| | | messages.value[messages.value.length - 1].content = aiContent; |
| | | |
| | | // æ»å¨å°åºé¨ |
| | | await nextTick(); |
| | | scrollToBottom(); |
| | | } |
| | | |
| | | // 妿AIè¿å空å
容,æ¾ç¤ºæç¤º |
| | | if (!aiContent.trim()) { |
| | | messages.value[messages.value.length - 1].content = 'æ±æ,ç¥è¯åºä¸æªæ¾å°ç¸å
³å
容,请å°è¯å
¶ä»é®é¢ã'; |
| | | } |
| | | } catch (error) { |
| | | console.error("é®ç请æ±å¤±è´¥:", error); |
| | | ElMessage.error("é®ç请æ±å¤±è´¥,请ç¨åéè¯"); |
| | | messages.value.push({ |
| | | role: 'assistant', |
| | | content: 'æ±æ,åçäºé误,请ç¨åéè¯' |
| | | }); |
| | | } finally { |
| | | chatLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // æ¸
ç©ºå¯¹è¯ |
| | | const clearMessages = () => { |
| | | ElMessageBox.confirm( |
| | | "ç¡®å®è¦æ¸
空ææå¯¹è¯è®°å½å?", |
| | | "æ¸
空确认", |
| | | { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning" |
| | | } |
| | | ).then(() => { |
| | | messages.value = []; |
| | | memoryId.value = generateUUID(); // éæ°çæä¼è¯ID |
| | | ElMessage.success("对è¯å·²æ¸
空"); |
| | | }).catch(() => { |
| | | // ç¨æ·åæ¶ |
| | | }); |
| | | }; |
| | | |
| | | // æ»å¨å°åºé¨ |
| | | const scrollToBottom = () => { |
| | | if (chatMessagesRef.value) { |
| | | chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight; |
| | | } |
| | | }; |
| | | |
| | | // å
³éé®çå¼¹çª |
| | | const closeChatDialog = () => { |
| | | chatDialogVisible.value = false; |
| | | currentKnowledgeBase.value = null; |
| | | messages.value = []; |
| | | inputQuestion.value = ""; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | /* æä»¶ç®¡çæ ·å¼ */ |
| | | .file-manager { |
| | | padding: 20px 0; |
| | | } |
| | | |
| | | .upload-section { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uploaded-list { |
| | | margin-top: 16px; |
| | | padding: 12px; |
| | | background: #f0f9ff; |
| | | border-radius: 6px; |
| | | border: 1px solid #b3d8ff; |
| | | } |
| | | |
| | | .uploaded-tip { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | color: #409eff; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | /* ç¥è¯åºé®çæ ·å¼ */ |
| | | .knowledge-chat { |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: 500px; |
| | | } |
| | | |
| | | .chat-header { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .chat-messages { |
| | | flex: 1; |
| | | overflow-y: auto; |
| | | padding: 16px; |
| | | background: #f5f7fa; |
| | | border-radius: 8px; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .message { |
| | | margin-bottom: 16px; |
| | | max-width: 80%; |
| | | } |
| | | |
| | | .message.user { |
| | | margin-left: auto; |
| | | text-align: right; |
| | | } |
| | | |
| | | .message.assistant { |
| | | margin-right: auto; |
| | | } |
| | | |
| | | .message-role { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .message-content { |
| | | display: inline-block; |
| | | padding: 10px 14px; |
| | | border-radius: 8px; |
| | | line-height: 1.6; |
| | | word-wrap: break-word; |
| | | white-space: pre-wrap; |
| | | } |
| | | |
| | | .message.user .message-content { |
| | | background: #409eff; |
| | | color: white; |
| | | } |
| | | |
| | | .message.assistant .message-content { |
| | | background: white; |
| | | color: #303133; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .typing { |
| | | animation: typing 1.5s infinite; |
| | | } |
| | | |
| | | @keyframes typing { |
| | | 0%, 50%, 100% { |
| | | opacity: 1; |
| | | } |
| | | 25%, 75% { |
| | | opacity: 0.5; |
| | | } |
| | | } |
| | | |
| | | .chat-input { |
| | | margin-top: auto; |
| | | } |
| | | |
| | | .chat-actions { |
| | | margin-top: 8px; |
| | | text-align: right; |
| | | } |
| | | |
| | | </style> |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¼å§æ¶é´" prop="startTime"> |
| | | <el-time-picker |
| | | <el-select |
| | | v-model="meetingForm.startTime" |
| | | placeholder="è¯·éæ©å¼å§æ¶é´" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | :disabled-hours="disabledStartHours" |
| | | :disabled-minutes="disabledStartMinutes" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in startTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»ææ¶é´" prop="endTime"> |
| | | <el-time-picker |
| | | <el-select |
| | | v-model="meetingForm.endTime" |
| | | placeholder="è¯·éæ©ç»ææ¶é´" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | :disabled-hours="disabledEndHours" |
| | | :disabled-minutes="disabledEndMinutes" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in endTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | // åå·¥å表 |
| | | const employees = ref([]) |
| | | |
| | | // æ¶é´é项ï¼ä»¥åå°æ¶ä¸ºé´éï¼ |
| | | const timeOptions = ref([]) |
| | | |
| | | const getTimeInMinutes = (time) => { |
| | | if (!time) return -1 |
| | | const [hour, minute] = time.split(':').map(Number) |
| | |
| | | callback() |
| | | return |
| | | } |
| | | |
| | | if (isToday(meetingForm.meetingDate)) { |
| | | const now = new Date() |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes() |
| | | if (getTimeInMinutes(value) > currentMinutes) { |
| | | callback(new Error('å½å¤©å¼å§æ¶é´ä¸è½æäºå½åæ¶é´')) |
| | | return |
| | | } |
| | | } |
| | | |
| | | callback() |
| | | } |
| | | |
| | |
| | | callback() |
| | | return |
| | | } |
| | | |
| | | if (getTimeInMinutes(value) <= getTimeInMinutes(meetingForm.startTime)) { |
| | | callback(new Error('ç»ææ¶é´å¿
须大äºå¼å§æ¶é´')) |
| | | return |
| | | } |
| | | |
| | | callback() |
| | | } |
| | | |
| | | // è¡¨åæ ¡éªè§å |
| | | const rules = { |
| | | title: [{required: true, message: '请è¾å
¥ä¼è®®ä¸»é¢', trigger: 'blur'}], |
| | | roomId: [{required: true, message: 'è¯·éæ©ä¼è®®å®¤', trigger: 'change'}], |
| | |
| | | participants: [{required: true, message: 'è¯·éæ©åä¼äººå', trigger: 'change'}] |
| | | } |
| | | |
| | | // æ¶é´éæ©å¨ç¦ç¨é»è¾ |
| | | const disabledStartHours = () => { |
| | | const hours = [] |
| | | for (let h = 0; h < 24; h++) { |
| | | if (h < 8 || h > 18) hours.push(h) |
| | | const startTimeOptions = computed(() => { |
| | | if (!isToday(meetingForm.meetingDate)) { |
| | | return timeOptions.value |
| | | } |
| | | if (isToday(meetingForm.meetingDate)) { |
| | | const now = new Date() |
| | | for (let h = 8; h < now.getHours(); h++) { |
| | | if (!hours.includes(h)) hours.push(h) |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes() |
| | | return timeOptions.value.filter(item => getTimeInMinutes(item.value) <= currentMinutes) |
| | | }) |
| | | |
| | | const endTimeOptions = computed(() => { |
| | | if (!meetingForm.startTime) { |
| | | return timeOptions.value |
| | | } |
| | | const startMinutes = getTimeInMinutes(meetingForm.startTime) |
| | | return timeOptions.value.filter(item => getTimeInMinutes(item.value) > startMinutes) |
| | | }) |
| | | |
| | | // åå§åæ¶é´é项 |
| | | const initTimeOptions = () => { |
| | | const options = [] |
| | | const now = new Date() |
| | | const currentHour = now.getHours() |
| | | const currentMinute = now.getMinutes() |
| | | // meetingDate æ¯ "yyyy-MM-dd" |
| | | const meetingDate = new Date(meetingForm.meetingDate) |
| | | |
| | | const isSameDay = |
| | | now.getFullYear() === meetingDate.getFullYear() && |
| | | now.getMonth() === meetingDate.getMonth() && |
| | | now.getDate() === meetingDate.getDate() |
| | | |
| | | console.log('æ¯å¦åä¸å¤©:', isSameDay) |
| | | for (let hour = 8; hour <= 18; hour++) { |
| | | // å¼å§æ¶é´å¿
é¡»æäºå½åæ¶é´ |
| | | if (hour < currentHour && isSameDay) { |
| | | continue |
| | | } |
| | | if (hour === currentHour && currentMinute > 30 && isSameDay) { |
| | | continue |
| | | } |
| | | // æ¯ä¸ªå°æ¶æ·»å 两个éé¡¹ï¼æ´ç¹ååç¹ |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:00`, |
| | | label: `${hour.toString().padStart(2, '0')}:00` |
| | | }) |
| | | |
| | | if (hour < 18) { // 18:00ä¹å没æåç¹é项 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:30`, |
| | | label: `${hour.toString().padStart(2, '0')}:30` |
| | | }) |
| | | } |
| | | } |
| | | return hours |
| | | timeOptions.value = options |
| | | } |
| | | |
| | | const disabledStartMinutes = (hour) => { |
| | | const minutes = [] |
| | | for (let m = 0; m < 60; m++) { |
| | | if (m !== 0 && m !== 30) minutes.push(m) |
| | | watch(() => meetingForm.meetingDate, () => { |
| | | if (meetingForm.startTime && !startTimeOptions.value.some(item => item.value === meetingForm.startTime)) { |
| | | meetingForm.startTime = '' |
| | | } |
| | | if (isToday(meetingForm.meetingDate)) { |
| | | const now = new Date() |
| | | if (hour === now.getHours()) { |
| | | if (now.getMinutes() >= 30) { |
| | | minutes.push(0) |
| | | } |
| | | } |
| | | } |
| | | return minutes |
| | | } |
| | | |
| | | const disabledEndHours = () => { |
| | | const hours = [] |
| | | for (let h = 0; h < 24; h++) { |
| | | if (h < 8 || h > 18) hours.push(h) |
| | | if (meetingForm.endTime && !endTimeOptions.value.some(item => item.value === meetingForm.endTime)) { |
| | | meetingForm.endTime = '' |
| | | } |
| | | if (meetingForm.startTime) { |
| | | const startHour = parseInt(meetingForm.startTime.split(':')[0]) |
| | | for (let h = 8; h < startHour; h++) { |
| | | if (!hours.includes(h)) hours.push(h) |
| | | meetingFormRef.value?.validateField('startTime') |
| | | } |
| | | if (meetingForm.endTime) { |
| | | meetingFormRef.value?.validateField('endTime') |
| | | } |
| | | return hours |
| | | } |
| | | |
| | | const disabledEndMinutes = (hour) => { |
| | | const minutes = [] |
| | | for (let m = 0; m < 60; m++) { |
| | | if (m !== 0 && m !== 30) minutes.push(m) |
| | | } |
| | | if (meetingForm.startTime) { |
| | | const startHour = parseInt(meetingForm.startTime.split(':')[0]) |
| | | const startMinute = parseInt(meetingForm.startTime.split(':')[1]) |
| | | if (hour === startHour) { |
| | | if (startMinute >= 0) minutes.push(0) |
| | | if (startMinute >= 30) minutes.push(30) |
| | | // only keep minutes > startMinute |
| | | for (let m = 0; m <= startMinute; m++) { |
| | | if (!minutes.includes(m)) minutes.push(m) |
| | | } |
| | | } |
| | | } |
| | | return minutes |
| | | } |
| | | initTimeOptions() |
| | | }) |
| | | |
| | | watch(() => meetingForm.startTime, () => { |
| | | if (meetingForm.endTime && getTimeInMinutes(meetingForm.endTime) <= getTimeInMinutes(meetingForm.startTime)) { |
| | |
| | | |
| | | // 模ææäº¤æä½ |
| | | ElMessage.success(`${getCurrentTypeName()}æäº¤æå`) |
| | | |
| | | // æ ¹æ®ä¸åç±»åæ§è¡ä¸åæä½ |
| | | switch (currentType.value) { |
| | | case 'approval': |
| | | ElMessage.info('ä¼è®®å·²æäº¤å®¡æ¹æµç¨') |
| | | break |
| | | case 'department': |
| | | ElMessage.info('é¨é¨çº§ä¼è®®ç³è¯·å·²æäº¤') |
| | | break |
| | | case 'notification': |
| | | ElMessage.info('ä¼è®®éç¥å·²åå¸') |
| | | break |
| | | } |
| | | resetForm() |
| | | }) |
| | | |
| | |
| | | |
| | | // 页é¢å è½½æ¶åå§å |
| | | onMounted(() => { |
| | | initTimeOptions() |
| | | getRoomEnum().then(res => { |
| | | meetingRooms.value = res.data |
| | | }) |
| | |
| | | <el-table-column prop="location" label="ä¼è®®å°ç¹" align="center" width="150"/> |
| | | <el-table-column prop="participants" label="åä¼äººæ°" align="center" width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.staffCount }}人 |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="审æ¹ç¶æ" align="center" width="120"> |
| | |
| | | let resp = await getExamineList({...searchForm, ...queryParams}) |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = room ? `${room.name}(${room.location})` : '' |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.length |
| | | it.staffCount = staffs.size |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | return { |
| | |
| | | if (endIdx === -1) { |
| | | endIdx = timeSlots.value.length |
| | | } |
| | | // å¾å延伏䏿 ¼ï¼è®©ä¼è®®æ ¼åè¦çå°ç»ææ¶é´åä¸ |
| | | const displayEndIdx = Math.min(endIdx + 1, timeSlots.value.length) |
| | | if (startIdx !== -1) { |
| | | console.log('endIdx111', endIdx) |
| | | if (startIdx !== -1 && endIdx !== -1) { |
| | | // æ 记被å ç¨çæ¶é´æ®µ |
| | | for (let i = startIdx; i < displayEndIdx; i++) { |
| | | for (let i = startIdx; i < endIdx; i++) { |
| | | occupiedSlots.add(timeSlots.value[i].value) |
| | | } |
| | | |
| | |
| | | cells.push({ |
| | | type: 'meeting', |
| | | meeting: meeting, |
| | | span: displayEndIdx - startIdx, |
| | | span: endIdx - startIdx, |
| | | startTime: meeting.startTime, |
| | | endTime: meeting.endTime |
| | | }) |
| | |
| | | <el-table-column prop="location" label="ä¼è®®å°ç¹" align="center" width="150"/> |
| | | <el-table-column prop="participants" label="åä¼äººæ°" align="center" width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.staffCount }}人 |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="åå¸ç¶æ" align="center" width="120"> |
| | |
| | | let resp = await getMeetingPublish({...searchForm, ...queryParams}) |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = room ? `${room.name}(${room.location})` : '' |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.length |
| | | it.staffCount = staffs.size |
| | | it.status = it.publishStatus |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | |
| | | <el-table-column prop="location" label="ä¼è®®å°ç¹" align="center" width="150" /> |
| | | <el-table-column prop="participants" label="åä¼äººæ°" align="center" width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.staffCount }}人 |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" align="center" width="200" fixed="right"> |
| | |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }) |
| | | meetingList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = room ? `${room.name}(${room.location})` : '' |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.length |
| | | it.staffCount = staffs.size |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => { |
| | | return { |
| | |
| | | <p><strong>主æäººï¼</strong>${row.host}</p> |
| | | <p><strong>åä¼äººåï¼</strong></p> |
| | | <ol> |
| | | ${(row.participants || []).map(p => `<li>${p?.name || ''}</li>`).join('')} |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join('')} |
| | | </ol> |
| | | <p><strong>ä¼è®®å
容ï¼</strong></p> |
| | | <ol> |
| | |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [ |
| | | { required: false, message: "请è¾å
¥", trigger: "blur" }, |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | if (operationType.value == "add") { |
| | | form.value.purchaseContractNumber = ""; |
| | | createPurchaseNo().then(res => { |
| | | form.value.purchaseContractNumber = res.data; |
| | | }); |
| | | } |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | |
| | | } |
| | | // æäº¤è¡¨å |
| | | const submitForm = n => { |
| | | proxy.$refs["formRef"].validate(async valid => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | if (productData.value.length > 0) { |
| | | form.value.productData = proxy.HaveJson(productData.value); |
| | |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 2; |
| | | form.value.approvalStatus = n; |
| | | |
| | | // 妿éè´ååå·ä¸ºç©ºï¼åæ ¹æ®å½å
¥æ¥æèªå¨çæ |
| | | if (!form.value.purchaseContractNumber) { |
| | | try { |
| | | const purchaseNoRes = await createPurchaseNo(form.value.entryDate); |
| | | if (purchaseNoRes?.data) { |
| | | form.value.purchaseContractNumber = purchaseNoRes.data; |
| | | } |
| | | } catch (error) { |
| | | console.error("çæéè´ååå·å¤±è´¥:", error); |
| | | proxy.$modal.msgWarning("çæéè´ååå·å¤±è´¥"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | addOrEditPurchase(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | |
| | | formatData: (v) => (v === "active" ? "çæä¸" : "å·²åºæ¢"), |
| | | formatType: (v) => (v === "active" ? "success" : "info"), |
| | | }, |
| | | { label: "已读人æ°", prop: "readCount", width: 100 }, |
| | | // { label: "已读人æ°", prop: "readCount", width: 100 }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | <!-- ç¨å°ç³è¯·ç®¡ç --> |
| | | <div class="tab-content"> |
| | | <el-row :gutter="20" class="mb-20 "> |
| | |
| | | const sealFormRef = ref() |
| | | const userList = ref([]) |
| | | const sealForm = reactive({ |
| | | id: null, |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | |
| | | |
| | | // ç¨å°ç³è¯·è¡¨æ ¼åé
ç½®ï¼éå¨ getStatusText/getSealTypeText çä¹åå®ä¹ï¼ |
| | | const sealTableColumn = ref([ |
| | | { label: 'ç³è¯·ç¼å·', prop: 'applicationNum' }, |
| | | { label: 'ç³è¯·ç¼å·', prop: 'applicationNum',}, |
| | | { label: 'ç³è¯·æ é¢', prop: 'title', showOverflowTooltip: true }, |
| | | { label: 'ç³è¯·äºº', prop: 'createUserName' }, |
| | | { label: 'ç³è¯·äºº', prop: 'createUserName', }, |
| | | { label: 'æå±é¨é¨', prop: 'department', width: 150 }, |
| | | { |
| | | label: 'ç¨å°ç±»å', |
| | |
| | | formatData: (v) => getStatusText(v), |
| | | formatType: (v) => getStatusType(v) |
| | | }, |
| | | { label: '审æ¹äºº', prop: 'approveUserName', width: 100 }, |
| | | { |
| | | dataType: 'action', |
| | | label: 'æä½', |
| | | width: 250, |
| | | width: 200, |
| | | fixed: 'right', |
| | | align: 'center', |
| | | operation: [ |
| | | { |
| | | name: '审æ¹', |
| | | clickFun: (row) => approveSeal(row), |
| | | showHide: (row) => row.status === 'pending' && Number(userStore.id) === row.approveUserId |
| | | showHide: (row) => row.status === 'pending' |
| | | }, |
| | | { |
| | | name: 'æç»', |
| | | clickFun: (row) => rejectSeal(row), |
| | | showHide: (row) => row.status === 'pending' && Number(userStore.id) === row.approveUserId |
| | | }, |
| | | { |
| | | name: 'éæ°ç³è¯·', |
| | | clickFun: (row) => reapplySeal(row), |
| | | showHide: (row) => row.status === 'rejected' && Number(userStore.id) === row.createUser |
| | | showHide: (row) => row.status === 'pending' |
| | | }, |
| | | { name: '详æ
', clickFun: (row) => viewSealDetail(row) } |
| | | ] |
| | |
| | | const searchSealApplications = () => { |
| | | page.current = 1 |
| | | getSealApplicationList() |
| | | |
| | | // ElMessage.success('æç´¢å®æ') |
| | | } |
| | | // éç½®å°ç« ç³è¯·æç´¢ |
| | | const resetSealSearch = () => { |
| | |
| | | sealSearchForm.applicationNum = '' |
| | | searchSealApplications() |
| | | } |
| | | |
| | | // éæ°ç³è¯·ç¨å° |
| | | const reapplySeal = (row) => { |
| | | // é¢å¡«è¡¨åæ°æ® |
| | | Object.assign(sealForm, { |
| | | id: row.id, |
| | | applicationNum: row.applicationNum, |
| | | title: row.title, |
| | | sealType: row.sealType, |
| | | reason: row.reason, |
| | | approveUserId: row.approveUserId, |
| | | urgency: row.urgency || 'normal', |
| | | status: 'pending', |
| | | storageBlobDTOs: row.storageBlobVOList || [] |
| | | }) |
| | | showSealApplyDialog.value = true |
| | | } |
| | | |
| | | // æäº¤ç¨å°ç³è¯· |
| | | const submitSealApplication = async () => { |
| | | try { |
| | | await sealFormRef.value.validate() |
| | | const request = sealForm.id ? updateSealApplication : addSealApplication |
| | | request(sealForm).then(res => { |
| | | addSealApplication(sealForm).then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success(sealForm.id ? 'éæ°ç³è¯·æå' : 'ç³è¯·æäº¤æå') |
| | | ElMessage.success('ç³è¯·æäº¤æå') |
| | | closeSealApplyDialog() |
| | | getSealApplicationList() |
| | | Object.assign(sealForm, { |
| | | id: null, |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | |
| | | }).catch(err => { |
| | | console.log(err.msg) |
| | | }) |
| | | |
| | | } catch (error) { |
| | | } |
| | | } |
| | | |
| | | // å
³éç¨å°ç³è¯·å¯¹è¯æ¡ |
| | | const closeSealApplyDialog = () => { |
| | | // æ¸
ç©ºè¡¨åæ°æ® |
| | | Object.assign(sealForm, { |
| | | id: null, |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | |
| | | inputErrorMessage: 'æç»åå ä¸è½ä¸ºç©º' |
| | | }).then(({ value }) => { |
| | | row.status = 'rejected' |
| | | row.reason = value |
| | | updateSealApplication(row).then(res => { |
| | | if (res.code == 200) { |
| | | ElMessage.success('å·²æç»ç³è¯·') |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è¯åï¼" prop="rating"> |
| | | <el-rate |
| | | v-model="form.rating" |
| | | :disabled="operationType === 'view'" |
| | | :max="5" |
| | | show-text |
| | | :texts="['é叏䏿»¡æ', '䏿»¡æ', 'ä¸è¬', '满æ', 'é常满æ']" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="è¯ä»·å
容æè¿°ï¼" prop="evaluation"> |
| | | <el-input |
| | | v-model="form.evaluation" |
| | | placeholder="请è¾å
¥è¯ä»·å
容æè¿°" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | type="textarea" |
| | | :rows="3" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | |
| | | disposeUserId: "", |
| | | disDate: "", |
| | | disRes: "", |
| | | rating: null, |
| | | evaluation: "", |
| | | }, |
| | | rules: { |
| | | feedbackDate: [{required: true, message: "è¯·éæ©", trigger: "change"}], |
| | |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | <view-dia ref="viewDia"></view-dia> |
| | | <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="after_sales_service" :record-id="recordId" /> |
| | | </div> |
| | | </template> |
| | |
| | | <script setup> |
| | | import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick, defineAsyncComponent} from "vue"; |
| | | import FormDia from "@/views/customerService/afterSalesHandling/components/formDia.vue"; |
| | | import ViewDia from "@/views/customerService/components/viewDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | afterSalesServiceListPage, |
| | |
| | | align: "center" |
| | | }, |
| | | { |
| | | label: "æ°é", |
| | | prop: "productModelQuantities", |
| | | align: "center", |
| | | formatData: params => { |
| | | if (!params) return "0"; |
| | | return String(params).split(',').reduce((sum, val) => sum + (Number(val) || 0), 0); |
| | | } |
| | | }, |
| | | { |
| | | label: "é®é¢æè¿°", |
| | | prop: "proDesc", |
| | | width:300, |
| | |
| | | } |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openForm("view", row); |
| | | viewDia.value?.openDialog(row); |
| | | }, |
| | | }, |
| | | // TODO ä¸ºåæ¥åæ·»å ç |
| | |
| | | selectedRows.value = selection; |
| | | }; |
| | | const formDia = ref() |
| | | const viewDia = ref() |
| | | const fileListRef = ref(null) |
| | | const fileListDialogVisible = ref(false) |
| | | const currentFileRow = ref(null) |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="dialogVisible" title="å®åå详æ
" width="80%" @close="closeDia"> |
| | | <div v-loading="loading"> |
| | | <span class="descriptions">åºç¡èµæ</span> |
| | | <el-descriptions :column="4" border style="margin-top: 10px;"> |
| | | <el-descriptions-item label="客æ·åç§°">{{ detail.customerName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®åç±»å">{{ getDictLabel(classificationOptions, detail.serviceType) }}</el-descriptions-item> |
| | | <el-descriptions-item label="å
³èéå®åå·">{{ detail.salesContractNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç´§æ¥ç¨åº¦">{{ getDictLabel(degreeOfUrgencyOptions, detail.urgency) }}</el-descriptions-item> |
| | | <el-descriptions-item label="å·¥åç¼å·">{{ detail.afterSalesServiceNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¤çç¶æ"> |
| | | <el-tag :type="detail.status === 1 ? 'danger' : 'success'" size="small"> |
| | | {{ detail.status === 1 ? 'å¾
å¤ç' : 'å·²å¤ç' }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç»è®°äºº">{{ detail.checkNickName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å馿¥æ">{{ detail.feedbackDate || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·è¯æ±" :span="4">{{ detail.proDesc || '-' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div style="margin-top: 20px;"> |
| | | <span class="descriptions">å¤çä¿¡æ¯</span> |
| | | <el-descriptions :column="3" border style="margin-top: 10px;"> |
| | | <el-descriptions-item label="å¤ç人">{{ detail.disposeNickName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¤çæ¥æ">{{ detail.disDate || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¤çç»æ" :span="3">{{ detail.disRes || '-' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | |
| | | <div style="margin-top: 20px;"> |
| | | <span class="descriptions">å
³è产å</span> |
| | | <el-table :data="tableData" border style="width: 100%; margin-top: 10px;"> |
| | | <el-table-column type="index" label="åºå·" width="60" align="center" /> |
| | | <el-table-column prop="productCategory" label="产å大类" align="center" /> |
| | | <el-table-column prop="specificationModel" label="è§æ ¼åå·" align="center" /> |
| | | <el-table-column prop="unit" label="åä½" align="center" /> |
| | | <el-table-column prop="expressCompany" label="å¿«éå
¬å¸" align="center" /> |
| | | <el-table-column prop="expressNumber" label="å¿«éåå·" align="center" /> |
| | | <el-table-column prop="shippingCarNumber" label="å货车ç" align="center" /> |
| | | <el-table-column prop="shippingDate" label="åè´§æ¥æ" align="center" /> |
| | | <el-table-column prop="quantity" label="å®åæ°é" align="center" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, getCurrentInstance } from 'vue'; |
| | | import { getAfterSalesServiceById } from '@/api/customerService/index.js'; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const dialogVisible = ref(false); |
| | | const loading = ref(false); |
| | | const detail = ref({}); |
| | | const tableData = ref([]); |
| | | |
| | | const { post_sale_waiting_list, degree_of_urgency } = proxy.useDict( |
| | | "post_sale_waiting_list", |
| | | "degree_of_urgency" |
| | | ); |
| | | const classificationOptions = computed(() => post_sale_waiting_list?.value || []); |
| | | const degreeOfUrgencyOptions = computed(() => degree_of_urgency?.value || []); |
| | | |
| | | const getDictLabel = (options, value) => { |
| | | if (!value) return '-'; |
| | | const item = options.find(i => String(i.value) === String(value)); |
| | | return item ? item.label : value; |
| | | }; |
| | | |
| | | const openDialog = (row) => { |
| | | dialogVisible.value = true; |
| | | loading.value = true; |
| | | detail.value = {}; |
| | | tableData.value = []; |
| | | |
| | | getAfterSalesServiceById(row.id).then(res => { |
| | | loading.value = false; |
| | | if (res.code === 200) { |
| | | detail.value = res.data || {}; |
| | | let productData = res.data?.salesLedgerDto?.productData || []; |
| | | const selectedIds = res.data.productModelIds ? String(res.data.productModelIds).split(",") : []; |
| | | tableData.value = productData.filter(item => selectedIds.includes(String(item.id))); |
| | | } |
| | | }).catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const closeDia = () => { |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | defineExpose({ |
| | | openDialog |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .descriptions { |
| | | display: inline-block; |
| | | font-size: 1rem; |
| | | font-weight: 600; |
| | | padding-left: 12px; |
| | | position: relative; |
| | | } |
| | | .descriptions::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 1rem; |
| | | background-color: #002fa7; |
| | | border-radius: 2px; |
| | | } |
| | | </style> |
| | |
| | | {{ getShippingStatusText(row) }} |
| | | </el-tag> |
| | | </template> |
| | | <template #quantity="{ row }"> |
| | | <el-input-number v-model="row.quantity" :min="0" size="small" /> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </div> |
| | |
| | | salesContractNo: "", |
| | | proDesc: "", |
| | | customerName: "", |
| | | productModelQuantities: "", |
| | | }, |
| | | rules: { |
| | | customerName: [ |
| | |
| | | align: "center", |
| | | }, |
| | | { label: "åè´§æ¥æ", prop: "shippingDate", minWidth: 100, align: "center" }, |
| | | { label: "æ°é", prop: "quantity", width: 100 }, |
| | | { label: "ç¨ç(%)", prop: "taxRate", width: 100 }, |
| | | { |
| | | label: "å«ç¨åä»·(å
)", |
| | | prop: "taxInclusiveUnitPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | label: "å«ç¨æ»ä»·(å
)", |
| | | prop: "taxInclusiveTotalPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | }, |
| | | { |
| | | label: "ä¸å«ç¨æ»ä»·(å
)", |
| | | prop: "taxExclusiveTotalPrice", |
| | | width: 160, |
| | | formatData: formatCurrency, |
| | | label: "æ°é", |
| | | prop: "quantity", |
| | | width: 140, |
| | | dataType: "slot", |
| | | slot: "quantity", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | |
| | | form.value.customerId = null; |
| | | } |
| | | getSalesLedger({ |
| | | customerName: form.value.customerName, |
| | | customerId: form.value.customerId, |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | associatedSalesOrderNumberOptions.value = res.data.records.map(item => ({ |
| | |
| | | form.value = { ...row }; |
| | | if (form.value.customerName) { |
| | | const res = await getSalesLedger({ |
| | | customerName: form.value.customerName, |
| | | customerId: form.value.customerId, |
| | | }); |
| | | if (res?.code === 200) { |
| | | console.log(res); |
| | |
| | | id: item.id, |
| | | }) |
| | | ); |
| | | const opt = associatedSalesOrderNumberOptions.value.find( |
| | | item => item.value === form.value.salesContractNo |
| | | ); |
| | | if (opt) { |
| | | let restoredData = (opt.productData || []).map(normalizeProductRow); |
| | | const selectedIds = form.value.productModelIds ? String(form.value.productModelIds).split(",") : []; |
| | | const quantities = form.value.productModelQuantities ? String(form.value.productModelQuantities).split(",") : []; |
| | | tableData.value = restoredData.filter(item => selectedIds.includes(String(item.id))).map(item => { |
| | | let qIndex = selectedIds.indexOf(String(item.id)); |
| | | if (qIndex !== -1 && qIndex < quantities.length && quantities[qIndex] !== "") { |
| | | item.quantity = Number(quantities[qIndex]); |
| | | } |
| | | return item; |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | console.log(form.value); |
| | |
| | | form.value.productModelIds = tableData.value |
| | | .map(item => item.id) |
| | | .join(","); |
| | | form.value.productModelQuantities = tableData.value |
| | | .map(item => item.quantity || 0) |
| | | .join(","); |
| | | if (operationType.value === "add") { |
| | | afterSalesServiceAdd(form.value).then(response => { |
| | | proxy.$modal.msgSuccess("æ°å¢æå"); |
| | |
| | | </div> |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | <view-dia ref="viewDia"></view-dia> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | nextTick, |
| | | } from "vue"; |
| | | import FormDia from "@/views/customerService/feedbackRegistration/components/formDia.vue"; |
| | | import ViewDia from "@/views/customerService/components/viewDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | afterSalesServiceDelete, |
| | |
| | | { |
| | | icon: markRaw(FolderOpened), |
| | | count: 0, |
| | | label: "å·²å¤ç", |
| | | label: "å¾
å¤ç", |
| | | color: "#ff9a2e", |
| | | bgColor: "#fff5e6", |
| | | }, |
| | | { |
| | | icon: markRaw(UserFilled), |
| | | count: 0, |
| | | label: "已宿", |
| | | label: "å·²å¤ç", |
| | | color: "#00b42a", |
| | | bgColor: "#e6f7ed", |
| | | }, |
| | |
| | | align: "center", |
| | | }, |
| | | { |
| | | label: "æ°é", |
| | | prop: "productModelQuantities", |
| | | align: "center", |
| | | formatData: params => { |
| | | if (!params) return "0"; |
| | | return String(params).split(',').reduce((sum, val) => sum + (Number(val) || 0), 0); |
| | | } |
| | | }, |
| | | { |
| | | label: "客æ·è¯æ±", |
| | | prop: "proDesc", |
| | | width: 300, |
| | |
| | | dataType: "action", |
| | | label: "æä½", |
| | | fixed: "right", |
| | | width: 180, |
| | | align: "center", |
| | | operation: [ |
| | | { |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: row => { |
| | | viewDia.value?.openDialog(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | |
| | | selectedRows.value = selection; |
| | | }; |
| | | const formDia = ref(); |
| | | const viewDia = ref(); |
| | | |
| | | // åå
¸è·å |
| | | /* |
| | |
| | | if (res.code === 200) { |
| | | const statsData = Array.isArray(res.data) ? res.data : []; |
| | | statsList.value[0].count = getStatsCountByStatus(statsData, 3); |
| | | statsList.value[1].count = getStatsCountByStatus(statsData, 2); |
| | | statsList.value[2].count = getStatsCountByStatus(statsData, 1); |
| | | statsList.value[1].count = getStatsCountByStatus(statsData, 1); |
| | | statsList.value[2].count = getStatsCountByStatus(statsData, 2); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | form.value = {...row} |
| | | form.value.inspector = form.value.inspectorIds.split(',').map(Number) |
| | | |
| | | // å¤çWEEKLYé¢ççåæ¾ï¼å°frequencyDetailæå为weekåtime |
| | | if (form.value.frequencyType === 'WEEKLY' && form.value.frequencyDetail) { |
| | | const parts = form.value.frequencyDetail.split(',') |
| | | form.value.week = parts[0] || '' |
| | | form.value.time = parts[1] || '' |
| | | form.value.frequencyDetail = '' |
| | | } |
| | | |
| | | // 妿æè®¾å¤IDï¼èªå¨è®¾ç½®è®¾å¤ä¿¡æ¯ |
| | | if (form.value.taskId) { |
| | | setDeviceModel(form.value.taskId); |
| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | title="ä¸ä¼ å·¡æ£è®°å½" |
| | | <FormDialog v-model="dialogVisible" |
| | | :title="operationType === 'view' ? 'å·¡æ£è®°å½è¯¦æ
' : 'ä¸ä¼ å·¡æ£è®°å½'" |
| | | width="980px" |
| | | :operation-type="operationType" |
| | | @close="handleClose" |
| | | @cancel="handleClose" |
| | | > |
| | | @cancel="handleClose"> |
| | | <main class="upload-content"> |
| | | <el-card v-if="taskInfo" class="section-card"> |
| | | <el-descriptions :column="1" border> |
| | | <el-card v-if="taskInfo" |
| | | class="section-card"> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="å·¡æ£ä»»å¡åç§°"> |
| | | {{ taskInfo.taskName || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å·¡æ£é¡¹ç®"> |
| | | {{ taskInfo.inspectionProject || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ§è¡å·¡æ£äºº"> |
| | | <template v-if="formattedInspector && formattedInspector.length"> |
| | | <el-tag v-for="tag in formattedInspector" |
| | | :key="tag" |
| | | size="small" |
| | | style="margin-right: 4px"> |
| | | {{ tag }} |
| | | </el-tag> |
| | | </template> |
| | | <span v-else>--</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="颿¬¡"> |
| | | {{ formatFrequencyType(taskInfo.frequencyType) || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å¼å§æ¥æä¸æ¶é´"> |
| | | {{ formatFrequencyDetail(taskInfo.frequencyDetail) || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç»è®°äºº"> |
| | | {{ taskInfo.registrant || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç»è®°æ¥æ"> |
| | | {{ formatDateTime(taskInfo.createTime) || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨"> |
| | | {{ taskInfo.remarks || "-" }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-card> |
| | | |
| | | <el-card class="section-card"> |
| | | <h3>å·¡æ£ç¶æ</h3> |
| | | <el-radio-group v-model="hasException"> |
| | | <el-radio-group v-model="hasException" |
| | | :disabled="operationType === 'view'"> |
| | | <el-radio-button :value="false">æ£å¸¸</el-radio-button> |
| | | <el-radio-button :value="true">åå¨å¼å¸¸</el-radio-button> |
| | | </el-radio-group> |
| | | </el-card> |
| | | |
| | | <el-card v-if="hasException === true" class="section-card"> |
| | | <el-card v-if="hasException === true" |
| | | class="section-card"> |
| | | <h3>å¼å¸¸æè¿°</h3> |
| | | <el-input |
| | | v-model="abnormalDescription" |
| | | <el-input v-model="abnormalDescription" |
| | | type="textarea" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :rows="4" |
| | | placeholder="请æè¿°å¼å¸¸æ
åµ..." |
| | | /> |
| | | :disabled="operationType === 'view'" |
| | | placeholder="请æè¿°å¼å¸¸æ
åµ..." /> |
| | | </el-card> |
| | | |
| | | <el-card v-if="hasException === true" class="section-card"> |
| | | <el-tabs v-model="currentUploadType"> |
| | | <el-tab-pane label="ç产å" name="before" /> |
| | | <el-tab-pane label="ç产ä¸" name="after" /> |
| | | <el-tab-pane label="ç产å" name="issue" /> |
| | | </el-tabs> |
| | | |
| | | <div class="upload-buttons"> |
| | | <el-upload |
| | | :show-file-list="false" |
| | | <el-card v-if="hasException === true" |
| | | class="section-card"> |
| | | <div class="upload-buttons" |
| | | v-if="operationType !== 'view'"> |
| | | <el-upload :show-file-list="false" |
| | | :http-request="uploadFile" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit || uploading" |
| | | accept="image/*" |
| | | > |
| | | <el-button type="primary" :loading="uploading"> |
| | | <el-icon><Camera /></el-icon> |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit || uploading" |
| | | accept="image/*"> |
| | | <el-button type="primary" |
| | | :loading="uploading"> |
| | | <el-icon> |
| | | <Camera /> |
| | | </el-icon> |
| | | éæ©å¾ç |
| | | </el-button> |
| | | </el-upload> |
| | | |
| | | <el-upload |
| | | :show-file-list="false" |
| | | <el-upload :show-file-list="false" |
| | | :http-request="uploadFile" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit || uploading" |
| | | accept="video/*" |
| | | > |
| | | <el-button type="success" :loading="uploading"> |
| | | <el-icon><VideoCamera /></el-icon> |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit || uploading" |
| | | accept="video/*"> |
| | | <el-button type="success" |
| | | :loading="uploading"> |
| | | <el-icon> |
| | | <VideoCamera /> |
| | | </el-icon> |
| | | éæ©è§é¢ |
| | | </el-button> |
| | | </el-upload> |
| | | </div> |
| | | |
| | | <el-progress |
| | | v-if="uploading" |
| | | <el-progress v-if="uploading" |
| | | :percentage="uploadProgress" |
| | | class="upload-progress" |
| | | /> |
| | | |
| | | <div v-if="getCurrentFiles().length" class="file-list"> |
| | | <div |
| | | v-for="(file, index) in getCurrentFiles()" |
| | | class="upload-progress" /> |
| | | <div v-if="beforeModelValue.length" |
| | | class="file-list"> |
| | | <div v-for="(file, index) in beforeModelValue" |
| | | :key="file.uid || file.id || index" |
| | | class="file-item" |
| | | > |
| | | class="file-item"> |
| | | <div class="file-preview-container"> |
| | | <el-image |
| | | v-if="file.type === 'image' || !file.type" |
| | | <el-image v-if="file.type === 'image' || !file.type" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | fit="cover" |
| | | class="file-preview" |
| | | :preview-src-list="[file.url || file.tempFilePath || file.path || file.downloadUrl]" |
| | | preview-teleported |
| | | /> |
| | | |
| | | <div v-else class="video-preview" @click="previewVideo(file)"> |
| | | <el-icon><VideoCamera /></el-icon> |
| | | preview-teleported /> |
| | | <div v-else |
| | | class="video-preview" |
| | | @click="previewVideo(file)"> |
| | | <el-icon> |
| | | <VideoCamera /> |
| | | </el-icon> |
| | | <span>è§é¢</span> |
| | | </div> |
| | | |
| | | <el-button |
| | | class="delete-btn" |
| | | <el-button class="delete-btn" |
| | | type="danger" |
| | | circle |
| | | size="small" |
| | | @click="removeFile(index)" |
| | | > |
| | | <el-icon><Close /></el-icon> |
| | | v-if="operationType !== 'view'" |
| | | @click="removeFile(index)"> |
| | | <el-icon> |
| | | <Close /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | |
| | | <div class="file-info"> |
| | | <div class="file-name"> |
| | | {{ file.bucketFilename || file.name || (file.type === "image" ? "å¾ç" : "è§é¢") }} |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-empty |
| | | v-else |
| | | :description="`è¯·éæ©è¦ä¸ä¼ ç${getUploadTypeText()}å¾çæè§é¢`" |
| | | /> |
| | | |
| | | <el-alert |
| | | class="upload-summary" |
| | | type="info" |
| | | :closable="false" |
| | | :title="`ç产åï¼${beforeModelValue.length}个æä»¶ | ç产ä¸ï¼${afterModelValue.length}个æä»¶ | ç产åï¼${issueModelValue.length}个æä»¶`" |
| | | /> |
| | | <el-empty v-else |
| | | :description="operationType === 'view' ? 'ææ å¼å¸¸ç
§çæè§é¢' : 'è¯·éæ©è¦ä¸ä¼ çå·¡æ£å¾çæè§é¢'" /> |
| | | </el-card> |
| | | |
| | | <el-result |
| | | v-if="hasException === false" |
| | | <el-result v-if="hasException === false" |
| | | icon="success" |
| | | title="设å¤è¿è¡æ£å¸¸" |
| | | sub-title="æ éä¸ä¼ ç
§ç" |
| | | /> |
| | | :sub-title="operationType === 'view' ? '' : 'æ éä¸ä¼ ç
§ç'" /> |
| | | </main> |
| | | |
| | | <template #footer> |
| | | <footer class="footer-buttons"> |
| | | <el-button type="primary" @click="submitUpload">æäº¤</el-button> |
| | | <el-button v-if="hasException === true" type="warning" @click="goToRepair"> |
| | | <el-button type="primary" |
| | | v-if="operationType !== 'view'" |
| | | @click="submitUpload">æäº¤</el-button> |
| | | <el-button v-if="hasException === true && operationType !== 'view'" |
| | | type="warning" |
| | | @click="goToRepair"> |
| | | æ°å¢æ¥ä¿® |
| | | </el-button> |
| | | <el-button @click="handleClose">åæ¶</el-button> |
| | | <el-button @click="handleClose">{{ operationType === 'view' ? 'å
³é' : 'åæ¶' }}</el-button> |
| | | </footer> |
| | | </template> |
| | | </FormDialog> |
| | | |
| | | <el-dialog |
| | | v-model="showVideoDialog" |
| | | <el-dialog v-model="showVideoDialog" |
| | | :title="currentVideoFile?.originalFilename || currentVideoFile?.name || 'è§é¢é¢è§'" |
| | | width="720px" |
| | | > |
| | | <video |
| | | v-if="currentVideoFile" |
| | | width="720px"> |
| | | <video v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | /> |
| | | autoplay /> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | |
| | | import { ElLoading, ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Camera, Close, VideoCamera } from "@element-plus/icons-vue"; |
| | | import axios from "axios"; |
| | | import dayjs from "dayjs"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { uploadInspectionTask } from "@/api/inspectionManagement/index.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | |
| | | const taskInfo = ref(null); |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | const operationType = ref("add"); // add, view |
| | | |
| | | const beforeModelValue = ref([]); |
| | | const afterModelValue = ref([]); |
| | | const issueModelValue = ref([]); |
| | | |
| | | const currentUploadType = ref("before"); |
| | | const formattedInspector = computed(() => { |
| | | if (!taskInfo.value?.inspector) return []; |
| | | if (Array.isArray(taskInfo.value.inspector)) return taskInfo.value.inspector; |
| | | if (typeof taskInfo.value.inspector === "string") { |
| | | return taskInfo.value.inspector |
| | | .split(",") |
| | | .map(s => s.trim()) |
| | | .filter(s => s); |
| | | } |
| | | return [taskInfo.value.inspector]; |
| | | }); |
| | | |
| | | const formatFrequencyType = type => { |
| | | const mapping = { |
| | | DAILY: "æ¯æ¥", |
| | | WEEKLY: "æ¯å¨", |
| | | MONTHLY: "æ¯æ", |
| | | QUARTERLY: "å£åº¦", |
| | | }; |
| | | return mapping[type] || type; |
| | | }; |
| | | |
| | | const formatFrequencyDetail = detail => { |
| | | if (typeof detail !== "string") return detail; |
| | | const replacements = { |
| | | MON: "å¨ä¸", |
| | | TUE: "å¨äº", |
| | | WED: "å¨ä¸", |
| | | THU: "å¨å", |
| | | FRI: "å¨äº", |
| | | SAT: "å¨å
", |
| | | SUN: "卿¥", |
| | | }; |
| | | return detail.replace( |
| | | /MON|TUE|WED|THU|FRI|SAT|SUN/g, |
| | | match => replacements[match] |
| | | ); |
| | | }; |
| | | |
| | | const formatDateTime = date => { |
| | | if (!date) return "-"; |
| | | return dayjs(date).format("YYYY-MM-DD"); |
| | | }; |
| | | |
| | | const hasException = ref(null); |
| | | const abnormalDescription = ref(""); |
| | | |
| | |
| | | |
| | | return { |
| | | ...item, |
| | | url: processFileUrl(item.url || item.previewURL || item.downloadUrl || item.path || ""), |
| | | url: processFileUrl( |
| | | item.url || item.previewURL || item.downloadUrl || item.path || "" |
| | | ), |
| | | downloadUrl: processFileUrl( |
| | | item.downloadUrl || item.url || item.previewURL || item.path || "" |
| | | ), |
| | |
| | | const resetState = () => { |
| | | taskInfo.value = null; |
| | | beforeModelValue.value = []; |
| | | afterModelValue.value = []; |
| | | issueModelValue.value = []; |
| | | currentUploadType.value = "before"; |
| | | hasException.value = null; |
| | | abnormalDescription.value = ""; |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | showVideoDialog.value = false; |
| | | currentVideoFile.value = null; |
| | | operationType.value = "add"; |
| | | }; |
| | | |
| | | const openDialog = row => { |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type || "add"; |
| | | const raw = JSON.parse(JSON.stringify(row?.__raw || row || {})); |
| | | taskInfo.value = raw; |
| | | |
| | |
| | | raw.commonFileListBeforeVO || raw.commonFileListBefore || [], |
| | | "image" |
| | | ); |
| | | afterModelValue.value = normalizeList( |
| | | raw.commonFileListVO || raw.commonFileList || [], |
| | | "image" |
| | | ); |
| | | issueModelValue.value = normalizeList( |
| | | raw.commonFileListAfterVO || raw.commonFileListAfter || [], |
| | | "image" |
| | | ); |
| | | |
| | | abnormalDescription.value = raw.abnormalDescription || ""; |
| | | |
| | | if (raw.hasException !== undefined && raw.hasException !== null) { |
| | | hasException.value = raw.hasException; |
| | | } else if (raw.inspectionResult !== undefined && raw.inspectionResult !== null) { |
| | | } else if ( |
| | | raw.inspectionResult !== undefined && |
| | | raw.inspectionResult !== null |
| | | ) { |
| | | hasException.value = String(raw.inspectionResult) === "0"; |
| | | } else { |
| | | hasException.value = null; |
| | | } |
| | | |
| | | if ( |
| | | hasException.value !== true && |
| | | (beforeModelValue.value.length || afterModelValue.value.length || issueModelValue.value.length) |
| | | ) { |
| | | if (hasException.value !== true && beforeModelValue.value.length) { |
| | | hasException.value = true; |
| | | } |
| | | |
| | |
| | | emit("closeDia"); |
| | | }; |
| | | |
| | | const getCurrentFiles = () => { |
| | | if (currentUploadType.value === "before") return beforeModelValue.value; |
| | | if (currentUploadType.value === "after") return afterModelValue.value; |
| | | if (currentUploadType.value === "issue") return issueModelValue.value; |
| | | return []; |
| | | }; |
| | | |
| | | const getUploadTypeText = () => { |
| | | if (currentUploadType.value === "before") return "ç产å"; |
| | | if (currentUploadType.value === "after") return "ç产ä¸"; |
| | | if (currentUploadType.value === "issue") return "ç产å"; |
| | | return ""; |
| | | }; |
| | | |
| | | const getTabType = () => { |
| | | if (currentUploadType.value === "before") return 10; |
| | | if (currentUploadType.value === "after") return 11; |
| | | if (currentUploadType.value === "issue") return 12; |
| | | return 10; |
| | | }; |
| | | |
| | | const previewVideo = file => { |
| | | currentVideoFile.value = file; |
| | | showVideoDialog.value = true; |
| | |
| | | const uploadFile = async uploadRequest => { |
| | | const rawFile = uploadRequest.file; |
| | | |
| | | if (getCurrentFiles().length >= uploadConfig.limit) { |
| | | if (beforeModelValue.value.length >= uploadConfig.limit) { |
| | | ElMessage.warning(`æå¤åªè½éæ©${uploadConfig.limit}个æä»¶`); |
| | | return; |
| | | } |
| | | |
| | | const ext = rawFile.name.split(".").pop()?.toLowerCase(); |
| | | if (!uploadConfig.fileType.includes(ext)) { |
| | | ElMessage.warning(`æä»¶æ ¼å¼ä¸æ¯æï¼è¯·ä¸ä¼ ${uploadConfig.fileType.join("/")} æ ¼å¼`); |
| | | ElMessage.warning( |
| | | `æä»¶æ ¼å¼ä¸æ¯æï¼è¯·ä¸ä¼ ${uploadConfig.fileType.join("/")} æ ¼å¼` |
| | | ); |
| | | return; |
| | | } |
| | | |
| | |
| | | |
| | | const formData = new FormData(); |
| | | formData.append("files", rawFile); |
| | | formData.append("type", getTabType()); |
| | | formData.append("type", 10); // ç产ååºå®ä¸º10 |
| | | |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | |
| | | const finalUrl = processFileUrl( |
| | | resultData.url || resultData.previewURL || resultData.downloadUrl || "" |
| | | ); |
| | | const finalName = resultData.name || resultData.originalFilename || resultData.bucketFilename; |
| | | const finalName = |
| | | resultData.name || |
| | | resultData.originalFilename || |
| | | resultData.bucketFilename; |
| | | const finalId = resultData.tempId || resultData.id || resultData.tempFileId; |
| | | |
| | | const uploadedFile = { |
| | |
| | | uid: `${Date.now()}-${Math.random()}`, |
| | | }; |
| | | |
| | | getCurrentFiles().push(uploadedFile); |
| | | beforeModelValue.value.push(uploadedFile); |
| | | ElMessage.success("ä¸ä¼ æå"); |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || "ä¸ä¼ 失败"); |
| | |
| | | } |
| | | |
| | | if (hasException.value === true) { |
| | | const totalFiles = |
| | | beforeModelValue.value.length + |
| | | afterModelValue.value.length + |
| | | issueModelValue.value.length; |
| | | const totalFiles = beforeModelValue.value.length; |
| | | |
| | | if (!totalFiles) { |
| | | ElMessage.warning("请ä¸ä¼ å¼å¸¸ç
§çæè§é¢"); |
| | |
| | | }); |
| | | |
| | | try { |
| | | const allFiles = [ |
| | | ...beforeModelValue.value, |
| | | ...afterModelValue.value, |
| | | ...issueModelValue.value, |
| | | ]; |
| | | const allFiles = [...beforeModelValue.value]; |
| | | |
| | | const tempFileIds = allFiles |
| | | .map(item => item?.tempId ?? item?.tempFileId ?? item?.id) |
| | |
| | | const submitData = { |
| | | ...baseTaskInfo, |
| | | commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: afterModelValue.value.map(buildFileItem), |
| | | commonFileListAfterDTO: issueModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: [], |
| | | commonFileListAfterDTO: [], |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, |
| | | abnormalDescription: abnormalDescription.value, |
| | |
| | | await ElMessageBox.confirm("ç¡®å®è¦å é¤è¿ä¸ªæä»¶åï¼", "确认å é¤", { |
| | | type: "warning", |
| | | }); |
| | | getCurrentFiles().splice(index, 1); |
| | | beforeModelValue.value.splice(index, 1); |
| | | } catch {} |
| | | }; |
| | | |
| | |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, |
| | | commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: afterModelValue.value.map(buildFileItem), |
| | | commonFileListAfterDTO: issueModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: [], |
| | | commonFileListAfterDTO: [], |
| | | uploadedFiles: { |
| | | before: beforeModelValue.value, |
| | | after: afterModelValue.value, |
| | | issue: issueModelValue.value, |
| | | after: [], |
| | | issue: [], |
| | | }, |
| | | }; |
| | | |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog title="æ¥çéä»¶" v-model="dialogVisitable" width="800px" @close="cancel"> |
| | | <el-dialog title="æ¥çéä»¶" |
| | | v-model="dialogVisitable" |
| | | width="800px" |
| | | @close="cancel"> |
| | | <div class="upload-container"> |
| | | <div class="form-container"> |
| | | <div class="title">ç产å</div> |
| | | |
| | | <div class="title">å¾çå表</div> |
| | | <div class="media-list"> |
| | | <img |
| | | v-for="(item, index) in beforeProductionImgs" |
| | | <img v-for="(item, index) in beforeProductionImgs" |
| | | :key="`before-img-${index}`" |
| | | :src="item" |
| | | alt="" |
| | | class="media-image" |
| | | @click="showMedia(beforeProductionImgs, index, 'image')" |
| | | /> |
| | | @click="showMedia(beforeProductionImgs, index, 'image')" /> |
| | | </div> |
| | | |
| | | <div class="media-list"> |
| | | <div |
| | | v-for="(videoUrl, index) in beforeProductionVideos" |
| | | <div v-for="(videoUrl, index) in beforeProductionVideos" |
| | | :key="`before-video-${index}`" |
| | | class="video-item" |
| | | @click="showMedia(beforeProductionVideos, index, 'video')" |
| | | > |
| | | @click="showMedia(beforeProductionVideos, index, 'video')"> |
| | | <div class="video-thumb"> |
| | | <img src="@/assets/images/video.png" alt="ææ¾" class="video-icon" /> |
| | | <img src="@/assets/images/video.png" |
| | | alt="ææ¾" |
| | | class="video-icon" /> |
| | | </div> |
| | | <div class="video-text">ç¹å»ææ¾</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="form-container"> |
| | | <!-- <div class="form-container"> |
| | | <div class="title">ç产ä¸</div> |
| | | |
| | | <div class="media-list"> |
| | |
| | | <div class="video-text">ç¹å»ææ¾</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <div v-if="isMediaViewerVisible" class="media-viewer-overlay" @click.self="closeMediaViewer"> |
| | | <div class="media-viewer-content" @click.stop> |
| | | <vue-easy-lightbox |
| | | v-if="mediaType === 'image'" |
| | | <div v-if="isMediaViewerVisible" |
| | | class="media-viewer-overlay" |
| | | @click.self="closeMediaViewer"> |
| | | <div class="media-viewer-content" |
| | | @click.stop> |
| | | <vue-easy-lightbox v-if="mediaType === 'image'" |
| | | :visible="isMediaViewerVisible" |
| | | :imgs="mediaList" |
| | | :index="currentMediaIndex" |
| | | @hide="closeMediaViewer" |
| | | /> |
| | | |
| | | <div v-else-if="mediaType === 'video'" class="video-player-wrap"> |
| | | <video :src="mediaList[currentMediaIndex]" autoplay controls class="video-player" /> |
| | | @hide="closeMediaViewer" /> |
| | | <div v-else-if="mediaType === 'video'" |
| | | class="video-player-wrap"> |
| | | <video :src="mediaList[currentMediaIndex]" |
| | | autoplay |
| | | controls |
| | | class="video-player" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | formatData: cell => { |
| | | if (!cell) return "-"; |
| | | try { |
| | | return dayjs(cell).format("YYYY-MM-DD HH:mm:ss"); |
| | | return dayjs(cell).format("YYYY-MM-DD"); |
| | | } catch { |
| | | return cell; |
| | | } |
| | |
| | | operation: operations |
| | | .map(op => { |
| | | switch (op) { |
| | | case "view": |
| | | return { |
| | | name: "详æ
", |
| | | clickFun: openViewDialog, |
| | | color: "#409EFF", |
| | | }; |
| | | case "edit": |
| | | return { |
| | | name: "ç¼è¾", |
| | |
| | | ]; |
| | | operationsArr.value = ["edit"]; |
| | | } else if (value === "task") { |
| | | const operationColumn = getOperationColumn(["upload", "viewFile"]); |
| | | const operationColumn = getOperationColumn(["view", "upload", "viewFile"]); |
| | | // å·¡æ£è®°å½ä¸å±ç¤º"æ¯å¦å¯ç¨"å |
| | | const taskColumns = columns.value.filter(col => col.prop !== "isEnabled"); |
| | | tableColumns.value = [ |
| | | ...taskColumns, |
| | | ...(operationColumn ? [operationColumn] : []), |
| | | ]; |
| | | operationsArr.value = ["upload", "viewFile"]; |
| | | operationsArr.value = ["view", "upload", "viewFile"]; |
| | | } |
| | | pageNum.value = 1; |
| | | pageSize.value = 10; |
| | |
| | | |
| | | const openUploadDialog = row => { |
| | | nextTick(() => { |
| | | uploadFiles.value?.openDialog(row); |
| | | uploadFiles.value?.openDialog("add", row); |
| | | }); |
| | | }; |
| | | |
| | | const openViewDialog = row => { |
| | | nextTick(() => { |
| | | uploadFiles.value?.openDialog("view", row); |
| | | }); |
| | | }; |
| | | |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¥ä¿®æ¥ä¿®é¡¹ç®"> |
| | | <el-form-item label="æ¥ä¿®é¡¹ç®"> |
| | | <el-input v-model="form.machineryCategory" |
| | | placeholder="请è¾å
¥æ¥ä¿®æ¥ä¿®é¡¹ç®" |
| | | placeholder="请è¾å
¥æ¥ä¿®é¡¹ç®" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | :tableLoading="loading" |
| | | :page="pagination" |
| | | :isShowPagination="true" |
| | | :row-class-name="rowClassName" |
| | | @pagination="handleSizeChange" |
| | | > |
| | | <template #status="{ row }"> |
| | |
| | | </el-dialog> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="å¤ä»¶é¢ç¨è®°å½" name="usage"> |
| | | <el-tab-pane label="å¤ä»¶ä½¿ç¨è®°å½" name="usage"> |
| | | <div class="search_form"> |
| | | <el-form :inline="true" :model="usageQuery" class="search-form"> |
| | | <el-form-item label="å¤ä»¶åç§°"> |
| | |
| | | onMounted(() => { |
| | | fetchListData(); |
| | | }); |
| | | |
| | | // æ ¹æ®æ°éè¿è¡å¤æèæ¯é¢è² |
| | | const rowClassName = ({ row }) => { |
| | | const num = Number(row.quantity); |
| | | if (num <= 1) return 'row-qty-low'; |
| | | if (num >= 8) return 'row-qty-high'; |
| | | return 'row-qty-mid'; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | <style> |
| | | .spare-part-category { |
| | | padding: 20px; |
| | | } |
| | |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .el-table__row:hover > td { |
| | | background-color: #fafafa; |
| | | .el-table .row-qty-low td.el-table__cell, |
| | | .el-table .row-qty-low:hover > td.el-table__cell { |
| | | background-color: #fde8e8 !important; |
| | | } |
| | | .el-table .row-qty-mid td.el-table__cell, |
| | | .el-table .row-qty-mid:hover > td.el-table__cell { |
| | | background-color: #f0f4f8 !important; |
| | | } |
| | | .el-table .row-qty-high td.el-table__cell, |
| | | .el-table .row-qty-high:hover > td.el-table__cell { |
| | | background-color: #e8f5e9 !important; |
| | | } |
| | | |
| | | /* æé®ç»æ ·å¼ */ |
| | |
| | | font-size: 12px; |
| | | margin-right: 4px; |
| | | } |
| | | |
| | | </style> |
| | |
| | | <el-form-item label="ä¿å
»é¡¹ç®"> |
| | | <el-input |
| | | v-model="form.machineryCategory" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥ä¿å
»é¡¹ç®" |
| | | /> |
| | | </el-form-item> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="dialogVisitable" |
| | | title="ä¿å
»è®°å½è¯¦æ
" |
| | | width="800px" |
| | | operation-type="detail" |
| | | @close="cancel" |
| | | > |
| | | <el-descriptions border :column="2"> |
| | | <el-descriptions-item label="设å¤åç§°"> |
| | | {{ detailData.deviceName || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="è§æ ¼åå·"> |
| | | {{ detailData.deviceModel || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="计åä¿å
»æ¥æ"> |
| | | {{ formatDate(detailData.maintenancePlanTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å½å
¥äºº"> |
| | | {{ detailData.createUserName || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¿å
»é¡¹ç®" :span="2"> |
| | | {{ detailData.machineryCategory || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å®é
ä¿å
»äºº"> |
| | | {{ detailData.maintenanceActuallyName || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å®é
ä¿å
»æ¥æ"> |
| | | {{ formatDate(detailData.maintenanceActuallyTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¿å
ȍȾ" :span="2"> |
| | | {{ detailData.maintenanceResult || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç¶æ" :span="2"> |
| | | <el-tag v-if="detailData.status === 2" type="danger">失败</el-tag> |
| | | <el-tag v-else-if="detailData.status === 1" type="success">å®ç»</el-tag> |
| | | <el-tag v-else type="warning">å¾
ä¿å
»</el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div v-if="fileList && fileList.length > 0" class="image-section"> |
| | | <div class="image-title">ä¿å
»éä»¶(å¾ç)ï¼</div> |
| | | <AttachmentUploadImage |
| | | v-model:fileList="fileList" |
| | | :disabled="true" |
| | | /> |
| | | </div> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ref } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { attachmentList } from "@/api/basicData/storageAttachment.js"; |
| | | import AttachmentUploadImage from '@/components/AttachmentUpload/image/index.vue'; |
| | | |
| | | const dialogVisitable = ref(false); |
| | | const detailData = ref({}); |
| | | const fileList = ref([]); |
| | | |
| | | const formatDate = (date) => { |
| | | return date ? dayjs(date).format("YYYY-MM-DD") : "--"; |
| | | }; |
| | | |
| | | const openDialog = (row) => { |
| | | dialogVisitable.value = true; |
| | | detailData.value = { ...row }; |
| | | fileList.value = []; |
| | | |
| | | if (row.id) { |
| | | attachmentList({ |
| | | recordType: 'device_maintenance', |
| | | recordId: row.id, |
| | | }).then(res => { |
| | | if (res && res.data) { |
| | | fileList.value = res.data || []; |
| | | } |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const cancel = () => { |
| | | dialogVisitable.value = false; |
| | | }; |
| | | |
| | | defineExpose({ openDialog }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .image-section { |
| | | margin-top: 20px; |
| | | } |
| | | .image-title { |
| | | font-weight: bold; |
| | | margin-bottom: 10px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="dialogVisitable" |
| | | title="ä¿å
»ä»»å¡è¯¦æ
" |
| | | width="800px" |
| | | operation-type="detail" |
| | | @close="cancel" |
| | | > |
| | | <el-descriptions border :column="2"> |
| | | <el-descriptions-item label="设å¤åç§°"> |
| | | {{ detailData.taskName || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="è§æ ¼åå·"> |
| | | {{ detailData.deviceModel || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å½å
¥äºº"> |
| | | {{ detailData.registrant || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç»è®°æ¶é´"> |
| | | {{ detailData.registrationDate || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¿å
»é¡¹ç®" :span="2"> |
| | | {{ detailData.machineryCategory || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¿å
»äºº"> |
| | | {{ detailData.maintenancePerson || '--' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä»»å¡é¢ç"> |
| | | {{ formatFrequencyType(detailData.frequencyType) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ§è¡æ¶é´"> |
| | | {{ formatFrequencyDetail(detailData.frequencyDetail) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="宿¶ä»»å¡"> |
| | | <el-tag :type="detailData.isActive === 1 ? 'success' : 'info'"> |
| | | {{ detailData.isActive === 1 ? 'å¼å¯' : 'å
³é' }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨" :span="2"> |
| | | {{ detailData.remarks || '--' }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div v-if="fileList && fileList.length > 0" class="image-section"> |
| | | <div class="image-title">设å¤å¾çï¼</div> |
| | | <AttachmentUploadImage |
| | | v-model:fileList="fileList" |
| | | :disabled="true" |
| | | /> |
| | | </div> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ref } from "vue"; |
| | | import { getLedgerById } from "@/api/equipmentManagement/ledger"; |
| | | import AttachmentUploadImage from '@/components/AttachmentUpload/image/index.vue'; |
| | | |
| | | const dialogVisitable = ref(false); |
| | | const detailData = ref({}); |
| | | const fileList = ref([]); |
| | | |
| | | const formatFrequencyType = (type) => { |
| | | const map = { |
| | | DAILY: "æ¯æ¥", |
| | | WEEKLY: "æ¯å¨", |
| | | MONTHLY: "æ¯æ", |
| | | QUARTERLY: "å£åº¦", |
| | | }; |
| | | return map[type] || "--"; |
| | | }; |
| | | |
| | | const formatFrequencyDetail = (detail) => { |
| | | if (!detail) return "--"; |
| | | const replacements = { |
| | | MON: "å¨ä¸", |
| | | TUE: "å¨äº", |
| | | WED: "å¨ä¸", |
| | | THU: "å¨å", |
| | | FRI: "å¨äº", |
| | | SAT: "å¨å
", |
| | | SUN: "卿¥", |
| | | }; |
| | | return detail.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]); |
| | | }; |
| | | |
| | | const openDialog = (row) => { |
| | | dialogVisitable.value = true; |
| | | detailData.value = { ...row }; |
| | | fileList.value = []; |
| | | |
| | | if (row.taskId) { |
| | | getLedgerById(row.taskId).then(res => { |
| | | if (res.code === 200 && res.data) { |
| | | fileList.value = res.data.storageBlobVOs || []; |
| | | } |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const cancel = () => { |
| | | dialogVisitable.value = false; |
| | | }; |
| | | |
| | | defineExpose({ openDialog }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .image-section { |
| | | margin-top: 20px; |
| | | } |
| | | .image-title { |
| | | font-weight: bold; |
| | | margin-bottom: 10px; |
| | | color: #606266; |
| | | } |
| | | </style> |
| | |
| | | <el-form-item label="ä¿å
»é¡¹ç®" prop="machineryCategory"> |
| | | <el-input |
| | | v-model.trim="form.machineryCategory" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥ä¿å
»é¡¹ç®" |
| | | maxlength="100" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="宿¶ä»»å¡"> |
| | | <el-switch |
| | | v-model="form.isActive" |
| | | :active-value="1" |
| | | :inactive-value="0" |
| | | active-text="å¼å¯" |
| | | inactive-text="å
³é" |
| | | inline-prompt |
| | | /> |
| | | <span style="margin-left: 12px; color: #909399; font-size: 12px;"> |
| | | {{ form.isActive === 1 ? 'ä»»å¡å°æè®¡åæ§è¡' : '任塿忧è¡' }} |
| | | </span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="夿³¨" prop="remarks"> |
| | | <el-input v-model="form.remarks" placeholder="请è¾å
¥å¤æ³¨" type="textarea" /> |
| | |
| | | time: '', |
| | | deviceModel: undefined, // è§æ ¼åå· |
| | | registrationDate: '', |
| | | maintenancePerson: '' // ä¿å
»äºº |
| | | maintenancePerson: '', // ä¿å
»äºº |
| | | isActive: 0 // 宿¶ä»»å¡å¼å
³ï¼0=å
³éï¼1=å¼å¯ |
| | | }, |
| | | rules: { |
| | | taskId: [{ required: true, message: "è¯·éæ©è®¾å¤", trigger: "change" },], |
| | |
| | | time: '', |
| | | deviceModel: undefined, |
| | | registrationDate: '', |
| | | maintenancePerson: '' |
| | | maintenancePerson: '', |
| | | isActive: 0 |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | delete payload.inspector |
| | | delete payload.inspectorIds |
| | | delete payload.active |
| | | |
| | | if (payload.frequencyType === 'WEEKLY') { |
| | | let frequencyDetail = '' |
| | |
| | | if (payload.status === undefined || payload.status === null || payload.status === '') { |
| | | payload.status = '0' // é»è®¤ç¶æï¼å¯æå®é
æä¸¾è°æ´ |
| | | } |
| | | payload.active = true |
| | | payload.deleted = 0 |
| | | |
| | | if (operationType.value === 'edit') { |
| | |
| | | }" |
| | | @selection-change="handleScheduledSelectionChange" |
| | | @pagination="changeScheduledPage"> |
| | | <template #isActiveRef="{ row }"> |
| | | <el-tag v-if="row.isActive === 1" |
| | | type="success">å¼å¯</el-tag> |
| | | <el-tag v-else |
| | | type="info">å
³é</el-tag> |
| | | </template> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 1" |
| | | type="success">å¯ç¨</el-tag> |
| | |
| | | type="danger">åç¨</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="handleDetail(row)"> |
| | | 详æ
|
| | | </el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="editScheduledTask(row)"> |
| | |
| | | type="warning">å¾
ä¿å
»</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="handleRecordDetail(row)"> |
| | | 详æ
|
| | | </el-button> |
| | | <!-- è¿ä¸ªåè½è·æ°å¢ä¿å
»åè½ä¸æ¨¡ä¸æ ·ï¼æå¥æä¹ï¼ --> |
| | | <!-- <el-button |
| | | type="primary" |
| | |
| | | @ok="getTableData" /> |
| | | <FormDia ref="formDiaRef" |
| | | @closeDia="getScheduledTableData" /> |
| | | <DetailDia ref="detailDiaRef" /> |
| | | <RecordDetailDia ref="recordDetailDiaRef" /> |
| | | <FileList v-if="fileDialogVisible" |
| | | v-model:visible="fileDialogVisible" |
| | | :record-type="'device_maintenance'" |
| | |
| | | import PlanModal from "./Form/PlanModal.vue"; |
| | | import MaintenanceModal from "./Form/MaintenanceModal.vue"; |
| | | import FormDia from "./Form/formDia.vue"; |
| | | import DetailDia from "./Form/detailDia.vue"; |
| | | import RecordDetailDia from "./Form/RecordDetailDia.vue"; |
| | | import { |
| | | getUpkeepPage, |
| | | delUpkeep, |
| | |
| | | const maintainModalRef = ref(); |
| | | // 宿¶ä»»å¡å¼¹çªæ§å¶å¨ |
| | | const formDiaRef = ref(); |
| | | // 宿¶ä»»å¡è¯¦æ
å¼¹çªæ§å¶å¨ |
| | | const detailDiaRef = ref(); |
| | | // ä¿å
»è®°å½è¯¦æ
å¼¹çªæ§å¶å¨ |
| | | const recordDetailDiaRef = ref(); |
| | | // éä»¶å¼¹çª |
| | | const fileListDialogRef = ref(null); |
| | | const fileDialogVisible = ref(false); |
| | |
| | | { |
| | | prop: "frequencyType", |
| | | label: "颿¬¡", |
| | | minWidth: 150, |
| | | minWidth: 80, |
| | | // PIMTable 使ç¨çæ¯ formatDataï¼è䏿¯ Element-Plus ç formatter |
| | | formatData: cell => |
| | | ({ |
| | |
| | | { |
| | | prop: "frequencyDetail", |
| | | label: "å¼å§æ¥æä¸æ¶é´", |
| | | minWidth: 150, |
| | | minWidth: 130, |
| | | // åæ ·æ¹ç¨ formatDataï¼PIMTable å
é¨ä¼æåå
æ ¼å¼ä¼ è¿æ¥ |
| | | formatData: cell => { |
| | | if (typeof cell !== "string") return ""; |
| | |
| | | ); |
| | | }, |
| | | }, |
| | | { |
| | | prop: "isActive", |
| | | label: "宿¶ä»»å¡", |
| | | dataType: "slot", |
| | | slot: "isActiveRef", |
| | | align: "center", |
| | | minWidth: 100, |
| | | }, |
| | | { prop: "maintenancePerson", label: "ä¿å
»äºº", minWidth: 100 }, |
| | | { prop: "registrant", label: "ç»è®°äºº", minWidth: 100 }, |
| | | { |
| | |
| | | label: "ç»è®°æ¥æ", |
| | | minWidth: 100, |
| | | formatData: cell => |
| | | cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | cell ? dayjs(cell).format("YYYY-MM-DD") : "-", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "200px", |
| | | width: "160px", |
| | | }, |
| | | ]); |
| | | |
| | |
| | | align: "center", |
| | | prop: "maintenanceActuallyTime", |
| | | formatData: cell => |
| | | cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | cell ? dayjs(cell).format("YYYY-MM-DD") : "-", |
| | | }, |
| | | { |
| | | label: "ä¿å
ȍȾ", |
| | |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "350px", |
| | | width: "250px", |
| | | }, |
| | | ]); |
| | | |
| | |
| | | if (row) { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog("edit", row); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const handleDetail = row => { |
| | | if (row) { |
| | | nextTick(() => { |
| | | detailDiaRef.value?.openDialog(row); |
| | | }); |
| | | } |
| | | }; |
| | |
| | | maintainModalRef.value.open(row.id, row); |
| | | }; |
| | | |
| | | const handleRecordDetail = row => { |
| | | if (row) { |
| | | nextTick(() => { |
| | | recordDetailDiaRef.value?.openDialog(row); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const addPlan = () => { |
| | | planModalRef.value.openModal(); |
| | | }; |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç¼å·" prop="assetCode"> |
| | | <el-input v-model="form.assetCode" placeholder="ä¿ååèªå¨çæ" disabled /> |
| | | <el-input v-model="form.assetCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´" prop="createTime"> |
| | | <el-date-picker v-model="createTimeDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | |
| | | keeper: "", |
| | | status: "in_use", |
| | | remark: "", |
| | | createTime: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | const createTimeDate = computed({ |
| | | get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | form.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | |
| | | const rules = { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `GD${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "æ°å¢åºå®èµäº§"; |
| | | Object.assign(form, createDefaultForm(), { |
| | | assetCode: buildAssetCode(), |
| | | purchaseDate: new Date().toISOString().split('T')[0], |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button v-if="row.status !== 'amortized'" type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èµäº§ç¼å·" prop="assetCode"> |
| | | <el-input v-model="form.assetCode" placeholder="ä¿ååèªå¨çæ" disabled /> |
| | | <el-input v-model="form.assetCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | <el-select v-model="form.status" placeholder="è¯·éæ©ç¶æ" style="width: 100%;"> |
| | | <el-option label="å¨ç¨" value="in_use" /> |
| | | <el-option label="é²ç½®" value="idle" /> |
| | | <el-option v-if="isView" label="å·²æé宿¯" value="amortized" /> |
| | | <el-option label="å·²æé宿¯" value="amortized" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´" prop="createTime"> |
| | | <el-date-picker v-model="createTimeDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | |
| | | ); |
| | | |
| | | const createDefaultForm = () => ({ |
| | | id: null, |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | |
| | | status: "in_use", |
| | | description: "", |
| | | remark: "", |
| | | createTime: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | const createTimeDate = computed({ |
| | | get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | form.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | |
| | | const rules = { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const buildAssetCode = () => `WX${Date.now().toString().slice(-10)}`; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "æ°å¢æ å½¢èµäº§"; |
| | | Object.assign(form, createDefaultForm(), { |
| | | assetCode: buildAssetCode(), |
| | | acquisitionDate: new Date().toISOString().split('T')[0], |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | invoiceType: [{ required: true, message: "è¯·éæ©å票类å", trigger: "change" }], |
| | | taxRate: [{ required: true, message: "è¯·éæ©ç¨ç", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const summaryProps = ["amount", "taxAmount", "totalAmount"]; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (summaryProps.includes(col.property)) { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur[col.property]); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = Number(total.toFixed(2)).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | const totalPaymentAmount = computed(() => |
| | | dataList.value.reduce((sum, item) => sum + Number(item.amount ?? 0), 0) |
| | | ); |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "amount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.amount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const formatMoney = value => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·åå·" prop="invoiceApplicationNo"> |
| | | <el-input v-model="form.invoiceApplicationNo" placeholder="ä¿ååèªå¨çæ" disabled /> |
| | | <el-input v-model="form.invoiceApplicationNo" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´" prop="createTime"> |
| | | <el-date-picker |
| | | v-model="formCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾éé¢" prop="paymentAmount"> |
| | | <el-input-number |
| | | v-model="form.paymentAmount" |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾åå·" prop="paymentNumber"> |
| | | <el-input v-model="paymentForm.paymentNumber" placeholder="ä¿ååèªå¨çæ" disabled /> |
| | | <el-input v-model="paymentForm.paymentNumber" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´" prop="createTime"> |
| | | <el-date-picker |
| | | v-model="paymentFormCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%;" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾éé¢" prop="paymentAmount"> |
| | | <el-input-number |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted, nextTick, getCurrentInstance } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | |
| | | bankAccount: "", |
| | | bankName: "", |
| | | remark: "", |
| | | createTime: "", |
| | | }); |
| | | |
| | | const paymentRules = { |
| | |
| | | stockInRecordIds: [], |
| | | inboundBatches: "", |
| | | status: 0, |
| | | createTime: "", |
| | | }); |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | form.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | const paymentFormCreateTimeDate = computed({ |
| | | get: () => (paymentForm.createTime ? String(paymentForm.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | paymentForm.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | |
| | | const rules = { |
| | |
| | | paymentAmount: [{ required: true, message: "请è¾å
¥ä»æ¬¾éé¢", trigger: "blur" }], |
| | | paymentMethod: [{ required: true, message: "è¯·éæ©ä»æ¬¾æ¹å¼", trigger: "change" }], |
| | | applyDate: [{ required: true, message: "è¯·éæ©ç³è¯·æ¥æ", trigger: "change" }], |
| | | }; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "amount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.amount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | |
| | | stockInRecordIds, |
| | | inboundBatches: formatInboundBatches(row.inboundBatches), |
| | | status: normalizeStatus(row.status), |
| | | createTime: row.createTime ?? "", |
| | | }); |
| | | }; |
| | | |
| | |
| | | remark: row.remark ?? "", |
| | | status: statusOverride !== undefined ? statusOverride : normalizeStatus(row.status), |
| | | paymentAmount: Number(row.paymentAmount ?? row.amount ?? 0), |
| | | createTime: row.createTime, |
| | | }); |
| | | |
| | | const buildSubmitPayload = (forUpdate = false) => { |
| | |
| | | remark: form.remark || "", |
| | | status: 0, |
| | | paymentAmount: form.paymentAmount, |
| | | createTime: form.createTime, |
| | | }; |
| | | if (forUpdate) { |
| | | payload.id = currentId.value; |
| | |
| | | stockInRecordIds: [], |
| | | inboundBatches: "", |
| | | status: 0, |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | inboundBatchList.value = []; |
| | | inboundBatchOptions.value = []; |
| | |
| | | bankAccount: row.bankAccountNum ?? row.bankAccount ?? "", |
| | | bankName: row.bankAccountName ?? row.bankName ?? "", |
| | | remark: "", |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | paymentDialogVisible.value = true; |
| | | nextTick(() => { |
| | |
| | | paymentAmount: paymentForm.paymentAmount, |
| | | paymentNumber: paymentForm.paymentNumber || "", |
| | | remark: paymentForm.remark || "", |
| | | createTime: paymentForm.createTime, |
| | | }) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | slot: "inboundDate", |
| | | }, |
| | | { label: "产ååç§°", prop: "productName", minWidth: "140" }, |
| | | { label: "è§æ ¼åå·", prop: "specificationModel", minWidth: "140" }, |
| | | { label: "产åè§æ ¼", prop: "specificationModel", minWidth: "140" }, |
| | | { |
| | | label: "éé¢", |
| | | prop: "inboundAmount", |
| | |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "inboundAmount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.inboundAmount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | { label: "éè´§åå·", prop: "returnNo", minWidth: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", minWidth: "180" }, |
| | | { label: "å
³èå
¥åºåå·", prop: "inboundBatches", minWidth: "150" }, |
| | | { |
| | | label: "åè´§ç±»å", |
| | | prop: "shippingType", |
| | | minWidth: "110", |
| | | formatData: (val) => ({ 1: "货车", 2: "å¿«é" }[String(val)] || "--"), |
| | | }, |
| | | { |
| | | label: "å货车çå·", |
| | | prop: "truckPlateNo", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "1" ? row?.truckPlateNo || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "å¿«éå
¬å¸", |
| | | prop: "expressCompany", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressCompany || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "å¿«éåå·", |
| | | prop: "expressNo", |
| | | minWidth: "150", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressNo || "--" : "--"), |
| | | }, |
| | | { label: "éè´§æ¥æ", prop: "preparedAt", minWidth: "170" }, |
| | | { |
| | | label: "鿬¾æ»é¢", |
| | |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "totalAmount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.totalAmount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | closingBalance: generateForm.closingBalance, |
| | | accountStatementDetails: selectedPurchases.value.map(buildDetailSubmitItem), |
| | | }); |
| | | |
| | | const summaryProps = ["openingBalance", "currentPlan", "currentActually", "closingBalance"]; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (summaryProps.includes(col.property)) { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur[col.property]); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = Number(total.toFixed(2)).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | |
| | | v-loading="tableLoading" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="ç³è¯·åå·" prop="applyCode"> |
| | | <el-input v-model="form.applyCode" placeholder="ä¿ååèªå¨çæ" disabled /> |
| | | <el-input v-model="form.applyCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´" prop="createTime"> |
| | | <el-date-picker |
| | | v-model="formCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å票å
容" prop="content"> |
| | | <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请è¾å
¥å票å
容" :disabled="isView" /> |
| | | </el-form-item> |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted, nextTick, getCurrentInstance, defineAsyncComponent } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { listCustomer } from "@/api/basicData/customer.js"; |
| | |
| | | applyDate: "", |
| | | content: "", |
| | | remark: "", |
| | | createTime: "", |
| | | }); |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | form.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | |
| | | const rules = { |
| | |
| | | proxy.download("/accountInvoiceApplication/exportAccountInvoiceApplication", params, filename); |
| | | }; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "amount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.amount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | |
| | | status: normalizeStatus(row.status ?? row.auditStatus), |
| | | outboundBatchNos, |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | createTime: row.createTime ?? "", |
| | | }); |
| | | }; |
| | | |
| | |
| | | isView.value = false; |
| | | dialogTitle.value = "æ°å¢å¼ç¥¨ç³è¯·"; |
| | | Object.assign(form, { |
| | | applyCode: "", |
| | | applyCode: "KP" + Date.now().toString().slice(-8), |
| | | customerId: "", |
| | | outboundBatchNos: [], |
| | | outboundBatches: "", |
| | |
| | | applyDate: new Date().toISOString().split("T")[0], |
| | | content: "", |
| | | remark: "", |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | |
| | | invoiceAmount: form.amount, |
| | | taxRate: form.taxRate, |
| | | status: 0, |
| | | createTime: form.createTime, |
| | | }; |
| | | if (forUpdate) { |
| | | payload.id = currentId.value; |
| | |
| | | dialogTitle.value = "ç¼è¾å¼ç¥¨ç³è¯·"; |
| | | fillFormFromRow(row); |
| | | dialogVisible.value = true; |
| | | loadOutboundBatches(form.customerId, true); |
| | | }; |
| | | |
| | | const view = (row) => { |
| | |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">å½å
¥å票</el-button> |
| | | <!-- <el-button type="primary" @click="add" icon="Plus">å½å
¥å票</el-button> --> |
| | | <el-button type="success" @click="handleExport" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | |
| | | v-loading="tableLoading" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | @confirm="submitForm" |
| | | @cancel="closeDialog" |
| | | > |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row v-if="isView" :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æ"> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select @change="checkCustomer" v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isView"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isView"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
³èå¼ç¥¨ç³è¯·" prop="accountInvoiceApplicationId"> |
| | | <el-select v-model="form.accountInvoiceApplicationId" |
| | | placeholder="请å
鿩客æ·" |
| | | readonly |
| | | @change="selectionApply" |
| | | :disabled="!form.customerId || isView" |
| | | class="outbound-batch-select"> |
| | | <el-option v-for="(item,index) in applyList" :key="index" :label="item.invoiceApplicationNo" :value="item.id"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¼ç¥¨æ¥æ" prop="invoiceDate"> |
| | | <el-date-picker |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票类å" prop="invoiceType"> |
| | | <el-select |
| | |
| | | placeholder="è¯·éæ©å票类å" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | @change="handleInvoiceTypeChange" |
| | | > |
| | | <el-option label="å¢å¼ç¨ä¸ç¨å票" value="å¢å¼ç¨ä¸ç¨å票" /> |
| | | <el-option label="å¢å¼ç¨æ®éå票" value="å¢å¼ç¨æ®éå票" /> |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç" prop="taxRate"> |
| | | <el-select |
| | |
| | | placeholder="è¯·éæ©ç¨ç" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | @change="handleTaxRateChange" |
| | | @change="calculateTax" |
| | | > |
| | | <el-option |
| | | v-for="dict in tax_rate" |
| | |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="10"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="éé¢(ä¸å«ç¨)" prop="amount"> |
| | | <el-input-number |
| | | v-model="form.amount" |
| | |
| | | :precision="2" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | @change="handleTaxRateChange" |
| | | @change="calculateTax" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="7"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ç¨é¢"> |
| | | <el-input v-model="form.taxAmount" style="width: 100%;" disabled /> |
| | | <el-input v-model="form.taxAmount" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="7"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ä»·ç¨å计"> |
| | | <el-input v-model="form.totalAmount" style="width: 100%;" disabled /> |
| | | <el-input v-model="form.totalAmount" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | cancelAccountSalesInvoice, |
| | | deleteAccountSalesInvoice, |
| | | } from "@/api/financialManagement/accountSalesInvoice.js"; |
| | | import {getAccountInvoiceApplicationList} from "@/api/financialManagement/invoiceApply.js"; |
| | | |
| | | const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); |
| | | |
| | |
| | | const fileDialogVisible = ref(false); |
| | | const currentRecordId = ref(0); |
| | | |
| | | const applySelectVisible = ref(false); |
| | | const applyList = ref([]); |
| | | |
| | | const openFileDialog = (row) => { |
| | | if (!row.accountInvoiceApplicationId) { |
| | | ElMessage.warning("æªå
³èå¼ç¥¨ç³è¯·ï¼æ æ³æ¥çéä»¶"); |
| | |
| | | totalAmount: 0, |
| | | content: "", |
| | | remark: "", |
| | | deptId: undefined, |
| | | accountInvoiceApplicationId: undefined, |
| | | storageAttachmentId: undefined, |
| | | }); |
| | | |
| | | const rules = { |
| | | invoiceNo: [{ required: true, message: "请è¾å
¥å票å·ç ", trigger: "blur" }], |
| | | accountInvoiceApplicationId: [{ required: true, message: "è¯·éæ©å¼ç¥¨ç³è¯·", trigger: "change" }], |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | invoiceDate: [{ required: true, message: "è¯·éæ©å¼ç¥¨æ¥æ", trigger: "change" }], |
| | | invoiceType: [{ required: true, message: "è¯·éæ©å票类å", trigger: "change" }], |
| | |
| | | amount: [{ required: true, message: "请è¾å
¥éé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | // éæ©å®¢æ· |
| | | const checkCustomer = (val) =>{ |
| | | loadApplyList(val); |
| | | } |
| | | |
| | | //æ¥è¯¢å¼ç¥¨ç³è¯·å表 |
| | | const loadApplyList = (customerId) => { |
| | | if (!customerId) { |
| | | applyList.value = []; |
| | | return Promise.resolve(); |
| | | } |
| | | return getAccountInvoiceApplicationList({ customerId }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | const list = res.data?.records ?? res.data ?? []; |
| | | applyList.value = Array.isArray(list) ? list : []; |
| | | } else { |
| | | applyList.value = []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | applyList.value = []; |
| | | }) |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const selectionApply = (val)=>{ |
| | | const applyObj = applyList.value.find(item=>item.id === val); |
| | | if(!applyObj){ |
| | | ElMessage.warning("è¯·éæ©å
³èçå¼ç¥¨ç³è¯·"); |
| | | return; |
| | | } |
| | | form.remark = applyObj.remark; |
| | | form.content = applyObj.invoiceContent; |
| | | form.invoiceDate = applyObj.applyDate; |
| | | form.invoiceType = applyObj.invoiceType; |
| | | form.deptId = applyObj.deptId; |
| | | form.taxRate = applyObj.taxRate; |
| | | form.totalAmount = applyObj.invoiceAmount |
| | | handleTaxRateChange(); |
| | | } |
| | | |
| | | /** ä»·ç¨åè®¡åæ´ï¼æç¨çåç®ä¸å«ç¨éé¢ãç¨é¢ */ |
| | | const calculateTaxFromInclusive = (inclusiveTotal) => { |
| | | const total = Number(inclusiveTotal ?? form.totalAmount ?? 0); |
| | | if (total <= 0) { |
| | | form.amount = 0; |
| | | form.taxAmount = 0; |
| | | form.totalAmount = 0; |
| | | return; |
| | | } |
| | | const rate = Number(form.taxRate) / 100; |
| | | form.totalAmount = Number(total.toFixed(2)); |
| | | form.amount = Number((form.totalAmount / (1 + rate)).toFixed(2)); |
| | | form.taxAmount = Number((form.totalAmount - form.amount).toFixed(2)); |
| | | }; |
| | | |
| | | const calculateTaxFromExclusive = () => { |
| | | const calculateTax = () => { |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2)); |
| | | }; |
| | | |
| | | const handleTaxRateChange = () => { |
| | | if (form.totalAmount > 0) { |
| | | calculateTaxFromInclusive(form.totalAmount); |
| | | } else { |
| | | calculateTaxFromExclusive(); |
| | | } |
| | | }; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "amount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.amount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | const handleInvoiceTypeChange = () => { |
| | | calculateTax(); |
| | | }; |
| | | |
| | | const normalizeTableRow = (row) => ({ |
| | |
| | | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | applySelectVisible.value = false; |
| | | isView.value = false; |
| | | isView.value = false; |
| | | }; |
| | | |
| | |
| | | <el-form-item label="æ¶æ¬¾åå·" |
| | | prop="receiptCode"> |
| | | <el-input v-model="form.receiptCode" |
| | | placeholder="ä¿ååèªå¨çæ" |
| | | placeholder="ç³»ç»èªå¨çæ" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | prop="amount"> |
| | | <el-input-number v-model="form.amount" |
| | | :min="0" |
| | | :max="collectionAmountInputMax" |
| | | :precision="2" |
| | | style="width: 100%;" |
| | | :disabled="isView" /> |
| | | :disabled="isView" |
| | | placeholder="æ ¹æ®å
³èåæ®èªå¨æ±æ»ï¼å¯ä¿®æ¹" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´" |
| | | prop="createTime"> |
| | | <el-date-picker v-model="formCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%;" |
| | | :disabled="isView" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | align="right"> |
| | | <template #default="{ row }">Â¥{{ formatMoney(row.outboundAmount) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="amountReceived" |
| | | label="å·²æ¶æ¬¾éé¢" |
| | | width="110" |
| | | align="right"> |
| | | <template #default="{ row }">Â¥{{ formatMoney(row.amountReceived) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="taxRate" |
| | | label="ç¨ç" |
| | | width="80" |
| | |
| | | nextTick, |
| | | getCurrentInstance, |
| | | } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { listCustomer } from "@/api/basicData/customer.js"; |
| | |
| | | const isEdit = ref(false); |
| | | const isView = ref(false); |
| | | const currentId = ref(null); |
| | | const originalReceiptAmount = ref(0); |
| | | const submitLoading = ref(false); |
| | | |
| | | const customerList = ref([]); |
| | |
| | | stockOutRecordIds: [], |
| | | outboundBatches: "", |
| | | remark: "", |
| | | createTime: "", |
| | | }); |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | form.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | |
| | | const maxCollectionAmount = computed(() => { |
| | | const selected = form.stockOutRecordIds || []; |
| | | const editAmount = isEdit.value ? Number(originalReceiptAmount.value) || 0 : 0; |
| | | if (!selected.length) return isEdit.value ? editAmount : undefined; |
| | | const selectedValueSet = new Set(selected.map(id => String(id))); |
| | | const selectedOptions = outboundBatchOptions.value.filter( |
| | | opt => opt.amountLimitAvailable && selectedValueSet.has(String(opt.value)) |
| | | ); |
| | | if (selectedOptions.length !== selectedValueSet.size) { |
| | | return isEdit.value ? Number(editAmount.toFixed(2)) : undefined; |
| | | } |
| | | const sum = selectedOptions.reduce( |
| | | (acc, opt) => acc + (Number(opt.outboundAmount) || 0), |
| | | 0 |
| | | ); |
| | | return Number((sum + editAmount).toFixed(2)); |
| | | }); |
| | | |
| | | const collectionAmountInputMax = computed( |
| | | () => maxCollectionAmount.value ?? Number.MAX_SAFE_INTEGER |
| | | ); |
| | | |
| | | const validateCollectionAmount = (rule, value, callback) => { |
| | | if (value === undefined || value === null || value === "") { |
| | | callback(); |
| | | return; |
| | | } |
| | | const amount = Number(value); |
| | | if (Number.isNaN(amount)) { |
| | | callback(new Error("请è¾å
¥æ¶æ¬¾éé¢")); |
| | | return; |
| | | } |
| | | const max = maxCollectionAmount.value; |
| | | if (max !== undefined && amount - max > 0.000001) { |
| | | callback(new Error(`æ¶æ¬¾éé¢ä¸è½è¶
è¿${max.toFixed(2)}`)); |
| | | return; |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | const rules = { |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | |
| | | receiptDate: [ |
| | | { required: true, message: "è¯·éæ©æ¶æ¬¾æ¥æ", trigger: "change" }, |
| | | ], |
| | | amount: [ |
| | | { required: true, message: "请è¾å
¥æ¶æ¬¾éé¢", trigger: "blur" }, |
| | | { validator: validateCollectionAmount, trigger: ["blur", "change"] }, |
| | | ], |
| | | amount: [{ required: true, message: "请è¾å
¥æ¶æ¬¾éé¢", trigger: "blur" }], |
| | | receiptMethod: [ |
| | | { required: true, message: "è¯·éæ©æ¶æ¬¾æ¹å¼", trigger: "change" }, |
| | | ], |
| | |
| | | return list.map((item, index) => { |
| | | if (typeof item === "string" || typeof item === "number") { |
| | | const text = String(item); |
| | | return { |
| | | label: text, |
| | | value: text, |
| | | outboundAmount: 0, |
| | | amountLimitAvailable: false, |
| | | }; |
| | | return { label: text, value: text, outboundAmount: 0 }; |
| | | } |
| | | const label = |
| | | item.outboundBatches ?? |
| | |
| | | item.label ?? |
| | | `åºåºå${index + 1}`; |
| | | const value = item.id ?? item.stockOutRecordId ?? label; |
| | | const outboundAmount = Number(item.outboundAmount) || 0; |
| | | const amountReceived = Number(item.amountReceived) || 0; |
| | | const availableAmount = outboundAmount - amountReceived; |
| | | return { |
| | | label: String(label), |
| | | value, |
| | | outboundAmount: |
| | | availableAmount > 0 ? Number(availableAmount.toFixed(2)) : 0, |
| | | amountLimitAvailable: true, |
| | | outboundAmount: Number(item.outboundAmount) || 0, |
| | | }; |
| | | }); |
| | | }; |
| | |
| | | label: String(id), |
| | | value: id, |
| | | outboundAmount: 0, |
| | | amountLimitAvailable: false, |
| | | }); |
| | | }); |
| | | }; |
| | |
| | | outboundSelectVisible.value = false; |
| | | syncCollectionAmount(); |
| | | formRef.value?.validateField("stockOutRecordIds"); |
| | | formRef.value?.validateField("amount"); |
| | | }; |
| | | |
| | | const handleOutboundDialogClosed = () => { |
| | |
| | | collectionNumber: form.receiptCode || "", |
| | | remark: form.remark || "", |
| | | stockOutRecordIds: (form.stockOutRecordIds || []).join(","), |
| | | createTime: form.createTime, |
| | | }; |
| | | if (forUpdate) { |
| | | payload.id = currentId.value; |
| | |
| | | stockOutRecordIds, |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | remark: row.remark ?? "", |
| | | createTime: row.createTime ?? "", |
| | | }); |
| | | }; |
| | | |
| | |
| | | outboundSelectVisible.value = false; |
| | | isView.value = false; |
| | | isEdit.value = false; |
| | | originalReceiptAmount.value = 0; |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | dialogTitle.value = "æ°å¢æ¶æ¬¾"; |
| | | originalReceiptAmount.value = 0; |
| | | Object.assign(form, { |
| | | receiptCode: "", |
| | | receiptCode: "SK" + Date.now().toString().slice(-8), |
| | | customerId: "", |
| | | receiptDate: new Date().toISOString().split("T")[0], |
| | | amount: 0, |
| | |
| | | stockOutRecordIds: [], |
| | | outboundBatches: "", |
| | | remark: "", |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ¶æ¬¾"; |
| | | fillFormFromRow(row); |
| | | originalReceiptAmount.value = Number(form.amount || 0); |
| | | dialogVisible.value = true; |
| | | loadOutboundBatches(form.customerId, true); |
| | | }; |
| | | |
| | | const view = row => { |
| | | isView.value = true; |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ¥çæ¶æ¬¾"; |
| | | originalReceiptAmount.value = 0; |
| | | fillFormFromRow(row); |
| | | dialogVisible.value = true; |
| | | }; |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | closingBalance: generateForm.closingBalance, |
| | | accountStatementDetails: selectedSales.value.map(buildDetailSubmitItem), |
| | | }); |
| | | |
| | | const summaryProps = ["openingBalance", "currentPlan", "currentActually", "closingBalance"]; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (summaryProps.includes(col.property)) { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur[col.property]); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = Number(total.toFixed(2)).toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | |
| | | <template> |
| | | <!-- éå®åºåº --> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="åºåºåå·:"> |
| | | <el-input v-model="filters.outboundBatches" |
| | | placeholder="请è¾å
¥åºåºåå·" |
| | | clearable |
| | | style="width: 200px;" /> |
| | | <el-input v-model="filters.outboundBatches" placeholder="请è¾å
¥åºåºåå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·åç§°:"> |
| | | <el-input v-model="filters.customerName" |
| | | placeholder="请è¾å
¥å®¢æ·åç§°" |
| | | clearable |
| | | style="width: 200px;" /> |
| | | <el-input v-model="filters.customerName" placeholder="请è¾å
¥å®¢æ·åç§°" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="åºåºæ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable /> |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="onSearch">æç´¢</el-button> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button @click="handleOut" |
| | | icon="Download">导åº</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" /> |
| | | @pagination="changePage" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | { label: "客æ·åç§°", prop: "customerName", minWidth: "180" }, |
| | | { label: "åºåºæ¥æ", prop: "shippingDate", width: "170" }, |
| | | { label: "产ååç§°", prop: "productName", minWidth: "140" }, |
| | | { label: "è§æ ¼åå·", prop: "specificationModel", minWidth: "140" }, |
| | | { label: "产åè§æ ¼", prop: "specificationModel", minWidth: "140" }, |
| | | { |
| | | label: "éé¢", |
| | | prop: "outboundAmount", |
| | | minWidth: "120", |
| | | formatData: val => |
| | | val === null || val === undefined || val === "" |
| | | ? "" |
| | | : Number(val).toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }), |
| | | align: "right", |
| | | formatData: (val) => (val === null || val === undefined || val === "" ? "" : Number(val).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 })), |
| | | }, |
| | | { label: "åè´§ç¼å·", prop: "shippingNo", minWidth: "140" }, |
| | | { label: "éå®è®¢åå·", prop: "salesContractNo", minWidth: "150" }, |
| | |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }) |
| | | .then(res => { |
| | | .then((res) => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "outboundAmount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.outboundAmount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | isShowSummary |
| | | :summaryMethod="getSummaries" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | { label: "éè´§åå·", prop: "returnNo", minWidth: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", minWidth: "180" }, |
| | | { label: "å
³èåè´§åå·", prop: "shippingNo", minWidth: "150" }, |
| | | { |
| | | label: "åè´§ç±»å", |
| | | prop: "shippingType", |
| | | minWidth: "110", |
| | | formatData: (val) => ({ 1: "货车", 2: "å¿«é" }[String(val)] || "--"), |
| | | }, |
| | | { |
| | | label: "å货车çå·", |
| | | prop: "truckPlateNo", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "1" ? row?.truckPlateNo || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "å¿«éå
¬å¸", |
| | | prop: "expressCompany", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressCompany || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "å¿«éåå·", |
| | | prop: "expressNo", |
| | | minWidth: "150", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressNo || "--" : "--"), |
| | | }, |
| | | { label: "éè´§æ¥æ", prop: "makeTime", minWidth: "170" }, |
| | | { |
| | | label: "鿬¾æ»é¢", |
| | | prop: "refundAmount", |
| | | minWidth: "120", |
| | | align: "right", |
| | | formatData: (val) => |
| | | val === null || val === undefined || val === "" |
| | | ? "" |
| | |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getSummaries = ({ columns, data }) => { |
| | | const sums = []; |
| | | columns.forEach((col, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | } else if (col.property === "refundAmount") { |
| | | const total = data.reduce((prev, cur) => { |
| | | const v = Number(cur.refundAmount); |
| | | return prev + (isNaN(v) ? 0 : v); |
| | | }, 0); |
| | | sums[index] = total.toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | return map[key] || ""; |
| | | }; |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // èè°çº¦å®ï¼åé¡µåæ°ä½¿ç¨ current/sizeï¼æ¥æèå´æå为 startDate/endDate |
| | | const getTableData = async () => { |
| | | try { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | |
| | | <el-radio-button :value="'waiting'">å¾
å¼å§({{ orderProgressMeta.waitingCount }})</el-radio-button> |
| | | <el-radio-button :value="'inProgress'">è¿è¡ä¸({{ orderProgressMeta.inProgressCount }})</el-radio-button> |
| | | <el-radio-button :value="'completed'">已宿({{ orderProgressMeta.completedCount }})</el-radio-button> |
| | | <el-radio-button :value="'end'">å·²ç»æ({{ orderProgressMeta.endCount }})</el-radio-button> |
| | | <el-radio-button :value="'paused'">å·²æå({{ orderProgressMeta.pausedCount }})</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | <div class="order-table-wrap"> |
| | | <el-table :data="filteredOrders" stripe max-height="440"> |
| | | <el-table :data="filteredOrders" stripe> |
| | | <el-table-column prop="orderNo" label="订åç¼å·" min-width="150" /> |
| | | <el-table-column prop="productName" label="产ååç§°" min-width="120" /> |
| | | <el-table-column prop="planQty" label="è®¡åæ°é" min-width="90" /> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-if="visiblePanels.contract" class="cockpit-panel contract-panel"> |
| | |
| | | <div v-if="visiblePanels.todo" class="cockpit-panel todo-panel"> |
| | | <div class="panel-title-row"> |
| | | <div class="panel-title">å¾
åäºé¡¹</div> |
| | | <span class="panel-more" @click="openTodoDialog">æ´å¤</span> |
| | | <span class="panel-more" @click="goToApprovalProcess">æ´å¤</span> |
| | | </div> |
| | | <ul class="todo-list" v-if="todoList.length > 0"> |
| | | <li v-for="item in todoList" :key="item.id" class="todo-item"> |
| | |
| | | <div v-if="visiblePanels.realtime" class="cockpit-panel realtime-panel"> |
| | | <div class="panel-title-row"> |
| | | <div class="panel-title">çäº§å®æ¶çæ¿</div> |
| | | <span class="panel-more" @click="goToProductionOrder">æ´å¤</span> |
| | | </div> |
| | | <div class="realtime-grid"> |
| | | <div class="realtime-item" v-for="item in realtimeBoard" :key="item.key"> |
| | |
| | | <div v-if="visiblePanels.plan" class="cockpit-panel plan-panel"> |
| | | <div class="panel-title-row"> |
| | | <div class="panel-title">仿¥ç产计å</div> |
| | | <span class="panel-more">{{ todayPlanTotal }}项</span> |
| | | <span class="panel-more" @click="goToProductionPlan">{{ todayPlanTotal }}项</span> |
| | | </div> |
| | | <ul class="plan-list"> |
| | | <li v-for="item in todayPlanList" :key="item.orderNo" class="plan-item"> |
| | |
| | | <el-button type="primary" @click="handleProcessDialogConfirm">确认</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <el-dialog v-model="todoDialogVisible" title="å
¨é¨å¾
åäºé¡¹" width="900px" append-to-body destroy-on-close> |
| | | <div v-loading="todoDialogLoading" class="todo-dialog-body"> |
| | | <el-table :data="todoDialogList" stripe max-height="520"> |
| | | <el-table-column prop="approveId" label="å¾
åç¼å·" min-width="160" /> |
| | | <el-table-column prop="approveDeptName" label="é¨é¨/模æ¿" min-width="160" /> |
| | | <el-table-column prop="approveReason" label="äºç±" min-width="240" show-overflow-tooltip /> |
| | | <el-table-column prop="approveUserName" label="å起人" min-width="120" /> |
| | | <el-table-column prop="approveTime" label="æ¶é´" min-width="170" /> |
| | | </el-table> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | |
| | | }); |
| | | |
| | | const realtimeBoardData = ref({ |
| | | deviceOee: { value: 0, compareYesterday: 0 }, |
| | | orderAchievementRate: { value: 0, compareYesterday: 0 }, |
| | | defectRate: { value: 0, compareYesterday: 0 }, |
| | | }); |
| | |
| | | waitingCount: 0, |
| | | inProgressCount: 0, |
| | | completedCount: 0, |
| | | endCount: 0, |
| | | pausedCount: 0, |
| | | }); |
| | | |
| | | const todayPlanList = ref([]); |
| | | const todayPlanTotal = ref(0); |
| | | const todoDialogVisible = ref(false); |
| | | const todoDialogLoading = ref(false); |
| | | const todoDialogList = ref([]); |
| | | |
| | | const sum = ref(0); |
| | | const yny = ref(0); |
| | |
| | | |
| | | const productionOrders = ref([]); |
| | | |
| | | const orderFilterOptions = ["all", "waiting", "inProgress", "completed", "end"]; |
| | | const orderFilterOptions = ["all", "waiting", "inProgress", "completed", "paused"]; |
| | | const orderFilterAliasMap = { |
| | | 1: "waiting", |
| | | 2: "inProgress", |
| | | 3: "completed", |
| | | 5: "end", |
| | | 4: "paused", |
| | | }; |
| | | const orderFilter = ref("all"); |
| | | const filteredOrders = computed(() => productionOrders.value); |
| | |
| | | }; |
| | | |
| | | const realtimeBoard = computed(() => { |
| | | const oee = Number(realtimeBoardData.value.deviceOee?.value || 0); |
| | | const orderAchievement = Number(realtimeBoardData.value.orderAchievementRate?.value || 0); |
| | | const defectRate = Number(realtimeBoardData.value.defectRate?.value || 0); |
| | | const oeeCompare = Number(realtimeBoardData.value.deviceOee?.compareYesterday || 0); |
| | | const orderCompare = Number(realtimeBoardData.value.orderAchievementRate?.compareYesterday || 0); |
| | | const defectCompare = Number(realtimeBoardData.value.defectRate?.compareYesterday || 0); |
| | | return [ |
| | | { |
| | | key: "oee", |
| | | label: "è®¾å¤ OEE", |
| | | percent: clampPercent(oee), |
| | | display: `${oee.toFixed(2)}%`, |
| | | delta: getCompareText(oeeCompare), |
| | | trend: getCompareTrend(oeeCompare), |
| | | color: "#2d8cff", |
| | | }, |
| | | { |
| | | key: "order", |
| | | label: "订åè¾¾æç", |
| | |
| | | display: `${orderAchievement.toFixed(2)}%`, |
| | | delta: getCompareText(orderCompare), |
| | | trend: getCompareTrend(orderCompare), |
| | | color: "#31d2ff", |
| | | color: "#2d8cff", |
| | | }, |
| | | { |
| | | key: "defect", |
| | |
| | | pathPrefixes: ["/inventorymanagement"], |
| | | }, |
| | | production: { |
| | | titles: ["ç产管æ§"], |
| | | pathPrefixes: ["/productionManagement", "/productionPlan"], |
| | | titles: ["ç产管ç", "主ç产计å", "ç产订å", "ç产æ¥å·¥"], |
| | | pathPrefixes: ["/productionmanagement", "/productionplan"], |
| | | }, |
| | | quality: { |
| | | titles: ["è´¨é管ç"], |
| | |
| | | realtime: visibleModules.value.production, |
| | | quick: quickEntries.value.length > 0, |
| | | plan: visibleModules.value.production, |
| | | receipt: visibleModules.value.finance, |
| | | receipt: visibleModules.value.sales || visibleModules.value.finance, |
| | | })); |
| | | |
| | | const hasLeftPanels = computed( |
| | |
| | | 1: "å¾
å¼å§", |
| | | 2: "è¿è¡ä¸", |
| | | 3: "已宿", |
| | | 5: "å·²ç»æ", |
| | | 4: "å·²æå", |
| | | }; |
| | | return mapping[status] || "æªç¥"; |
| | | }; |
| | |
| | | const res = await productionRealtimeBoard(); |
| | | const data = res?.data || {}; |
| | | realtimeBoardData.value = { |
| | | deviceOee: { |
| | | value: Number(data.deviceOee?.value || 0), |
| | | compareYesterday: Number(data.deviceOee?.compareYesterday || 0), |
| | | }, |
| | | orderAchievementRate: { |
| | | value: Number(data.orderAchievementRate?.value || 0), |
| | | compareYesterday: Number(data.orderAchievementRate?.compareYesterday || 0), |
| | |
| | | }; |
| | | } catch { |
| | | realtimeBoardData.value = { |
| | | deviceOee: { value: 0, compareYesterday: 0 }, |
| | | orderAchievementRate: { value: 0, compareYesterday: 0 }, |
| | | defectRate: { value: 0, compareYesterday: 0 }, |
| | | }; |
| | |
| | | const res = await productionOrderProgress({ |
| | | status: orderFilter.value, |
| | | tab: orderFilter.value, |
| | | pageNum: -1, |
| | | pageSize: -1, |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | }); |
| | | const data = res?.data || {}; |
| | | const statusValue = normalizeOrderFilter(data.status, orderFilter.value); |
| | |
| | | waitingCount: resolveProgressCount(data.waitingCount, statusValue, "waiting", total), |
| | | inProgressCount: resolveProgressCount(data.inProgressCount, statusValue, "inProgress", total), |
| | | completedCount: resolveProgressCount(data.completedCount, statusValue, "completed", total), |
| | | endCount: resolveProgressCount(data.endCount, statusValue, "end", total), |
| | | pausedCount: resolveProgressCount(data.pausedCount, statusValue, "paused", total), |
| | | }; |
| | | productionOrders.value = (data.records || []).map(mapOrderProgressRecord); |
| | | } catch { |
| | |
| | | waitingCount: 0, |
| | | inProgressCount: 0, |
| | | completedCount: 0, |
| | | endCount: 0, |
| | | pausedCount: 0, |
| | | }; |
| | | productionOrders.value = []; |
| | | } |
| | |
| | | const refreshTodayProductionPlan = async () => { |
| | | try { |
| | | const res = await todayProductionPlan({ |
| | | limit: 1000, |
| | | limit: 4, |
| | | planDate: nowDate.value, |
| | | }); |
| | | const data = res?.data || {}; |
| | |
| | | const todoInfoS = async () => { |
| | | const res = await homeTodos(); |
| | | todoList.value = res.data || []; |
| | | }; |
| | | |
| | | const openTodoDialog = async () => { |
| | | todoDialogVisible.value = true; |
| | | todoDialogLoading.value = true; |
| | | try { |
| | | const res = await homeTodos(); |
| | | todoDialogList.value = res.data || []; |
| | | } catch { |
| | | todoDialogList.value = []; |
| | | } finally { |
| | | todoDialogLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const statisticsReceivable = async () => { |
| | |
| | | const goToQuick = (path) => { |
| | | if (!path) return; |
| | | router.push(path).catch(() => {}); |
| | | }; |
| | | |
| | | const goToProductionOrder = () => { |
| | | router.push('/productionManagement/productionOrder').catch(() => {}); |
| | | }; |
| | | |
| | | const goToApprovalProcess = () => { |
| | | router.push('/collaborativeApproval/approvalProcess').catch(() => {}); |
| | | }; |
| | | |
| | | const goToProductionPlan = () => { |
| | | router.push('/productionManagement/productionPlan').catch(() => {}); |
| | | }; |
| | | |
| | | watch(orderFilter, () => { |
| | |
| | | } |
| | | |
| | | .stats-grid { |
| | | display: flex; |
| | | display: grid; |
| | | grid-template-columns: repeat(4, minmax(0, 1fr)); |
| | | gap: 14px; |
| | | } |
| | | |
| | | .stats-grid .stat-card { |
| | | flex: 1; |
| | | } |
| | | |
| | | .stat-card { |
| | |
| | | font-size: 14px; |
| | | color: #2563eb; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .todo-dialog-body { |
| | | min-height: 220px; |
| | | } |
| | | |
| | | .plan-panel { |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .plan-list { |
| | | margin: 10px 0 0; |
| | | padding: 0 4px 0 0; |
| | | list-style: none; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | max-height: 300px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .plan-list::-webkit-scrollbar { |
| | | width: 6px; |
| | | } |
| | | |
| | | .plan-list::-webkit-scrollbar-thumb { |
| | | border-radius: 999px; |
| | | background: rgba(37, 99, 235, 0.28); |
| | | } |
| | | |
| | | .todo-list { |
| | |
| | | .realtime-grid { |
| | | margin-top: 10px; |
| | | display: grid; |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | gap: 10px; |
| | | } |
| | | |
| | |
| | | border: 1px solid rgba(148, 163, 184, 0.2); |
| | | border-radius: 12px; |
| | | background: #f8fbff; |
| | | padding: 10px 8px; |
| | | padding: 16px 12px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .realtime-value { |
| | | font-size: 26px; |
| | | font-size: 32px; |
| | | color: #0f172a; |
| | | font-weight: 700; |
| | | } |
| | |
| | | |
| | | .order-panel { |
| | | min-height: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .order-table-wrap { |
| | | flex: 1; |
| | | min-height: 0; |
| | | } |
| | | |
| | | .quick-panel { |
| | |
| | | transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; |
| | | } |
| | | |
| | | .plan-panel { |
| | | min-height: 236px; |
| | | } |
| | | |
| | | .quality-panel { |
| | | min-height: 0; |
| | | } |
| | | |
| | | .receipt-panel { |
| | | min-height: 340px; |
| | | } |
| | | |
| | | .plan-list { |
| | | margin: 10px 0 0; |
| | | padding: 0; |
| | | list-style: none; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .plan-item { |
| | |
| | | .main-grid { |
| | | grid-template-columns: minmax(0, 1fr) 340px; |
| | | } |
| | | |
| | | .stats-grid { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 1200px) { |
| | |
| | | } |
| | | |
| | | .realtime-grid { |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .quality-cards { |
| | |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .stats-grid { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .right-column { |
| | | grid-template-columns: 1fr; |
| | | } |
| | |
| | | <template> |
| | | <div> |
| | | <div class="search_form" |
| | | style="margin-bottom: 10px"> |
| | | <el-form ref="searchFormRef" |
| | | <div class="search_form" style="margin-bottom: 10px"> |
| | | <el-form |
| | | ref="searchFormRef" |
| | | :model="searchForm" |
| | | class="demo-form-inline"> |
| | | class="demo-form-inline" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="åºåºæ¥æ" |
| | | prop="timeStr"> |
| | | <el-form-item label="åºåºæ¥æ" prop="timeStr"> |
| | | <el-date-picker v-model="searchForm.timeStr" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="产å大类" |
| | | prop="productName"> |
| | | <el-form-item label="产å大类" prop="productName"> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="4"> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | prop="model"> |
| | | <el-form-item label="产åè§æ ¼" prop="model"> |
| | | <el-input v-model="searchForm.model" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¹å·" |
| | | prop="batchNo"> |
| | | <el-form-item label="æ¹å·" prop="batchNo"> |
| | | <el-input v-model="searchForm.batchNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¥æº" |
| | | prop="recordType"> |
| | | <el-select v-model="searchForm.recordType" |
| | | <el-form-item label="æ¥æº" prop="recordType"> |
| | | <el-select |
| | | v-model="searchForm.recordType" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in stockRecordTypeOptions" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in stockRecordTypeOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <!-- æé® --> |
| | | <el-col :span="4"> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getList"> |
| | | <el-button type="primary" @click="getList"> |
| | | æç´¢ |
| | | </el-button> |
| | | |
| | | <el-button @click="resetSearch"> |
| | | éç½® |
| | | </el-button> |
| | |
| | | </el-form> |
| | | </div> |
| | | <div class="actions"> |
| | | <el-button type="primary" |
| | | @click="handleBatchApprove">审æ¹</el-button> |
| | | <el-button :disabled="!canReverseApprove" |
| | | @click="handleReverseApprove">å审</el-button> |
| | | <!-- <el-button type="primary" @click="handleBatchApprove">审æ¹</el-button> --> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handlePrint">æå°</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | <el-button type="primary" plain @click="handlePrint">æå°</el-button> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table :data="tableData" |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | style="width: 100%" |
| | | height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="åºåºæ¹æ¬¡" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column |
| | | label="åºåºæ¹æ¬¡" |
| | | prop="outboundBatches" |
| | | min-width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åºåºæ¥æ" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åºåºæ¥æ" |
| | | prop="createTime" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="产å大类" |
| | | width="120" |
| | | show-overflow-tooltip |
| | | > |
| | | <template #default="scope"> |
| | | <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="产å大类" |
| | | prop="productName" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | | prop="model" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ¹å·" |
| | | prop="batchNo" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åä½" |
| | | prop="unit" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åºåºæ°é" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="è§æ ¼åå·" prop="model" show-overflow-tooltip /> |
| | | <el-table-column label="æ¹å·" prop="batchNo" width="150" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" width="80" show-overflow-tooltip /> |
| | | <el-table-column |
| | | label="åºåºæ°é" |
| | | prop="stockOutNum" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åºåºäºº" |
| | | prop="createBy" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ¥æº" |
| | | prop="recordType" |
| | | show-overflow-tooltip> |
| | | width="80" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="åºåºäºº" prop="createBy" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="æ¥æº" prop="recordType" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ getRecordType(scope.row.recordType) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="审æ¹ç¶æ" |
| | | <!-- <el-table-column |
| | | label="审æ¹ç¶æ" |
| | | prop="approvalStatus" |
| | | show-overflow-tooltip> |
| | | show-overflow-tooltip |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" |
| | | size="small"> |
| | | <el-tag |
| | | :type="getApprovalStatusTagType(scope.row.approvalStatus)" |
| | | size="small" |
| | | > |
| | | {{ getApprovalStatusLabel(scope.row.approvalStatus) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> --> |
| | | </el-table> |
| | | <pagination v-show="total > 0" |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | getStockOutPage, |
| | | delPendingStockOut, |
| | | batchApproveStockOutRecords, |
| | | batchUnapproveStockOutRecords, |
| | | } from "@/api/inventoryManagement/stockOut.js"; |
| | | import { |
| | | findAllQualifiedStockOutRecordTypeOptions, |
| | | findAllUnQualifiedStockOutRecordTypeOptions, |
| | | } from "@/api/basicData/enum.js"; |
| | | import { parseTime } from "@/utils/ruoyi"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | |
| | | searchFormRef.value?.resetFields(); |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | } |
| | | |
| | | const paginationChange = obj => { |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | ...page, |
| | | topParentProductId: props.topParentProductId, |
| | | }) |
| | | .then(res => { |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value.map(item => { |
| | | tableData.value.map((item) => { |
| | | item.children = []; |
| | | }); |
| | | total.value = res.data.total; |
| | |
| | | }); |
| | | }; |
| | | |
| | | const getRecordType = recordType => { |
| | | const getRecordType = (recordType) => { |
| | | return ( |
| | | stockRecordTypeOptions.value.find(item => item.value === recordType) |
| | | stockRecordTypeOptions.value.find((item) => item.value === recordType) |
| | | ?.label || "" |
| | | ); |
| | | }; |
| | |
| | | REJECTED: "驳å", |
| | | }; |
| | | |
| | | const getApprovalStatusLabel = status => { |
| | | const getApprovalStatusLabel = (status) => { |
| | | if (status === null || status === undefined || status === "") { |
| | | return "å¾
审æ¹"; |
| | | } |
| | |
| | | }; |
| | | |
| | | // éè¿/驳ååºå®è²ï¼å
¶ä½ï¼å«å¾
审æ¹ã空å¼ãæªæ å°ä½ææ¡ä¸ºå¾
审æ¹ï¼ç»ä¸ç¨ warning é¢è¦è² |
| | | const getApprovalStatusTagType = status => { |
| | | const getApprovalStatusTagType = (status) => { |
| | | if ( |
| | | status === 1 || |
| | | status === "1" || |
| | |
| | | // è·åæ¥æºç±»åé项 |
| | | const fetchStockRecordTypeOptions = () => { |
| | | if (props.type === "0") { |
| | | findAllQualifiedStockOutRecordTypeOptions().then(res => { |
| | | findAllQualifiedStockOutRecordTypeOptions().then((res) => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | }); |
| | | return; |
| | | } |
| | | findAllUnQualifiedStockOutRecordTypeOptions().then(res => { |
| | | findAllUnQualifiedStockOutRecordTypeOptions().then((res) => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | const handleSelectionChange = (selection) => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter(item => item.id); |
| | | selectedRows.value = selection.filter((item) => item.id); |
| | | console.log("selection", selectedRows.value); |
| | | }; |
| | | |
| | | const isRejectedApproval = status => { |
| | | return status === 2 || status === "2" || status === "rejected" || status === "REJECTED"; |
| | | }; |
| | | |
| | | const canReverseApprove = computed(() => { |
| | | return ( |
| | | selectedRows.value.length > 0 && |
| | | selectedRows.value.every(row => isRejectedApproval(row.approvalStatus)) |
| | | ); |
| | | }); |
| | | |
| | | const handleReverseApprove = () => { |
| | | if (!canReverseApprove.value) { |
| | | proxy.$modal.msgWarning("è¯·éæ©é©³åç¶æçæ°æ®"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id); |
| | | ElMessageBox.confirm("å审åè®°å½å°æ¢å¤ä¸ºå¾
审æ¹ç¶æï¼æ¯å¦ç¡®è®¤å审ï¼", "å审", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | batchUnapproveStockOutRecords({ ids }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("å审æå"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError("å审失败"); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | const expandedRowKeys = ref([]); |
| | | |
| | | const handleBatchApprove = () => { |
| | |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id); |
| | | const ids = selectedRows.value.map((item) => item.id); |
| | | ElMessageBox.confirm("è¯·éæ©å®¡æ¹ç»æ", "审æ¹", { |
| | | confirmButtonText: "éè¿", |
| | | cancelButtonText: "驳å", |
| | |
| | | proxy.$modal.msgError("审æ¹éè¿å¤±è´¥"); |
| | | }); |
| | | }) |
| | | .catch(action => { |
| | | .catch((action) => { |
| | | if (action === "cancel") { |
| | | batchApproveStockOutRecords({ ids, approvalStatus: 2 }) |
| | | .then(() => { |
| | |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delPendingStockOut(ids).then(res => { |
| | | delPendingStockOut(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥æ |
| | | const formatDate = dateString => { |
| | | const formatDate = (dateString) => { |
| | | if (!dateString) return getCurrentDate(); |
| | | const date = new Date(dateString); |
| | | const year = date.getFullYear(); |
| | |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = date => { |
| | | const formatDateTime = (date) => { |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | |
| | | <template> |
| | | <div> |
| | | <div class="search_form" |
| | | style="margin-bottom: 10px;"> |
| | | <el-form ref="searchFormRef" |
| | | <div class="search_form" style="margin-bottom: 10px;"> |
| | | <el-form |
| | | ref="searchFormRef" |
| | | :model="searchForm" |
| | | class="demo-form-inline"> |
| | | class="demo-form-inline" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="å
¥åºæ¥æ" |
| | | prop="timeStr"> |
| | | <el-form-item label="å
¥åºæ¥æ" prop="timeStr"> |
| | | <el-date-picker v-model="searchForm.timeStr" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="产å大类" |
| | | prop="productName"> |
| | | <el-form-item label="产å大类" prop="productName"> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="4"> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | prop="model"> |
| | | <el-form-item label="产åè§æ ¼" prop="model"> |
| | | <el-input v-model="searchForm.model" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¹å·" |
| | | prop="batchNo"> |
| | | <el-form-item label="æ¹å·" prop="batchNo"> |
| | | <el-input v-model="searchForm.batchNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¥æº" |
| | | prop="recordType"> |
| | | <el-form-item label="æ¥æº" prop="recordType"> |
| | | <el-select v-model="searchForm.recordType" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©" |
| | |
| | | <!-- æé® --> |
| | | <el-col :span="4"> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getList"> |
| | | <el-button type="primary" @click="getList"> |
| | | æç´¢ |
| | | </el-button> |
| | | |
| | | <el-button @click="resetSearch"> |
| | | éç½® |
| | | </el-button> |
| | |
| | | </el-form> |
| | | </div> |
| | | <div class="actions"> |
| | | <el-button type="primary" |
| | | <!-- <el-button type="primary" |
| | | :disabled="!canBatchApprove" |
| | | @click="handleBatchApprove">审æ¹</el-button> |
| | | <el-button :disabled="!canReverseApprove" |
| | | @click="handleReverseApprove">å审</el-button> |
| | | @click="handleReverseApprove">å审</el-button> --> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | |
| | | width="60" /> |
| | | <el-table-column label="å
¥åºæ¹æ¬¡" |
| | | prop="inboundBatches" |
| | | width="200" |
| | | width="150" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="å
¥åºæ¶é´" |
| | | prop="createTime" |
| | | show-overflow-tooltip /> |
| | | width="120" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="产å大类" |
| | | prop="productName" |
| | | show-overflow-tooltip /> |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ¹å·" |
| | | prop="batchNo" |
| | | width="180" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åä½" |
| | | prop="unit" |
| | |
| | | {{ getRecordType(scope.row.recordType) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column v-if="showSourceOrderNoColumn" |
| | | <el-table-column |
| | | v-if="showSourceOrderNoColumn" |
| | | label="æºåå·" |
| | | width="150" |
| | | prop="sourceOrderNo" |
| | |
| | | {{ formatSourceOrderNo(scope.row?.sourceOrderNo) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="审æ¹ç¶æ" |
| | | <!-- <el-table-column label="审æ¹ç¶æ" |
| | | prop="approvalStatus" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" |
| | | size="small"> |
| | | <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small"> |
| | | {{ getApprovalStatusLabel(scope.row.approvalStatus) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> --> |
| | | </el-table> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | |
| | | findAllQualifiedStockInRecordTypeOptions, |
| | | // findAllUnQualifiedStockInRecordTypeOptions, |
| | | } from "@/api/basicData/enum.js"; |
| | | import { parseTime } from "@/utils/ruoyi"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | |
| | | type: { |
| | | type: String, |
| | | required: true, |
| | | default: "0", |
| | | default: '0' |
| | | }, |
| | | topParentProductId: { |
| | | type: [String, Number], |
| | | default: undefined, |
| | | }, |
| | | }); |
| | | default: undefined |
| | | } |
| | | }) |
| | | |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | |
| | | searchFormRef.value?.resetFields(); |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | } |
| | | |
| | | const getRecordType = recordType => { |
| | | return ( |
| | | stockRecordTypeOptions.value.find(item => item.value === recordType) |
| | | ?.label || "" |
| | | ); |
| | | }; |
| | | const getRecordType = (recordType) => { |
| | | return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || '' |
| | | } |
| | | |
| | | const approvalStatusLabelMap = { |
| | | 0: "å¾
审æ¹", |
| | |
| | | }; |
| | | approvalStatusLabelMap[3] = "å¾
确认"; |
| | | |
| | | const getApprovalStatusLabel = status => { |
| | | const getApprovalStatusLabel = (status) => { |
| | | if (status === null || status === undefined || status === "") { |
| | | return "å¾
审æ¹"; |
| | | } |
| | |
| | | }; |
| | | |
| | | // éè¿/驳ååºå®è²ï¼å
¶ä½ï¼å«å¾
审æ¹ã空å¼ãæªæ å°ä½ææ¡ä¸ºå¾
审æ¹ï¼ç»ä¸ç¨ warning é¢è¦è² |
| | | const getApprovalStatusTagType = status => { |
| | | if ( |
| | | status === 1 || |
| | | status === "1" || |
| | | status === "approved" || |
| | | status === "APPROVED" |
| | | ) |
| | | return "success"; |
| | | if ( |
| | | status === 2 || |
| | | status === "2" || |
| | | status === "rejected" || |
| | | status === "REJECTED" |
| | | ) |
| | | return "danger"; |
| | | const getApprovalStatusTagType = (status) => { |
| | | if (status === 1 || status === "1" || status === "approved" || status === "APPROVED") return "success"; |
| | | if (status === 2 || status === "2" || status === "rejected" || status === "REJECTED") return "danger"; |
| | | return "warning"; |
| | | }; |
| | | |
| | | const isPendingApproval = status => { |
| | | return ( |
| | | status === 0 || |
| | | status === "0" || |
| | | status === "pending" || |
| | | status === "PENDING" || |
| | | status === null || |
| | | status === undefined || |
| | | status === "" |
| | | ); |
| | | return status === 0 || status === "0" || status === "pending" || status === "PENDING" || status === null || status === undefined || status === ""; |
| | | }; |
| | | |
| | | const isRejectedApproval = status => { |
| | | return ( |
| | | status === 2 || |
| | | status === "2" || |
| | | status === "rejected" || |
| | | status === "REJECTED" |
| | | ); |
| | | return status === 2 || status === "2" || status === "rejected" || status === "REJECTED"; |
| | | }; |
| | | |
| | | const isRowSelectable = row => { |
| | | return ( |
| | | isPendingApproval(row?.approvalStatus) || |
| | | isRejectedApproval(row?.approvalStatus) |
| | | ); |
| | | return isPendingApproval(row?.approvalStatus) || isRejectedApproval(row?.approvalStatus); |
| | | }; |
| | | |
| | | const canBatchApprove = computed(() => { |
| | | return ( |
| | | selectedRows.value.length > 0 && |
| | | selectedRows.value.every(row => isPendingApproval(row.approvalStatus)) |
| | | ); |
| | | return selectedRows.value.length > 0 |
| | | && selectedRows.value.every(row => isPendingApproval(row.approvalStatus)); |
| | | }); |
| | | |
| | | const canReverseApprove = computed(() => { |
| | | return ( |
| | | selectedRows.value.length > 0 && |
| | | selectedRows.value.every(row => isRejectedApproval(row.approvalStatus)) |
| | | ); |
| | | return selectedRows.value.length > 0 |
| | | && selectedRows.value.every(row => isRejectedApproval(row.approvalStatus)); |
| | | }); |
| | | |
| | | const canDelete = computed(() => canBatchApprove.value); |
| | | const showSourceOrderNoColumn = computed(() => { |
| | | const topParentProductId = Number(props.topParentProductId); |
| | | return topParentProductId === 276 || topParentProductId === 278; |
| | | return topParentProductId === 1 || topParentProductId === 3; |
| | | }); |
| | | |
| | | const formatSourceOrderNo = value => { |
| | | const formatSourceOrderNo = (value) => { |
| | | const text = String(value ?? "").trim(); |
| | | return text || "--"; |
| | | }; |
| | |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | getStockInRecordListPage( |
| | | Object.assign( |
| | | {}, |
| | | { |
| | | ...searchForm.value, |
| | | ...page, |
| | | topParentProductId: props.topParentProductId, |
| | | } |
| | | ) |
| | | ) |
| | | getStockInRecordListPage(Object.assign({}, {...searchForm.value, ...page, topParentProductId: props.topParentProductId})) |
| | | .then(res => { |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total || 0; |
| | | }) |
| | | .finally(() => { |
| | | }).finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | }; |
| | | |
| | | // è·åæ¥æºç±»åé项 |
| | | const fetchStockRecordTypeOptions = () => { |
| | | if (props.type === "0") { |
| | | findAllQualifiedStockInRecordTypeOptions().then(res => { |
| | | if (props.type === '0') { |
| | | findAllQualifiedStockInRecordTypeOptions() |
| | | .then(res => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // findAllUnQualifiedStockInRecordTypeOptions() |
| | | // .then(res => { |
| | | // stockRecordTypeOptions.value = res.data; |
| | | // }) |
| | | }; |
| | | } |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection.filter( |
| | | item => item.id && isRowSelectable(item) |
| | | ); |
| | | selectedRows.value = selection.filter(item => item.id && isRowSelectable(item)); |
| | | }; |
| | | |
| | | const expandedRowKeys = ref([]); |
| | |
| | | proxy.$modal.msgError("审æ¹éè¿å¤±è´¥"); |
| | | }); |
| | | }) |
| | | .catch(action => { |
| | | .catch((action) => { |
| | | if (action === "cancel") { |
| | | batchApproveStockInRecords({ ids, approvalStatus: 2 }) |
| | | .then(() => { |
| | |
| | | }) |
| | | .then(() => { |
| | | // æ ¹æ®ä¸åç tab ç±»åè°ç¨ä¸åçå¯¼åºæ¥å£ |
| | | proxy.download( |
| | | "/stockInRecord/exportStockInRecord", |
| | | { type: props.type }, |
| | | props.type === "0" ? "åæ ¼å
¥åº.xlsx" : "ä¸åæ ¼å
¥åº.xlsx" |
| | | ); |
| | | proxy.download("/stockInRecord/exportStockInRecord", {type: props.type}, props.type === '0' ? "åæ ¼å
¥åº.xlsx" : "ä¸åæ ¼å
¥åº.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | prop="qualifiedQuantity" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | <!-- <el-table-column |
| | | label="ä¸åæ ¼åºåæ°é" |
| | | prop="unQualifiedQuantity" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | /> --> |
| | | <!-- <el-table-column |
| | | label="åæ ¼å»ç»æ°é" |
| | | prop="qualifiedLockedQuantity" |
| | | show-overflow-tooltip |
| | |
| | | label="ä¸åæ ¼å»ç»æ°é" |
| | | prop="unQualifiedLockedQuantity" |
| | | show-overflow-tooltip |
| | | /> |
| | | /> --> |
| | | <el-table-column |
| | | label="åºåé¢è¦æ°é" |
| | | prop="warnNum" |
| | |
| | | prop="updateTime" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column fixed="right" label="æä½" min-width="180" align="center"> |
| | | <el-table-column fixed="right" label="æä½" min-width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | |
| | | " |
| | | >é¢ç¨</el-button |
| | | > |
| | | <el-button |
| | | <!-- <el-button |
| | | link |
| | | type="primary" |
| | | v-if=" |
| | |
| | | " |
| | | @click="handleFrozen(scope.row)" |
| | | >å»ç»</el-button |
| | | > |
| | | > --> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | |
| | | placeholder="è¯·éæ©åºåç±»å"> |
| | | <el-option label="åæ ¼åºå" |
| | | value="qualified" /> |
| | | <el-option label="ä¸åæ ¼åºå" |
| | | value="unqualified" /> |
| | | <!-- <el-option label="ä¸åæ ¼åºå" |
| | | value="unqualified" /> --> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åºåæ°é" |
| | | prop="qualitity"> |
| | | <el-input-number v-model="formState.qualitity" |
| | | :step="0.01" |
| | | :min="0.01" |
| | | :precision="2" |
| | | :step="1" |
| | | :min="1" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¹å·" |
| | |
| | | label="åºåé¢è¦æ°é" |
| | | prop="warnNum"> |
| | | <el-input-number v-model="formState.warnNum" |
| | | :step="0.01" |
| | | :step="1" |
| | | :min="0" |
| | | :max="formState.qualitity" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" |
| | |
| | | <template> |
| | | <div> |
| | | <div class="search_form mb10"> |
| | | <el-form ref="searchFormRef" |
| | | <el-form |
| | | ref="searchFormRef" |
| | | :model="searchForm" |
| | | class="demo-form-inline"> |
| | | class="demo-form-inline" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="产å大类" |
| | | prop="productName"> |
| | | <el-form-item label="产å大类" prop="productName"> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="4"> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | prop="model"> |
| | | <el-form-item label="产åè§æ ¼" prop="model"> |
| | | <el-input v-model="searchForm.model" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¹å·" |
| | | prop="batchNo"> |
| | | <el-form-item label="æ¹å·" prop="batchNo"> |
| | | <el-input v-model="searchForm.batchNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <!-- æé® --> |
| | | <el-col :span="4"> |
| | | <el-col :span="24" :sm="24" :md="12" :lg="12"> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getList"> |
| | | <div style="display: flex; align-items: center; gap: 4px; width: 100%; flex-wrap: wrap;"> |
| | | <el-button type="primary" @click="getList"> |
| | | æç´¢ |
| | | </el-button> |
| | | |
| | | <el-button @click="resetSearch"> |
| | | éç½® |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <div style="display: flex; gap: 10px; flex-wrap: nowrap;"> |
| | | <el-button type="primary" |
| | | @click="isShowNewModal = true">æ°å¢åºå</el-button> |
| | | <el-button type="info" |
| | | <div style="display: flex; gap: 4px; margin-left: auto;"> |
| | | <el-button type="primary" @click="isShowNewModal = true"> |
| | | æ°å¢åºå |
| | | </el-button> |
| | | <el-button |
| | | type="info" |
| | | plain |
| | | icon="Upload" |
| | | @click="isShowImportModal = true"> |
| | | @click="isShowImportModal = true" |
| | | > |
| | | 导å
¥åºå |
| | | </el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table :data="tableData" |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | |
| | | :row-key="(row, index) => index" |
| | | style="width: 100%" |
| | | :row-class-name="tableRowClassName" |
| | | height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="产ååç§°" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column |
| | | label="产ååç§°" |
| | | prop="productName" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | | prop="model" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åä½" |
| | | prop="unit" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ¹å·" |
| | | prop="batchNo" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åæ ¼åºåæ°é" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="è§æ ¼åå·" prop="model" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" show-overflow-tooltip /> |
| | | <!-- <el-table-column label="æ¹å·" prop="batchNo" show-overflow-tooltip /> --> |
| | | <el-table-column |
| | | label="åæ ¼åºåæ°é" |
| | | prop="qualifiedQuantity" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ä¸åæ ¼åºåæ°é" |
| | | show-overflow-tooltip |
| | | /> |
| | | <!-- <el-table-column |
| | | label="ä¸åæ ¼åºåæ°é" |
| | | prop="unQualifiedQuantity" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åæ ¼å»ç»æ°é" |
| | | show-overflow-tooltip |
| | | /> --> |
| | | <!-- <el-table-column |
| | | label="åæ ¼å»ç»æ°é" |
| | | prop="qualifiedLockedQuantity" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ä¸åæ ¼å»ç»æ°é" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="ä¸åæ ¼å»ç»æ°é" |
| | | prop="unQualifiedLockedQuantity" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åºåé¢è¦æ°é" |
| | | show-overflow-tooltip |
| | | /> --> |
| | | <el-table-column |
| | | label="åºåé¢è¦æ°é" |
| | | prop="warnNum" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="夿³¨" |
| | | prop="remark" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æè¿æ´æ°æ¶é´" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="夿³¨" prop="remark" show-overflow-tooltip /> |
| | | <el-table-column |
| | | label="æè¿æ´æ°æ¶é´" |
| | | prop="updateTime" |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | fixed="right" |
| | | label="æä½" |
| | | min-width="80" |
| | | align="center"> |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | <el-button link |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | @click="showDetailModal(scope.row)">详æ
</el-button> |
| | | @click="showDetailModal(scope.row)" |
| | | >详æ
</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | <batch-no-qty-detail v-if="isShowDetailModal" |
| | | <batch-no-qty-detail |
| | | v-if="isShowDetailModal" |
| | | v-model:visible="isShowDetailModal" |
| | | :record="record" |
| | | @subtract="handleDetailSubtract" |
| | | @frozen="handleDetailFrozen" |
| | | @thaw="handleDetailThaw" /> |
| | | <new-stock-inventory v-if="isShowNewModal" |
| | | @thaw="handleDetailThaw" |
| | | /> |
| | | <new-stock-inventory |
| | | v-if="isShowNewModal" |
| | | v-model:visible="isShowNewModal" |
| | | :top-product-parent-id="props.productId" |
| | | @completed="handleQuery" /> |
| | | <subtract-stock-inventory v-if="isShowSubtractModal" |
| | | @completed="handleQuery" |
| | | /> |
| | | |
| | | <subtract-stock-inventory |
| | | v-if="isShowSubtractModal" |
| | | v-model:visible="isShowSubtractModal" |
| | | :record="record" |
| | | :type="record.stockType" |
| | | @completed="handleQuery" /> |
| | | @completed="handleQuery" |
| | | /> |
| | | <!-- 导å
¥åºå--> |
| | | <import-stock-inventory v-if="isShowImportModal" |
| | | <import-stock-inventory |
| | | v-if="isShowImportModal" |
| | | v-model:visible="isShowImportModal" |
| | | type="qualified" |
| | | @uploadSuccess="handleQuery" /> |
| | | @uploadSuccess="handleQuery" |
| | | /> |
| | | <!-- å»ç»/è§£å»åºå--> |
| | | <frozen-and-thaw-stock-inventory v-if="isShowFrozenAndThawModal" |
| | | <frozen-and-thaw-stock-inventory |
| | | v-if="isShowFrozenAndThawModal" |
| | | v-model:visible="isShowFrozenAndThawModal" |
| | | :record="record" |
| | | :operation-type="operationType" |
| | | :type="record.stockType" |
| | | @completed="handleQuery" /> |
| | | @completed="handleQuery" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | searchFormRef.value?.resetFields(); |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | } |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const paginationChange = obj => { |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | getStockInventoryListPageCombined({ ...searchForm.value, ...page }) |
| | | .then(res => { |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | |
| | | }); |
| | | }; |
| | | |
| | | const handleFileSuccess = response => { |
| | | const handleFileSuccess = (response) => { |
| | | const { code, msg } = response; |
| | | if (code == 200) { |
| | | ElMessage({ message: "导å
¥æå", type: "success" }); |
| | |
| | | }; |
| | | |
| | | // ç¹å»é¢ç¨ |
| | | const showSubtractModal = row => { |
| | | const showSubtractModal = (row) => { |
| | | record.value = row; |
| | | isShowSubtractModal.value = true; |
| | | }; |
| | | |
| | | // ç¹å»è¯¦æ
|
| | | const showDetailModal = row => { |
| | | const showDetailModal = (row) => { |
| | | if (!row?.productId || !row?.productModelId) { |
| | | proxy.$modal.msgError("å½åæ°æ®ç¼ºå°äº§åIDæè§æ ¼åå·ID"); |
| | | return; |
| | |
| | | isShowDetailModal.value = true; |
| | | }; |
| | | |
| | | const handleDetailSubtract = row => { |
| | | const handleDetailSubtract = (row) => { |
| | | isShowDetailModal.value = false; |
| | | showSubtractModal(row); |
| | | }; |
| | | |
| | | const handleDetailFrozen = row => { |
| | | const handleDetailFrozen = (row) => { |
| | | isShowDetailModal.value = false; |
| | | showFrozenModal(row); |
| | | }; |
| | | |
| | | const handleDetailThaw = row => { |
| | | const handleDetailThaw = (row) => { |
| | | isShowDetailModal.value = false; |
| | | showThawModal(row); |
| | | }; |
| | | |
| | | // ç¹å»å»ç» |
| | | const showFrozenModal = row => { |
| | | const showFrozenModal = (row) => { |
| | | record.value = row; |
| | | isShowFrozenAndThawModal.value = true; |
| | | operationType.value = "frozen"; |
| | | }; |
| | | |
| | | // ç¹å»è§£å» |
| | | const showThawModal = row => { |
| | | const showThawModal = (row) => { |
| | | record.value = row; |
| | | isShowFrozenAndThawModal.value = true; |
| | | operationType.value = "thaw"; |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | const handleSelectionChange = (selection) => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter(item => item.id); |
| | | selectedRows.value = selection.filter((item) => item.id); |
| | | console.log("selection", selectedRows.value); |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | |
| | | <div class="login-page"> |
| | | <main class="page"> |
| | | <section class="factory"> |
| | | <!-- <div class="brand hero-brand">--> |
| | | <!-- <div class="logo hero-logo">--> |
| | | <!-- <img--> |
| | | <!-- :src="brandLogoUrl"--> |
| | | <!-- :alt="`${companyName} logo`"--> |
| | | <!-- class="logo-image hero-logo-image"--> |
| | | <!-- @error="handleLogoError"--> |
| | | <!-- />--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <div class="brand hero-brand"> |
| | | <div class="logo hero-logo"> |
| | | <img |
| | | :src="brandLogoUrl" |
| | | :alt="`${companyName} logo`" |
| | | class="logo-image hero-logo-image" |
| | | @error="handleLogoError" |
| | | /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="hero"> |
| | | <div class="chip">æ°åå·¥å · æºè½æäº§ · 设å¤äºè · è´¨é追溯</div> |
| | |
| | | </el-table-column>
|
| | | <el-table-column label="æä½æ¥æ" align="center" prop="operTime" width="180" sortable="custom" :sort-orders="['descending', 'ascending']">
|
| | | <template #default="scope">
|
| | | <span>{{ parseTime(scope.row.operTime) }}</span>
|
| | | <span>{{ parseTime(scope.row.operTime, '{y}-{m}-{d}') }}</span>
|
| | | </template>
|
| | | </el-table-column>
|
| | | <el-table-column label="æ¶èæ¶é´" align="center" prop="costTime" width="110" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']">
|
| | |
| | | <el-col :span="24">
|
| | | <el-form-item label="æä½æ¹æ³ï¼">{{ form.method }}</el-form-item>
|
| | | </el-col>
|
| | | <el-col :span="24">
|
| | | <el-col :span="24" v-if="form.status === 1">
|
| | | <el-form-item label="请æ±åæ°ï¼">{{ form.operParam }}</el-form-item>
|
| | | </el-col>
|
| | | <el-col :span="24">
|
| | | <el-col :span="24" v-if="form.status === 1">
|
| | | <el-form-item label="è¿ååæ°ï¼">{{ form.jsonResult }}</el-form-item>
|
| | | </el-col>
|
| | | <el-col :span="8">
|
| | |
| | | <el-form-item label="æ¶èæ¶é´ï¼">{{ form.costTime }}毫ç§</el-form-item>
|
| | | </el-col>
|
| | | <el-col :span="8">
|
| | | <el-form-item label="æä½æ¶é´ï¼">{{ parseTime(form.operTime) }}</el-form-item>
|
| | | <el-form-item label="æä½æ¶é´ï¼">{{ parseTime(form.operTime, '{y}-{m}-{d}') }}</el-form-item>
|
| | | </el-col>
|
| | | <el-col :span="24">
|
| | | <el-form-item label="å¼å¸¸ä¿¡æ¯ï¼" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item>
|
| | |
| | | <el-option v-for="item in shifts_list" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | :disabled=" |
| | | operationType === 'add' && |
| | | (String(item.label || '').includes('å') || |
| | | String(item.label || '').includes('ä¼')) |
| | | " /> |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æå¡èå´(m)" |
| | | prop="radius" |
| | | v-show="!hideClockFields"> |
| | | prop="radius"> |
| | | <el-input-number v-model="form.radius" |
| | | :min="10" |
| | | :max="1000" |
| | |
| | | </el-form-item> |
| | | <!-- é«å¾·å°å¾éæ© --> |
| | | <el-form-item label="æå¡ä½ç½®" |
| | | prop="longitude" |
| | | v-show="!hideClockFields"> |
| | | prop="longitude"> |
| | | <div class="map-container"> |
| | | <div class="map-header" |
| | | style="margin-bottom: 10px"> |
| | |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="å°ç¹åç§°" |
| | | prop="locationName" |
| | | v-show="!hideClockFields"> |
| | | prop="locationName"> |
| | | <el-input v-model="form.locationName" |
| | | :disabled="operationType === 'view'" |
| | | placeholder="请è¾å
¥å°ç¹åç§°" /> |
| | | </el-form-item> |
| | | <!-- ä¸ä¸çæ¶é´ --> |
| | | <el-form-item label="ä¸çæ¶é´" |
| | | prop="startAt" |
| | | v-show="!hideClockFields"> |
| | | prop="startAt"> |
| | | <el-time-picker v-model="form.startAt" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¸çæ¶é´" |
| | | prop="endAt" |
| | | v-show="!hideClockFields"> |
| | | prop="endAt"> |
| | | <el-time-picker v-model="form.endAt" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm" |
| | | v-if="operationType !== 'view'">ç¡®å®</el-button> |
| | | <el-button type="primary" @click="submitForm" v-if="operationType !== 'view'">ç¡®å®</el-button> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | |
| | | // è·åçæ¬¡åå
¸å¼ |
| | | const { shifts_list } = useDict("shifts_list"); |
| | | |
| | | const selectedShiftLabel = computed(() => { |
| | | const item = shifts_list.value?.find( |
| | | s => String(s.value) === String(form.shift) |
| | | ); |
| | | return item?.label || ""; |
| | | }); |
| | | |
| | | const hideClockFields = computed(() => { |
| | | if (props.operationType === "view") return false; |
| | | return ( |
| | | selectedShiftLabel.value.includes("å") || |
| | | selectedShiftLabel.value.includes("ä¼") |
| | | ); |
| | | }); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const formRef = ref(); |
| | | const form = reactive({ |
| | |
| | | }); |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = computed(() => { |
| | | const base = { |
| | | const rules = { |
| | | sysDeptId: [{ required: true, message: "è¯·éæ©é¨é¨", trigger: "change" }], |
| | | shift: [{ required: true, message: "è¯·éæ©çæ¬¡", trigger: "change" }], |
| | | }; |
| | | |
| | | if (hideClockFields.value) { |
| | | return base; |
| | | } |
| | | |
| | | return { |
| | | ...base, |
| | | locationName: [ |
| | | { required: true, message: "请è¾å
¥å°ç¹åç§°", trigger: "blur" }, |
| | | ], |
| | | longitude: [{ required: true, message: "è¯·éæ©æå¡ä½ç½®", trigger: "blur" }], |
| | | latitude: [{ required: true, message: "è¯·éæ©æå¡ä½ç½®", trigger: "blur" }], |
| | | shift: [{ required: true, message: "è¯·éæ©çæ¬¡", trigger: "change" }], |
| | | radius: [{ required: true, message: "请è¾å
¥æå¡èå´", trigger: "blur" }], |
| | | startAt: [{ required: true, message: "è¯·éæ©ä¸çæ¶é´", trigger: "change" }], |
| | | endAt: [ |
| | | { required: true, message: "è¯·éæ©ä¸çæ¶é´", trigger: "change" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (form.shift && ["å¤ç", "å¤"].includes(form.shift)) { |
| | | callback(); |
| | | return; |
| | | } |
| | | if (form.startAt && value) { |
| | | const startParts = form.startAt.split(":"); |
| | | const endParts = value.split(":"); |
| | |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | // é¨é¨é项 |
| | | const deptOptions = ref([]); |
| | |
| | | watch( |
| | | () => form.radius, |
| | | newValue => { |
| | | if (circle && newValue != null && newValue !== "") { |
| | | circle.setRadius(Number(newValue)); |
| | | if (circle) { |
| | | circle.setRadius(newValue); |
| | | } |
| | | } |
| | | ); |
| | |
| | | () => { |
| | | if (formRef.value && form.endAt) { |
| | | formRef.value.validateField("endAt"); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => hideClockFields.value, |
| | | val => { |
| | | if (!val) return; |
| | | form.radius = null; |
| | | form.longitude = ""; |
| | | form.latitude = ""; |
| | | form.locationName = ""; |
| | | form.startAt = ""; |
| | | form.endAt = ""; |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate([ |
| | | "radius", |
| | | "longitude", |
| | | "latitude", |
| | | "locationName", |
| | | "startAt", |
| | | "endAt", |
| | | ]); |
| | | } |
| | | } |
| | | ); |
| | |
| | | |
| | | // åå§åå°å¾ |
| | | setTimeout(() => { |
| | | if (!hideClockFields.value) { |
| | | initMap(); |
| | | } |
| | | }, 100); |
| | | } |
| | | } |
| | |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <el-form :model="searchForm" |
| | | <!-- æ¥è¯¢æ¡ä»¶ --> |
| | | <!-- <el-form :model="searchForm" |
| | | :inline="true" |
| | | class="search-form mb16"> |
| | | <el-form-item label="é¨é¨ï¼" |
| | | prop="sysDeptId"> |
| | | <el-tree-select v-model="searchForm.sysDeptId" |
| | | prop="countId"> |
| | | <el-tree-select v-model="searchForm.countId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="è¯·éæ©é¨é¨" |
| | | check-strictly |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="å°ç¹ï¼" |
| | | prop="locationName"> |
| | | <el-input v-model="searchForm.locationName" |
| | | placeholder="请è¾å
¥å°ç¹åç§°" |
| | | clearable |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="çæ¬¡ï¼" |
| | | prop="shift"> |
| | | <el-select v-model="searchForm.shift" |
| | | placeholder="è¯·éæ©çæ¬¡" |
| | | clearable |
| | | style="width: 200px"> |
| | | <el-option |
| | | v-for="item in shifts_list" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleSearch"> |
| | | @click="fetchData"> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | |
| | | éç½® |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-form> --> |
| | | <!-- çæ¬¡å表 --> |
| | | <el-card shadow="never" |
| | | class="mb16"> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="locationName" |
| | | label="å°ç¹åç§°"> |
| | | <template #default="scope"> |
| | | {{ scope.row.locationName || "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | label="å°ç¹åç§°" /> |
| | | <el-table-column prop="longitude" |
| | | label="ç»åº¦"> |
| | | <template #default="scope"> |
| | | {{ scope.row.longitude ?? "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | label="ç»åº¦" /> |
| | | <el-table-column prop="latitude" |
| | | label="纬度"> |
| | | <template #default="scope"> |
| | | {{ scope.row.latitude ?? "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | label="纬度" /> |
| | | <el-table-column prop="radius" |
| | | label="æå¡èå´(m)"> |
| | | <template #default="scope"> |
| | | {{ scope.row.radius ?? "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | label="æå¡èå´(m)" /> |
| | | <el-table-column prop="startAt" |
| | | label="ä¸çæ¶é´"> |
| | | <template #default="scope"> |
| | | {{ scope.row.startAt || "-" }} |
| | | {{ scope.row.startAt }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="endAt" |
| | | label="ä¸çæ¶é´"> |
| | | <template #default="scope"> |
| | | {{ scope.row.endAt || "-" }} |
| | | {{ scope.row.endAt }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | |
| | | <template #default="scope"> |
| | | <el-button type="primary" |
| | | link |
| | | :disabled="isRuleLocked(scope.row)" |
| | | @click="openForm('edit', scope.row)">ç¼è¾</el-button> |
| | | <el-button type="danger" |
| | | link |
| | | :disabled="isRuleLocked(scope.row)" |
| | | @click="handleDelete(scope.row.id)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | Plus, |
| | |
| | | |
| | | // æ¥è¯¢è¡¨å |
| | | const searchForm = reactive({ |
| | | sysDeptId: "", |
| | | shift: "", |
| | | countId: "", |
| | | locationName: "", |
| | | }); |
| | | |
| | | // é¨é¨é项 |
| | |
| | | if (!value) return ""; |
| | | const shift = shifts_list.value.find(item => item.value === value); |
| | | return shift ? shift.label : value; |
| | | }; |
| | | |
| | | const isRuleLocked = row => { |
| | | return Boolean(row?.usageLocked || row?.canEdit === false || row?.canDelete === false); |
| | | }; |
| | | |
| | | // è·åé¨é¨å表 |
| | |
| | | tableLoading.value = true; |
| | | getAttendanceRules({ ...page, ...searchForm }) |
| | | .then(res => { |
| | | const records = Array.isArray(res?.data?.records) ? res.data.records : []; |
| | | tableData.value = records.filter(r => String(r?.id) !== "999"); |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | page.current = 1; |
| | | fetchData(); |
| | | }; |
| | | |
| | | // å页忴 |
| | |
| | | |
| | | // éç½®æç´¢ |
| | | const resetSearch = () => { |
| | | searchForm.sysDeptId = ""; |
| | | searchForm.shift = ""; |
| | | page.current = 1; |
| | | searchForm.countId = ""; |
| | | searchForm.locationName = ""; |
| | | fetchData(); |
| | | }; |
| | | |
| | | // æå¼è¡¨å |
| | | const openForm = (type, row = {}) => { |
| | | if (type === "edit" && isRuleLocked(row)) { |
| | | ElMessage.warning("è¯¥çæ¬¡é
置已被æç并产çæå¡è®°å½ï¼ä¸è½ç¼è¾"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | currentRow.value = row; |
| | | dialogVisible.value = true; |
| | |
| | | |
| | | // å é¤ç次 |
| | | const handleDelete = id => { |
| | | const row = tableData.value.find(item => String(item.id) === String(id)); |
| | | if (isRuleLocked(row)) { |
| | | ElMessage.warning("åå¨å·²æçä¸å·²ææå¡è®°å½ççæ¬¡é
ç½®ï¼ä¸è½å é¤"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("ç¡®å®è¦å é¤è¿æ¡ç次åï¼", "å é¤ç¡®è®¤", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | |
| | | :key="'d' + i"> |
| | | <el-dropdown trigger="click" |
| | | placement="bottom" |
| | | :disabled="isCellBlockedByLeaveDate(item, m)" |
| | | @command="(e) => handleCommand(e, item, m)" |
| | | @command="(e) => handleCommand(e, m)" |
| | | class="shift-dropdown"> |
| | | <div class="shift-box" |
| | | :class="{ |
| | | 'shift-box-early': m.shift === 'æ©ç', |
| | | 'shift-box-mid': m.shift === 'ä¸ç', |
| | | 'shift-box-night': m.shift === 'å¤ç', |
| | | 'shift-box-rest': !m.shift || m.shift === '伿¯', |
| | | 'shift-box-rest': m.shift === '伿¯', |
| | | 'shift-box-leave': m.shift === '请å', |
| | | 'shift-box-other': m.shift === 'å¤11', |
| | | 'shift-box-business': m.shift === 'å¤12', |
| | | }"> |
| | | <span class="shift-text">{{ getShiftNameByValue(m.shift) || '伿¯' }}</span> |
| | | <span class="shift-text">{{ getShiftNameByValue(m.shift) || 'â' }}</span> |
| | | </div> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | |
| | | </div> --> |
| | | <div class="user-total"> |
| | | <span class="total-label">å计åºå¤:</span> |
| | | <span class="total-value">{{ item.year.totalYearAttendance }}天</span> |
| | | <span class="total-value">{{ item.work_time }}天</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <div class="search_thing"> |
| | | <div class="search_label" |
| | | style="width: 90px"> |
| | | <span style="color: red; margin-right: 4px">*</span>é¨é¨ï¼ |
| | | </div> |
| | | <div class="search_input"> |
| | | <el-tree-select v-model="schedulingQuery.deptId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="è¯·éæ©é¨é¨" |
| | | check-strictly |
| | | clearable |
| | | @change="onSchedulingDeptChange" |
| | | style="width: 100%" /> |
| | | </div> |
| | | </div> |
| | | <div class="search_thing"> |
| | | <div class="search_label" |
| | | style="width: 90px"> |
| | | <span style="color: red; margin-right: 4px">*</span>人ååç§°ï¼ |
| | | </div> |
| | | <div class="search_input"> |
| | |
| | | style="width: 100%" |
| | | multiple |
| | | clearable |
| | | collapse-tags |
| | | :disabled="!schedulingQuery.deptId"> |
| | | collapse-tags> |
| | | <el-option v-for="item in personList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | |
| | | <div class="search_input"> |
| | | <el-select v-model="schedulingQuery.shift" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" |
| | | :disabled="!schedulingQuery.deptId"> |
| | | <el-option v-for="item in schedulingClassType" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in classType" |
| | | :key="item.id" |
| | | :label="getShiftNameByValue(item.shift)" |
| | | :value="item.id"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | reactive, |
| | | computed, |
| | | watch, |
| | | onMounted, |
| | | getCurrentInstance, |
| | | } from "vue"; |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import { |
| | | page, |
| | |
| | | update, |
| | | staffOnJobListPage, |
| | | } from "@/api/personnelManagement/class"; |
| | | import { findStaffLeaveListPage } from "@/api/personnelManagement/staffLeave.js"; |
| | | import { deptTreeSelect } from "@/api/system/user.js"; |
| | | import { getAttendanceRules } from "@/api/personnelManagement/attendanceRules.js"; |
| | | import { useDict } from "@/utils/dict"; |
| | |
| | | |
| | | // 人åå表 |
| | | const personList = ref([]); |
| | | const personListAll = ref([]); |
| | | const leaveDateMap = ref({}); |
| | | |
| | | // å è½½ç¶æ |
| | | const loading = ref(false); |
| | |
| | | // æçæ¥è¯¢æ¡ä»¶ |
| | | const schedulingQuery = reactive({ |
| | | week: "", |
| | | deptId: "", |
| | | userId: null, |
| | | shift: "", |
| | | }); |
| | | |
| | | const schedulingClassType = computed(() => { |
| | | if (!schedulingQuery.deptId) return classType.value || []; |
| | | return (classType.value || []).filter(item => { |
| | | if (["998", "999"].includes(String(item?.id))) return true; |
| | | const itemDeptId = item.deptId ?? item.sysDeptId ?? item.dept_id; |
| | | return String(itemDeptId) === String(schedulingQuery.deptId); |
| | | }); |
| | | }); |
| | | |
| | | // åè¡¨æ°æ® |
| | |
| | | proxy.$modal.msgError("è¯·éæ©å¨æ¬¡"); |
| | | return; |
| | | } |
| | | if (!schedulingQuery.deptId) { |
| | | proxy.$modal.msgError("è¯·éæ©é¨é¨"); |
| | | return; |
| | | } |
| | | let time = schedulingQuery.week.getTime(); |
| | | |
| | | // æ ¼å¼åæ¥æä¸º YYYY-MM-DD æ ¼å¼ |
| | |
| | | proxy.$modal.msgError("è¯·éæ©çæ¬¡"); |
| | | return; |
| | | } |
| | | const blockedEmployees = (personList.value || []).filter(item => { |
| | | const leaveDate = getEmployeeLeaveDate(item?.id); |
| | | const weekEndDate = getSchedulingWeekEndDate(); |
| | | return leaveDate && weekEndDate && weekEndDate.getTime() > leaveDate.getTime(); |
| | | }); |
| | | if (blockedEmployees.length) { |
| | | proxy.$modal.msgError("åå¨å·²è¶
è¿ç¦»èæ¥æç人åï¼ä¸è½ç»§ç»æç"); |
| | | return; |
| | | } |
| | | loading.value = true; |
| | | add({ |
| | | startWeek, |
| | |
| | | proxy.$modal.msgSuccess("æä½æå"); |
| | | schedulingVisible.value = false; |
| | | schedulingQuery.week = ""; |
| | | schedulingQuery.deptId = ""; |
| | | schedulingQuery.userId = null; |
| | | schedulingQuery.shift = ""; |
| | | personList.value = []; |
| | | refresh(); |
| | | }) |
| | | .catch(err => { |
| | |
| | | } |
| | | proxy.$download.saveAs(blob, fileName + ".xlsx"); |
| | | }) |
| | | .catch(err => {}) |
| | | .catch(err => { |
| | | }) |
| | | .finally(() => { |
| | | downLoading.value = false; |
| | | }); |
| | | }; |
| | | // å¤çå½ä»¤ |
| | | const handleCommand = (e, row, cell) => { |
| | | if (isCellBlockedByLeaveDate(row, cell)) { |
| | | proxy.$modal.msgError("该å工已è¶
åºç¦»èæ¥æï¼ä¸è½ç»§ç»æç"); |
| | | return; |
| | | } |
| | | const handleCommand = (e, m) => { |
| | | // if (e != m.shift) { |
| | | update({ |
| | | id: cell.id, |
| | | id: m.id, |
| | | personalAttendanceLocationConfigId: e, |
| | | }).then(res => { |
| | | proxy.$modal.msgSuccess("æä½æå"); |
| | |
| | | // æ¥è¯¢è§åå表 |
| | | const fetchData = () => { |
| | | getAttendanceRules({ current: -1, size: -1 }).then(res => { |
| | | const records = Array.isArray(res?.data?.records) ? res.data.records : []; |
| | | const appendList = [ |
| | | { id: 998, shift: "伿¯" }, |
| | | { id: 999, shift: "请å" }, |
| | | ]; |
| | | const exists = new Set(records.map(r => String(r?.id))); |
| | | appendList.forEach(item => { |
| | | if (!exists.has(String(item.id))) { |
| | | records.push(item); |
| | | } |
| | | }); |
| | | classType.value = records; |
| | | classType.value = res.data.records; |
| | | }); |
| | | }; |
| | | // è·åç¨æ· |
| | |
| | | staffState: 1, |
| | | }).then(res => { |
| | | let arr = res.data.records; |
| | | personListAll.value = arr; |
| | | updatePersonListByDept(); |
| | | personList.value = arr; |
| | | }); |
| | | }; |
| | | |
| | | const getLeaveDates = () => { |
| | | findStaffLeaveListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | }).then(res => { |
| | | const records = Array.isArray(res?.data?.records) ? res.data.records : []; |
| | | const map = {}; |
| | | records.forEach(item => { |
| | | const staffId = item?.staffOnJobId ?? item?.staff_on_job_id; |
| | | const leaveDate = item?.leaveDate ?? item?.leave_date; |
| | | if (!staffId || !leaveDate) return; |
| | | const key = String(staffId); |
| | | const prev = map[key]; |
| | | if (!prev || String(leaveDate) > String(prev)) { |
| | | map[key] = leaveDate; |
| | | } |
| | | }); |
| | | leaveDateMap.value = map; |
| | | updatePersonListByDept(); |
| | | syncPersonListSelection(); |
| | | }); |
| | | }; |
| | | |
| | | const findDeptNodeById = (deptId, deptList = deptOptions.value) => { |
| | | const list = Array.isArray(deptList) ? deptList : []; |
| | | for (const node of list) { |
| | | if (String(node.id) === String(deptId)) return node; |
| | | if (node.children && node.children.length) { |
| | | const found = findDeptNodeById(deptId, node.children); |
| | | if (found) return found; |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const collectDeptIds = node => { |
| | | const ids = []; |
| | | if (!node) return ids; |
| | | ids.push(String(node.id)); |
| | | if (node.children && node.children.length) { |
| | | node.children.forEach(child => { |
| | | ids.push(...collectDeptIds(child)); |
| | | }); |
| | | } |
| | | return ids; |
| | | }; |
| | | |
| | | const updatePersonListByDept = () => { |
| | | if (!schedulingQuery.deptId) { |
| | | personList.value = []; |
| | | return; |
| | | } |
| | | const node = findDeptNodeById(schedulingQuery.deptId); |
| | | const deptIds = new Set(collectDeptIds(node)); |
| | | personList.value = (personListAll.value || []).filter(item => { |
| | | const itemDeptId = item.deptId ?? item.sysDeptId ?? item.dept_id; |
| | | return deptIds.has(String(itemDeptId)) && !isEmployeeBlockedByLeaveDate(item); |
| | | }); |
| | | }; |
| | | |
| | | const parseDateOnly = value => { |
| | | if (!value) return null; |
| | | if (value instanceof Date) { |
| | | const d = new Date(value); |
| | | d.setHours(0, 0, 0, 0); |
| | | return d; |
| | | } |
| | | const normalized = String(value).trim().slice(0, 10); |
| | | if (!normalized) return null; |
| | | const d = new Date(normalized.replace(/-/g, "/")); |
| | | if (Number.isNaN(d.getTime())) return null; |
| | | d.setHours(0, 0, 0, 0); |
| | | return d; |
| | | }; |
| | | |
| | | const getSchedulingWeekEndDate = () => { |
| | | if (!schedulingQuery.week) return null; |
| | | const start = new Date(schedulingQuery.week); |
| | | start.setHours(0, 0, 0, 0); |
| | | return new Date(start.getTime() + 24 * 60 * 60 * 1000 * 5); |
| | | }; |
| | | |
| | | const getCellDate = cell => parseDateOnly(cell?.time); |
| | | |
| | | const getEmployeeLeaveDate = staffId => { |
| | | const leaveDate = leaveDateMap.value[String(staffId)]; |
| | | return parseDateOnly(leaveDate); |
| | | }; |
| | | |
| | | const isEmployeeBlockedByLeaveDate = item => { |
| | | const leaveDate = getEmployeeLeaveDate(item?.id); |
| | | if (!leaveDate) return false; |
| | | const weekEndDate = getSchedulingWeekEndDate(); |
| | | if (!weekEndDate) return false; |
| | | return weekEndDate.getTime() > leaveDate.getTime(); |
| | | }; |
| | | |
| | | const isCellBlockedByLeaveDate = (row, cell) => { |
| | | const staffId = row?.userId ?? row?.user_id ?? row?.id; |
| | | const leaveDate = getEmployeeLeaveDate(staffId); |
| | | const cellDate = getCellDate(cell); |
| | | if (!leaveDate || !cellDate) return false; |
| | | return cellDate.getTime() > leaveDate.getTime(); |
| | | }; |
| | | |
| | | const syncPersonListSelection = () => { |
| | | if (!Array.isArray(schedulingQuery.userId) || !schedulingQuery.userId.length) return; |
| | | const validIds = new Set((personList.value || []).map(item => String(item.id))); |
| | | const nextIds = schedulingQuery.userId.filter(id => validIds.has(String(id))); |
| | | if (nextIds.length !== schedulingQuery.userId.length) { |
| | | schedulingQuery.userId = nextIds; |
| | | proxy.$modal.msgWarning("å·²èªå¨åé¤è¶
åºç¦»èæ¥æç人å"); |
| | | } |
| | | }; |
| | | |
| | | const setDefaultShiftByDept = () => { |
| | | if (!schedulingQuery.deptId) { |
| | | schedulingQuery.shift = ""; |
| | | return; |
| | | } |
| | | const first = schedulingClassType.value?.[0]; |
| | | schedulingQuery.shift = first ? first.id : ""; |
| | | }; |
| | | |
| | | const onSchedulingDeptChange = () => { |
| | | schedulingQuery.userId = null; |
| | | updatePersonListByDept(); |
| | | setDefaultShiftByDept(); |
| | | }; |
| | | |
| | | // æ ¹æ®åå
¸è·åæ¥æ |
| | |
| | | onMounted(() => { |
| | | fetchData(); |
| | | getUsers(); |
| | | getLeaveDates(); |
| | | fetchDeptOptions(); |
| | | if (query.month) { |
| | | init(); |
| | |
| | | } |
| | | monthList.value.reverse(); |
| | | }); |
| | | |
| | | watch( |
| | | () => classType.value, |
| | | () => { |
| | | if (schedulingVisible.value && schedulingQuery.deptId) { |
| | | setDefaultShiftByDept(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => schedulingQuery.week, |
| | | () => { |
| | | if (!schedulingVisible.value) return; |
| | | updatePersonListByDept(); |
| | | syncPersonListSelection(); |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => schedulingQuery.deptId, |
| | | () => { |
| | | if (!schedulingVisible.value) return; |
| | | updatePersonListByDept(); |
| | | syncPersonListSelection(); |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => schedulingVisible.value, |
| | | val => { |
| | | if (!val) return; |
| | | schedulingQuery.week = ""; |
| | | schedulingQuery.deptId = ""; |
| | | schedulingQuery.userId = null; |
| | | schedulingQuery.shift = ""; |
| | | personList.value = []; |
| | | } |
| | | ); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | title="详æ
" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | <PIMTable rowKey="id" |
| | | @close="closeDia" |
| | | > |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="600"></PIMTable> |
| | | height="600" |
| | | ></PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <FileList v-if="fileDialogVisible" |
| | | v-model:visible="fileDialogVisible" |
| | | :record-type="'staff_contract'" |
| | | :record-id="recordId" /> |
| | | <Files ref="filesDia"></Files> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, defineAsyncComponent, getCurrentInstance } from "vue"; |
| | | import {ref} from "vue"; |
| | | import { findStaffContractListPage } from "@/api/personnelManagement/staffContract.js"; |
| | | const FileList = defineAsyncComponent(() => |
| | | import("@/components/Dialog/FileList.vue") |
| | | ); |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | const fileDialogVisible = ref(false); |
| | | const recordId = ref(0); |
| | | const Files = defineAsyncComponent(() => import( "@/views/personnelManagement/contractManagement/filesDia.vue")); |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | const filesDia = ref() |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const operationType = ref('') |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ååå¹´é", |
| | |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | fixed: 'right', |
| | | width: 120, |
| | | operation: [ |
| | | { |
| | | name: "éä»¶", |
| | | name: "ä¸ä¼ éä»¶", |
| | | type: "text", |
| | | clickFun: row => { |
| | | recordId.value = row.id; |
| | | fileDialogVisible.value = true; |
| | | clickFun: (row) => { |
| | | filesDia.value.openDialog( row,'åå') |
| | | }, |
| | | }, |
| | | } |
| | | ], |
| | | }, |
| | | ]); |
| | |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === "edit") { |
| | | if (operationType.value === 'edit') { |
| | | findStaffContractListPage({ staffOnJobId: row.id }).then(res => { |
| | | tableData.value = res.data.records; |
| | | }); |
| | | tableData.value = res.data.records |
| | | }) |
| | | } |
| | | }; |
| | | } |
| | | |
| | | const openUploadFile = (row) => { |
| | | filesDia.value.open = true |
| | | filesDia.value.row = row |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | dialogFormVisible.value = false; |
| | | emit("close"); |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">å§åï¼</span> |
| | | <el-input v-model="searchForm.staffName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å§åæç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">ååç»ææ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | <el-input v-model="searchForm.staffName" style="width: 240px" placeholder="请è¾å
¥å§åæç´¢" @change="handleQuery" |
| | | clearable :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" class="search_title">ååç»ææ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <!-- <el-button type="primary" @click="openForm('add')">æ°å¢å
¥è</el-button>--> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true" |
| | | @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination" |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | <renew-contract v-if="isShowRenewContractModal" |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | <renew-contract |
| | | v-if="isShowRenewContractModal" |
| | | v-model:visible="isShowRenewContractModal" |
| | | :id="id" |
| | | @completed="handleQuery" /> |
| | | @completed="handleQuery" |
| | | /> |
| | | |
| | | <!-- åå导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog :title="upload.title" |
| | | <el-dialog |
| | | :title="upload.title" |
| | | v-model="upload.open" |
| | | width="400px" |
| | | append-to-body> |
| | | <el-upload ref="uploadRef" |
| | | append-to-body |
| | | > |
| | | <el-upload |
| | | ref="uploadRef" |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | |
| | | :on-progress="handleFileUploadProgress" |
| | | :on-success="handleFileSuccess" |
| | | :auto-upload="false" |
| | | drag> |
| | | drag |
| | | > |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">å°æä»¶æå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em></div> |
| | | <template #tip> |
| | |
| | | </el-upload> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitFileForm">ç¡® å®</el-button> |
| | | <el-button type="primary" @click="submitFileForm">ç¡® å®</el-button> |
| | | <el-button @click="upload.open = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, defineAsyncComponent } from "vue"; |
| | | import FormDia from "@/views/personnelManagement/contractManagement/components/formDia.vue"; |
| | | const RenewContract = defineAsyncComponent(() => |
| | | import( |
| | | "@/views/personnelManagement/employeeRecord/components/RenewContract.vue" |
| | | ) |
| | | ); |
| | | const RenewContract = defineAsyncComponent(() => import("@/views/personnelManagement/employeeRecord/components/RenewContract.vue")); |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js"; |
| | | import dayjs from "dayjs"; |
| | |
| | | label: "ç¶æ", |
| | | prop: "staffState", |
| | | dataType: "tag", |
| | | formatData: params => { |
| | | formatData: (params) => { |
| | | if (params == 0) { |
| | | return "离è"; |
| | | } else if (params == 1) { |
| | |
| | | return null; |
| | | } |
| | | }, |
| | | formatType: params => { |
| | | formatType: (params) => { |
| | | if (params == 0) { |
| | | return "danger"; |
| | | } else if (params == 1) { |
| | |
| | | prop: "sex", |
| | | }, |
| | | { |
| | | label: "æ·ç±ä½å", |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "å²ä½", |
| | | prop: "postName", |
| | | prop: "postJob", |
| | | }, |
| | | { |
| | | label: "ç°ä½å", |
| | | prop: "adress", |
| | | width: 200 |
| | | }, |
| | | { |
| | | label: "第ä¸å¦å", |
| | |
| | | { |
| | | label: "ä¸ä¸", |
| | | prop: "profession", |
| | | width: 100, |
| | | width: 100 |
| | | }, |
| | | { |
| | | label: "å¹´é¾", |
| | |
| | | { |
| | | label: "èç³»çµè¯", |
| | | prop: "phone", |
| | | width: 150, |
| | | width: 150 |
| | | }, |
| | | { |
| | | label: "ç´§æ¥è系人", |
| | | prop: "emergencyContact", |
| | | width: 120, |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "ç´§æ¥è系人çµè¯", |
| | | prop: "emergencyContactPhone", |
| | | width: 150, |
| | | width: 150 |
| | | }, |
| | | // { |
| | | // label: "ååå¹´é", |
| | |
| | | { |
| | | label: "ååç»ææ¥æ", |
| | | prop: "contractExpireTime", |
| | | width: 120, |
| | | width: 120 |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | fixed: 'right', |
| | | width: 160, |
| | | operation: [ |
| | | { |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | |
| | | name: "ç»ç¾åå", |
| | | type: "text", |
| | | showHide: row => row.staffState === 1, |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | isShowRenewContractModal.value = true; |
| | | id.value = row.id; |
| | | }, |
| | | }, |
| | | } |
| | | ], |
| | | }, |
| | | ]); |
| | | const filesDia = ref(); |
| | | const filesDia = ref() |
| | | const isShowRenewContractModal = ref(false); |
| | | const id = ref(0); |
| | | const tableData = ref([]); |
| | |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | const changeDaterange = value => { |
| | | const changeDaterange = (value) => { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | if (value) { |
| | |
| | | getList(); |
| | | }; |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | const openFilesFormDia = row => { |
| | | console.log(row); |
| | | const openFilesFormDia = (row) => { |
| | | console.log(row) |
| | | nextTick(() => { |
| | | filesDia.value?.openDialog(row, "åå"); |
| | | }); |
| | | filesDia.value?.openDialog( row,'åå') |
| | | }) |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = obj => { |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | params.staffState = 1; |
| | | staffOnJobListPage(params) |
| | | .then(res => { |
| | | params.entryDate = undefined |
| | | params.staffState = 1 |
| | | staffOnJobListPage(params).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download( |
| | | "/staff/staffOnJob/export", |
| | | { staffState: 1 }, |
| | | "åå管ç.xlsx" |
| | | ); |
| | | proxy.download("/staff/staffOnJob/export", {staffState: 1}, "åå管ç.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | } |
| | | /** æäº¤ä¸ä¼ æä»¶ */ |
| | | function submitFileForm() { |
| | | console.log(upload.url + "?updateSupport=" + upload.updateSupport); |
| | | console.log(upload.url + '?updateSupport=' + upload.updateSupport) |
| | | proxy.$refs["uploadRef"].submit(); |
| | | } |
| | | /**æä»¶ä¸ä¼ ä¸å¤ç */ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢ç¦»è' : 'ç¼è¾ç¦»è'" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | @close="closeDia" |
| | | > |
| | | <!-- å工信æ¯å±ç¤ºåºå --> |
| | | <div class="info-section"> |
| | | <div class="info-title">å工信æ¯</div> |
| | | <el-form :model="form" |
| | | label-width="200px" |
| | | label-position="left" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | style="margin-top: 20px"> |
| | | <el-form :model="form" label-width="200px" label-position="left" :rules="rules" ref="formRef" style="margin-top: 20px"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å§åï¼" |
| | | prop="staffOnJobId"> |
| | | <el-form-item label="å§åï¼" prop="staffOnJobId"> |
| | | <el-select v-model="form.staffOnJobId" |
| | | placeholder="è¯·éæ©äººå" |
| | | style="width: 100%" |
| | | :disabled="operationType === 'edit'" |
| | | @change="handleSelect"> |
| | | <el-option v-for="item in personList" |
| | | <el-option |
| | | v-for="item in personList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | | :value="item.id" /> |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | {{ currentStaffRecord.sex || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ·ç±ä½åï¼"> |
| | | {{ currentStaffRecord.nativePlace || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å²ä½ï¼"> |
| | | {{ currentStaffRecord.postName || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç°ä½åï¼"> |
| | | {{ currentStaffRecord.adress || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¦»èæ¥æï¼" |
| | | prop="leaveDate"> |
| | | <el-date-picker v-model="form.leaveDate" |
| | | <el-form-item label="ç¦»èæ¥æï¼" prop="leaveDate"> |
| | | <el-date-picker |
| | | v-model="form.leaveDate" |
| | | type="date" |
| | | :disabled="operationType === 'edit'" |
| | | :disabled-date="disabledFutureDate" |
| | | placeholder="è¯·éæ©ç¦»èæ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" /> |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="离èåå ï¼" |
| | | prop="reason"> |
| | | <el-select v-model="form.reason" |
| | | placeholder="è¯·éæ©ç¦»èåå " |
| | | style="width: 100%" |
| | | @change="handleSelectDimissionReason"> |
| | | <el-option v-for="(item, index) in dimissionReasonOptions" |
| | | <el-form-item label="离èåå ï¼" prop="reason"> |
| | | <el-select v-model="form.reason" placeholder="è¯·éæ©ç¦»èåå " style="width: 100%" @change="handleSelectDimissionReason"> |
| | | <el-option |
| | | v-for="(item, index) in dimissionReasonOptions" |
| | | :key="index" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="夿³¨ï¼" |
| | | prop="remark" |
| | | v-if="form.reason === 'other'"> |
| | | <el-input v-model="form.remark" |
| | | <el-form-item label="夿³¨ï¼" prop="remark" v-if="form.reason === 'other'"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="夿³¨" |
| | | maxlength="500" |
| | | show-word-limit /> |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | |
| | | <!-- <el-row :gutter="30">--> |
| | | <!-- <el-col :span="12">--> |
| | | <!-- <div class="info-item">--> |
| | |
| | | <!-- </el-col>--> |
| | | <!-- </el-row>--> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { |
| | | createStaffLeave, |
| | | updateStaffLeave, |
| | | } from "@/api/personnelManagement/staffLeave.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | import {createStaffLeave, updateStaffLeave} from "@/api/personnelManagement/staffLeave.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const operationType = ref('') |
| | | const getTodayDate = () => { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = `${now.getMonth() + 1}`.padStart(2, "0"); |
| | | const day = `${now.getDate()}`.padStart(2, "0"); |
| | | const month = `${now.getMonth() + 1}`.padStart(2, '0'); |
| | | const day = `${now.getDate()}`.padStart(2, '0'); |
| | | return `${year}-${month}-${day}`; |
| | | }; |
| | | |
| | | const disabledFutureDate = time => { |
| | | const disabledFutureDate = (time) => { |
| | | const todayEnd = new Date(); |
| | | todayEnd.setHours(23, 59, 59, 999); |
| | | return time.getTime() > todayEnd.getTime(); |
| | |
| | | }, |
| | | rules: { |
| | | staffName: [{ required: true, message: "è¯·éæ©äººå" }], |
| | | leaveDate: [ |
| | | { required: true, message: "è¯·éæ©ç¦»èæ¥æ", trigger: "change" }, |
| | | ], |
| | | leaveDate: [{ required: true, message: "è¯·éæ©ç¦»èæ¥æ", trigger: "change" }], |
| | | reason: [{ required: true, message: "è¯·éæ©ç¦»èåå " }], |
| | | }, |
| | | dimissionReasonOptions: [ |
| | | { label: "èªèµå¾
é", value: "salary" }, |
| | | { label: "èä¸åå±", value: "career_development" }, |
| | | { label: "å·¥ä½ç¯å¢", value: "work_environment" }, |
| | | { label: "个人åå ", value: "personal_reason" }, |
| | | { label: "å
¶ä»", value: "other" }, |
| | | {label: 'èªèµå¾
é', value: 'salary'}, |
| | | {label: 'èä¸åå±', value: 'career_development'}, |
| | | {label: 'å·¥ä½ç¯å¢', value: 'work_environment'}, |
| | | {label: '个人åå ', value: 'personal_reason'}, |
| | | {label: 'å
¶ä»', value: 'other'}, |
| | | ], |
| | | currentStaffRecord: {}, |
| | | }); |
| | | const { form, rules, dimissionReasonOptions, currentStaffRecord } = |
| | | toRefs(data); |
| | | const { form, rules, dimissionReasonOptions, currentStaffRecord } = toRefs(data); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === "edit") { |
| | | currentStaffRecord.value = row; |
| | | form.value.staffOnJobId = row.staffOnJobId; |
| | | form.value.leaveDate = row.leaveDate; |
| | | form.value.reason = row.reason; |
| | | form.value.remark = row.remark; |
| | | if (operationType.value === 'edit') { |
| | | currentStaffRecord.value = row |
| | | form.value.staffOnJobId = row.staffOnJobId |
| | | form.value.leaveDate = row.leaveDate |
| | | form.value.reason = row.reason |
| | | form.value.remark = row.remark |
| | | personList.value = [ |
| | | { |
| | | staffName: row.staffName, |
| | | id: row.staffOnJobId, |
| | | }, |
| | | ]; |
| | | } |
| | | ] |
| | | } else { |
| | | form.value.leaveDate = getTodayDate(); |
| | | getList(); |
| | | form.value.leaveDate = getTodayDate() |
| | | getList() |
| | | } |
| | | }; |
| | | } |
| | | |
| | | const handleSelectDimissionReason = val => { |
| | | if (val === "other") { |
| | | form.value.remark = ""; |
| | | const handleSelectDimissionReason = (val) => { |
| | | if (val === 'other') { |
| | | form.value.remark = '' |
| | | } |
| | | }; |
| | | } |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | form.value.staffState = 0; |
| | | if (form.value.reason !== "other") { |
| | | form.value.remark = ""; |
| | | form.value.staffState = 0 |
| | | if (form.value.reason !== 'other') { |
| | | form.value.remark = '' |
| | | } |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | |
| | | createStaffLeave(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | }) |
| | | } else { |
| | | updateStaffLeave(currentStaffRecord.value.id, form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | }) |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | }) |
| | | |
| | | } |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | // 表å已注éï¼æå¨éç½®è¡¨åæ°æ® |
| | |
| | | remark: "", |
| | | }; |
| | | dialogFormVisible.value = false; |
| | | emit("close"); |
| | | emit('close') |
| | | }; |
| | | |
| | | const personList = ref([]); |
| | |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | personList.value = res.data.records || []; |
| | | }); |
| | | personList.value = res.data.records || [] |
| | | }) |
| | | }; |
| | | |
| | | const handleSelect = val => { |
| | | let obj = personList.value.find(item => item.id === val); |
| | | currentStaffRecord.value = {}; |
| | | const handleSelect = (val) => { |
| | | let obj = personList.value.find(item => item.id === val) |
| | | currentStaffRecord.value = {} |
| | | if (obj) { |
| | | // ä¿çç¦»èæ¥æå离èåå ï¼åªæ´æ°åå·¥ä¿¡æ¯ |
| | | currentStaffRecord.value = obj; |
| | | currentStaffRecord.value = obj |
| | | } |
| | | }; |
| | | } |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">å§åï¼</span> |
| | | <el-input v-model="searchForm.staffName" |
| | | <el-input |
| | | v-model="searchForm.staffName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å§åæç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢ç¦»è</el-button> |
| | | <el-button type="primary" @click="openForm('add')">æ°å¢ç¦»è</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <!-- <el-button type="danger" plain @click="handleDelete">å é¤</el-button> --> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"></PIMTable> |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref } from "vue"; |
| | | import FormDia from "@/views/personnelManagement/dimission/components/formDia.vue"; |
| | | import { |
| | | findStaffLeaveListPage, |
| | | batchDeleteStaffLeaves, |
| | | } from "@/api/personnelManagement/staffLeave.js"; |
| | | import {findStaffLeaveListPage, batchDeleteStaffLeaves} from "@/api/personnelManagement/staffLeave.js"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | |
| | | const data = reactive({ |
| | |
| | | label: "ç¶æ", |
| | | prop: "staffState", |
| | | dataType: "tag", |
| | | formatData: params => { |
| | | formatData: (params) => { |
| | | if (params == 0) { |
| | | return "离è"; |
| | | } else if (params == 1) { |
| | |
| | | return null; |
| | | } |
| | | }, |
| | | formatType: params => { |
| | | formatType: (params) => { |
| | | if (params == 0) { |
| | | return "danger"; |
| | | } else if (params == 1) { |
| | |
| | | prop: "sex", |
| | | }, |
| | | { |
| | | label: "æ·ç±ä½å", |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "é¨é¨", |
| | | prop: "deptName", |
| | | }, |
| | |
| | | prop: "postName", |
| | | }, |
| | | { |
| | | label: "ç°ä½å", |
| | | prop: "adress", |
| | | width:200 |
| | | }, |
| | | { |
| | | label: "第ä¸å¦å", |
| | | prop: "firstStudy", |
| | | }, |
| | | { |
| | | label: "ä¸ä¸", |
| | | prop: "profession", |
| | | width: 100, |
| | | width:100 |
| | | }, |
| | | { |
| | | label: "å¹´é¾", |
| | |
| | | { |
| | | label: "èç³»çµè¯", |
| | | prop: "phone", |
| | | width: 150, |
| | | width:150 |
| | | }, |
| | | { |
| | | label: "ç´§æ¥è系人", |
| | | prop: "emergencyContact", |
| | | width: 120, |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "ç´§æ¥è系人çµè¯", |
| | | prop: "emergencyContactPhone", |
| | | width: 150, |
| | | width:150 |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | fixed: 'right', |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = obj => { |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | findStaffLeaveListPage({ ...page, ...searchForm.value }) |
| | | .then(res => { |
| | | findStaffLeaveListPage({...page, ...searchForm.value}).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | batchDeleteStaffLeaves(ids).then(res => { |
| | | batchDeleteStaffLeaves(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | |
| | | prop: "staffNo", |
| | | }, |
| | | { |
| | | label: "é¨é¨", |
| | | prop: "deptName", |
| | | }, |
| | | { |
| | | label: "å§å", |
| | | prop: "staffName", |
| | | }, |
| | |
| | | <script setup> |
| | | import { ref, computed, reactive, toRefs, getCurrentInstance, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import Cookies from "js-cookie"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { staffSalaryMainUpdate } from "@/api/personnelManagement/staffSalaryMain.js"; |
| | | |
| | |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | |
| | | return; |
| | | } |
| | | |
| | | const username = userStore.name || ""; |
| | | const currentUserId = Number(userStore.id); |
| | | const assignedAuditUserId = Number(row?.auditUserId); |
| | | |
| | | // æééªè¯ï¼åªææå®çå®¡æ ¸äººæè½è¿è¡å®¡æ ¸ |
| | | if (!currentUserId || currentUserId !== assignedAuditUserId) { |
| | | ElMessage.warning("æ¨ä¸æ¯æå®çå®¡æ ¸äººï¼æ æ³è¿è¡å®¡æ ¸æä½"); |
| | | return; |
| | | } |
| | | const username = Cookies.get("username") || ""; |
| | | const userIdRaw = Cookies.get("userId"); |
| | | const auditUserId = userIdRaw ? Number(userIdRaw) : undefined; |
| | | |
| | | // æå»ºå®¡æ ¸æ°æ® |
| | | const submitData = { |
| | | id: row.id, |
| | | status: Number(auditResult.value) === 2 ? 2 : 4, // 2=ä¸éè¿ 4=éè¿(å¾
åæ¾) |
| | | auditUserId: currentUserId, |
| | | auditUserId, |
| | | auditUserName: username, |
| | | }; |
| | | loading.value = true; |
| | |
| | | <template> |
| | | <FormDialog v-model="dialogVisible" |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | :title="operationType === 'add' ? 'æ°å»ºå·¥èµè¡¨' : 'ç¼è¾å·¥èµè¡¨'" |
| | | width="90%" |
| | | @close="closeDia"> |
| | | @close="closeDia" |
| | | > |
| | | <template #footer> |
| | | <el-button type="info" |
| | | @click="saveDraft">ä¿åè稿</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认æäº¤</el-button> |
| | | <el-button type="info" @click="saveDraft">ä¿åè稿</el-button> |
| | | <el-button type="primary" @click="submitForm">确认æäº¤</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </template> |
| | | <div class="form-dia-body"> |
| | | <!-- åºç¡èµæ --> |
| | | <el-card class="form-card" |
| | | shadow="never"> |
| | | <el-card class="form-card" shadow="never"> |
| | | <template #header> |
| | | <span class="card-title"><span class="card-title-line">|</span> åºç¡èµæ</span> |
| | | <el-icon class="card-collapse"> |
| | | <ArrowUp /> |
| | | </el-icon> |
| | | <el-icon class="card-collapse"><ArrowUp /></el-icon> |
| | | </template> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-position="top"> |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-position="top"> |
| | | <el-row :gutter="24"> |
| | | <el-col :span="6"> |
| | | <el-form-item label="å·¥èµä¸»é¢" |
| | | prop="salaryTitle"> |
| | | <el-input v-model="form.salaryTitle" |
| | | <el-form-item label="å·¥èµä¸»é¢" prop="salaryTitle"> |
| | | <el-input |
| | | v-model="form.salaryTitle" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | maxlength="20" |
| | | show-word-limit /> |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="éæ©å·¥èµæä»½" |
| | | prop="salaryMonth"> |
| | | <el-date-picker v-model="form.salaryMonth" |
| | | <el-form-item label="éæ©é¨é¨" prop="deptIds"> |
| | | <el-select |
| | | v-model="form.deptIds" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | multiple |
| | | collapse-tags-tooltip |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in deptOptions" |
| | | :key="item.deptId" |
| | | :label="item.deptName" |
| | | :value="item.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="éæ©å·¥èµæä»½" prop="salaryMonth"> |
| | | <el-date-picker |
| | | v-model="form.salaryMonth" |
| | | type="month" |
| | | value-format="YYYY-MM" |
| | | format="YYYY-MM" |
| | | placeholder="è¯·éæ©å·¥èµæä»½" |
| | | style="width: 100%" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="éæ©é¨é¨" |
| | | prop="deptIds"> |
| | | <el-select v-model="form.deptIds" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | multiple |
| | | collapse-tags-tooltip |
| | | style="width: 100%"> |
| | | <el-option v-for="item in deptOptions" |
| | | :key="item.deptId" |
| | | :label="item.deptName" |
| | | :value="item.deptId" |
| | | :disabled="isDeptDisabled(item.deptId)" /> |
| | | </el-select> |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="24"> |
| | | <el-col :span="6"> |
| | | <el-form-item label="æ¯ä»é¶è¡" |
| | | prop="payBank"> |
| | | <el-select v-model="form.payBank" |
| | | <el-form-item label="æ¯ä»é¶è¡" prop="payBank"> |
| | | <el-select |
| | | v-model="form.payBank" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable |
| | | style="width: 100%"> |
| | | <el-option v-for="b in bankOptions" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="b in bankOptions" |
| | | :key="b" |
| | | :label="b" |
| | | :value="b" /> |
| | | :value="b" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="å®¡æ ¸äºº" |
| | | prop="auditUserId"> |
| | | <el-select v-model="form.auditUserId" |
| | | <el-form-item label="å®¡æ ¸äºº" prop="auditUserId"> |
| | | <el-select |
| | | v-model="form.auditUserId" |
| | | placeholder="è¯·éæ©å®¡æ ¸äºº" |
| | | clearable |
| | | filterable |
| | | style="width: 100%"> |
| | | <el-option v-for="item in userList" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | :value="item.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- æä½æé® --> |
| | | <div class="toolbar"> |
| | | <el-button type="primary" |
| | | @click="handleGenerate">çæå·¥èµè¡¨</el-button> |
| | | <el-button type="primary" @click="handleGenerate">çæå·¥èµè¡¨</el-button> |
| | | <el-button @click="handleClear">æ¸
空</el-button> |
| | | <el-button @click="handleBatchDelete">å é¤</el-button> |
| | | <el-button @click="handleTaxForm">个ç¨è¡¨</el-button> |
| | | </div> |
| | | |
| | | <!-- å工工èµè¯¦æ
è¡¨æ ¼ --> |
| | | <div class="employee-table-wrap"> |
| | | <el-table ref="employeeTableRef" |
| | | <el-table |
| | | ref="employeeTableRef" |
| | | :data="employeeList" |
| | | border |
| | | max-height="400" |
| | | @selection-change="onEmployeeSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="55" |
| | | align="center" /> |
| | | <el-table-column label="åå·¥å§å" |
| | | prop="staffName" |
| | | minWidth="100" /> |
| | | <el-table-column label="é¨é¨" |
| | | prop="deptName" |
| | | minWidth="100" /> |
| | | <el-table-column label="åºæ¬å·¥èµ" |
| | | minWidth="110"> |
| | | @selection-change="onEmployeeSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="åå·¥å§å" prop="staffName" minWidth="100" /> |
| | | <el-table-column label="é¨é¨" prop="deptName" minWidth="100" /> |
| | | <el-table-column label="åºæ¬å·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.basicSalary" |
| | | <el-input |
| | | v-model.number="row.basicSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="handleSalaryInput(row)" /> |
| | | @input="row.basicSalary = parseNum(row.basicSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计件工èµ" |
| | | minWidth="110"> |
| | | <el-table-column label="计件工èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.pieceSalary" |
| | | <el-input |
| | | v-model.number="row.pieceSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.pieceSalary = parseNum(row.pieceSalary)" /> |
| | | @input="row.pieceSalary = parseNum(row.pieceSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计æ¶å·¥èµ" |
| | | minWidth="110"> |
| | | <el-table-column label="计æ¶å·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.hourlySalary" |
| | | <el-input |
| | | v-model.number="row.hourlySalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.hourlySalary = parseNum(row.hourlySalary)" /> |
| | | @input="row.hourlySalary = parseNum(row.hourlySalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¶ä»æ¶å
¥" |
| | | minWidth="110"> |
| | | <el-table-column label="å
¶ä»æ¶å
¥" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.otherIncome" |
| | | <el-input |
| | | v-model.number="row.otherIncome" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="handleSalaryInput(row)" /> |
| | | @input="row.otherIncome = parseNum(row.otherIncome)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="社ä¿ä¸ªäºº" |
| | | minWidth="110"> |
| | | <el-table-column label="社ä¿ä¸ªäºº" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.socialPersonal" |
| | | <el-input |
| | | v-model.number="row.socialPersonal" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.socialPersonal = parseNum(row.socialPersonal)" /> |
| | | @input="row.socialPersonal = parseNum(row.socialPersonal)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¬ç§¯é个人" |
| | | minWidth="120"> |
| | | <el-table-column label="å
¬ç§¯é个人" minWidth="120"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.fundPersonal" |
| | | <el-input |
| | | v-model.number="row.fundPersonal" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.fundPersonal = parseNum(row.fundPersonal)" /> |
| | | @input="row.fundPersonal = parseNum(row.fundPersonal)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¶ä»æ¯åº" |
| | | minWidth="110"> |
| | | <el-table-column label="å
¶ä»æ¯åº" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.otherDeduct" |
| | | <el-input |
| | | v-model.number="row.otherDeduct" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.otherDeduct = parseNum(row.otherDeduct)" /> |
| | | @input="row.otherDeduct = parseNum(row.otherDeduct)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="社ä¿è¡¥ç¼´" |
| | | minWidth="120"> |
| | | <el-table-column label="å·¥èµä¸ªç¨" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.socialSupplementAmount" |
| | | <el-input |
| | | v-model.number="row.salaryTax" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="handleSalaryInput(row)" /> |
| | | @input="row.salaryTax = parseNum(row.salaryTax)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·¥èµä¸ªç¨" |
| | | minWidth="110"> |
| | | <el-table-column label="åºåå·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.salaryTax" |
| | | <el-input |
| | | v-model.number="row.grossSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.salaryTax = parseNum(row.salaryTax)" /> |
| | | @input="row.grossSalary = parseNum(row.grossSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºåå·¥èµ" |
| | | minWidth="110"> |
| | | <el-table-column label="åºæ£å·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.grossSalary" |
| | | <el-input |
| | | v-model.number="row.deductSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.grossSalary = parseNum(row.grossSalary)" /> |
| | | @input="row.deductSalary = parseNum(row.deductSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºæ£å·¥èµ" |
| | | minWidth="110"> |
| | | <el-table-column label="å®åå·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.deductSalary" |
| | | <el-input |
| | | v-model.number="row.netSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.deductSalary = parseNum(row.deductSalary)" /> |
| | | @input="row.netSalary = parseNum(row.netSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å®åå·¥èµ" |
| | | minWidth="110"> |
| | | <el-table-column label="夿³¨" minWidth="120"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.netSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.netSalary = parseNum(row.netSalary)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" |
| | | minWidth="120"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model="row.remark" |
| | | <el-input |
| | | v-model="row.remark" |
| | | placeholder="请è¾å
¥" |
| | | size="small" /> |
| | | size="small" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | width="80" |
| | | align="center" |
| | | fixed="right"> |
| | | <el-table-column label="æä½" width="80" align="center" fixed="right"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="removeEmployee(row)">å é¤</el-button> |
| | | <el-button type="primary" link @click="removeEmployee(row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div v-if="!employeeList.length" |
| | | class="table-empty">ææ æ°æ®</div> |
| | | <div v-else |
| | | class="salary-total"> |
| | | <div v-if="!employeeList.length" class="table-empty">ææ æ°æ®</div> |
| | | <div v-else class="salary-total"> |
| | | <span class="total-label">å·¥èµæ»é¢ï¼</span> |
| | | <span class="total-value">Â¥ {{ totalSalary.toFixed(2) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | |
| | | |
| | | <!-- æ°å¢äººåå¼¹çª --> |
| | | <el-dialog v-model="addPersonVisible" |
| | | <el-dialog |
| | | v-model="addPersonVisible" |
| | | title="æ°å¢äººå" |
| | | width="400px" |
| | | append-to-body |
| | | @close="addPersonClose"> |
| | | @close="addPersonClose" |
| | | > |
| | | <div class="add-person-tree"> |
| | | <el-tree ref="personTreeRef" |
| | | <el-tree |
| | | ref="personTreeRef" |
| | | :data="deptStaffTree" |
| | | show-checkbox |
| | | node-key="id" |
| | | :props="{ label: 'label', children: 'children' }" |
| | | default-expand-all /> |
| | | default-expand-all |
| | | /> |
| | | </div> |
| | | <template #footer> |
| | | <el-button @click="addPersonVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" |
| | | @click="confirmAddPerson">ç¡®å®</el-button> |
| | | <el-button type="primary" @click="confirmAddPerson">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 个ç¨è¡¨å¼¹çª --> |
| | | <el-dialog v-model="taxDialogVisible" |
| | | <el-dialog |
| | | v-model="taxDialogVisible" |
| | | title="个ç¨è¡¨" |
| | | width="700px" |
| | | append-to-body> |
| | | append-to-body |
| | | > |
| | | <div class="tax-desc">个人æå¾ç¨å
å¾é¢ï¼5000å
</div> |
| | | <el-table :data="taxTableData" |
| | | border |
| | | style="width: 100%;margin-bottom: 20px;"> |
| | | <el-table-column prop="level" |
| | | label="级æ°" |
| | | width="80" |
| | | align="center" /> |
| | | <el-table-column prop="range" |
| | | <el-table :data="taxTableData" border style="width: 100%;margin-bottom: 20px;"> |
| | | <el-table-column prop="level" label="级æ°" width="80" align="center" /> |
| | | <el-table-column |
| | | prop="range" |
| | | label="å
¨å¹´åºçº³ç¨æå¾é¢/å
" |
| | | min-width="220" /> |
| | | <el-table-column prop="rate" |
| | | min-width="220" |
| | | /> |
| | | <el-table-column |
| | | prop="rate" |
| | | label="ç¨ç(%)" |
| | | width="100" |
| | | align="center" /> |
| | | <el-table-column prop="quickDeduction" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | prop="quickDeduction" |
| | | label="éç®æ£é¤æ°/å
" |
| | | width="160" |
| | | align="center" /> |
| | | align="center" |
| | | /> |
| | | </el-table> |
| | | </el-dialog> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | computed, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | watch, |
| | | } from "vue"; |
| | | import { ref, reactive, toRefs, computed, getCurrentInstance, nextTick } from "vue"; |
| | | import { ArrowUp } from "@element-plus/icons-vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { listDept } from "@/api/system/dept.js"; |
| | |
| | | staffSalaryMainAdd, |
| | | staffSalaryMainUpdate, |
| | | staffSalaryMainCalculateSalary, |
| | | staffSalaryMainCalculateByEmployeeId, |
| | | staffSalaryMainAvailableDepts, |
| | | } from "@/api/personnelManagement/staffSalaryMain.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | |
| | | const emit = defineEmits(["update:modelValue", "close"]); |
| | | const props = defineProps({ |
| | |
| | | |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | | set: val => emit("update:modelValue", val), |
| | | set: (val) => emit("update:modelValue", val), |
| | | }); |
| | | |
| | | const formRef = ref(null); |
| | |
| | | const addPersonVisible = ref(false); |
| | | const taxDialogVisible = ref(false); |
| | | const deptOptions = ref([]); |
| | | const availableDeptIds = ref(new Set()); |
| | | const deptStaffTree = ref([]); |
| | | const employeeList = ref([]); |
| | | const selectedEmployees = ref([]); |
| | | const bankOptions = ref([]); |
| | | const userList = ref([]); |
| | | const recalcTimers = new Map(); |
| | | const recalcVersions = new Map(); |
| | | const taxTableData = ref([ |
| | | { level: 1, range: "ä¸è¶
è¿36000å
", rate: 3, quickDeduction: 0 }, |
| | | { level: 2, range: "è¶
è¿36000-144000å
", rate: 10, quickDeduction: 2520 }, |
| | |
| | | return isNaN(n) ? 0 : n; |
| | | } |
| | | |
| | | function parseNullableNum(v) { |
| | | if (v === "" || v == null) return null; |
| | | const n = Number(v); |
| | | return isNaN(n) ? null : n; |
| | | } |
| | | |
| | | const createEmployeeRow = () => ({ |
| | | staffOnJobId: undefined, |
| | | id: undefined, |
| | | staffName: "", |
| | | postName: "", |
| | | deptName: "", |
| | | basicSalary: 0, |
| | | pieceSalary: 0, |
| | | hourlySalary: 0, |
| | | otherIncome: 0, |
| | | socialPersonal: 0, |
| | | fundPersonal: 0, |
| | | otherDeduct: 0, |
| | | socialSupplementAmount: null, |
| | | salaryTax: 0, |
| | | grossSalary: 0, |
| | | deductSalary: 0, |
| | | netSalary: 0, |
| | | remark: "", |
| | | }); |
| | | |
| | | const normalizeEmployeeRow = (source, fallback = {}) => ({ |
| | | ...createEmployeeRow(), |
| | | ...fallback, |
| | | ...source, |
| | | staffOnJobId: source?.staffOnJobId ?? source?.staffId ?? source?.id ?? fallback.staffOnJobId ?? fallback.id, |
| | | id: source?.staffOnJobId ?? source?.staffId ?? source?.id ?? fallback.staffOnJobId ?? fallback.id, |
| | | staffName: source?.staffName ?? fallback.staffName ?? "", |
| | | postName: source?.postName ?? fallback.postName ?? "", |
| | | deptName: source?.deptName ?? fallback.deptName ?? "", |
| | | basicSalary: parseNum(source?.basicSalary ?? fallback.basicSalary), |
| | | pieceSalary: parseNum(source?.pieceSalary ?? fallback.pieceSalary), |
| | | hourlySalary: parseNum(source?.hourlySalary ?? fallback.hourlySalary), |
| | | otherIncome: parseNum(source?.otherIncome ?? fallback.otherIncome), |
| | | socialPersonal: parseNum(source?.socialPersonal ?? fallback.socialPersonal), |
| | | fundPersonal: parseNum(source?.fundPersonal ?? fallback.fundPersonal), |
| | | otherDeduct: parseNum(source?.otherDeduct ?? fallback.otherDeduct), |
| | | socialSupplementAmount: |
| | | source?.socialSupplementAmount ?? fallback.socialSupplementAmount ?? null, |
| | | salaryTax: parseNum(source?.salaryTax ?? fallback.salaryTax), |
| | | grossSalary: parseNum(source?.grossSalary ?? fallback.grossSalary), |
| | | deductSalary: parseNum(source?.deductSalary ?? fallback.deductSalary), |
| | | netSalary: parseNum(source?.netSalary ?? fallback.netSalary), |
| | | remark: source?.remark ?? fallback.remark ?? "", |
| | | }); |
| | | |
| | | const applyBackendRow = (targetRow, sourceRow) => { |
| | | Object.assign(targetRow, normalizeEmployeeRow(sourceRow, targetRow)); |
| | | }; |
| | | |
| | | const clearRowRecalcState = row => { |
| | | const key = String(row?.staffOnJobId ?? row?.id ?? ""); |
| | | if (!key) return; |
| | | clearTimeout(recalcTimers.get(key)); |
| | | recalcTimers.delete(key); |
| | | recalcVersions.delete(key); |
| | | }; |
| | | |
| | | const resetRecalcState = () => { |
| | | recalcTimers.forEach(timer => clearTimeout(timer)); |
| | | recalcTimers.clear(); |
| | | recalcVersions.clear(); |
| | | }; |
| | | |
| | | const requestEmployeeRecalculate = row => { |
| | | const key = String(row?.staffOnJobId ?? row?.id ?? ""); |
| | | if (!key || !form.value.salaryMonth) return; |
| | | const version = (recalcVersions.get(key) || 0) + 1; |
| | | recalcVersions.set(key, version); |
| | | clearTimeout(recalcTimers.get(key)); |
| | | const payload = { |
| | | staffOnJobId: row.staffOnJobId ?? row.id, |
| | | salaryMonth: form.value.salaryMonth, |
| | | staffName: row.staffName ?? "", |
| | | postName: row.postName ?? "", |
| | | deptName: row.deptName ?? "", |
| | | basicSalary: parseNum(row.basicSalary), |
| | | pieceSalary: parseNum(row.pieceSalary), |
| | | hourlySalary: parseNum(row.hourlySalary), |
| | | otherIncome: parseNum(row.otherIncome), |
| | | otherDeduct: parseNum(row.otherDeduct), |
| | | socialSupplementAmount: parseNullableNum(row.socialSupplementAmount), |
| | | remark: row.remark ?? "", |
| | | }; |
| | | recalcTimers.set( |
| | | key, |
| | | setTimeout(() => { |
| | | staffSalaryMainCalculateByEmployeeId(payload).then(res => { |
| | | if (recalcVersions.get(key) !== version) return; |
| | | applyBackendRow(row, res?.data || {}); |
| | | }); |
| | | }, 250) |
| | | ); |
| | | }; |
| | | |
| | | const handleSalaryInput = row => { |
| | | row.basicSalary = parseNum(row.basicSalary); |
| | | row.otherIncome = parseNum(row.otherIncome); |
| | | requestEmployeeRecalculate(row); |
| | | }; |
| | | |
| | | // åºç¡èµæè¡¨å |
| | | const data = reactive({ |
| | | form: { |
| | |
| | | auditUserId: undefined, |
| | | }, |
| | | rules: { |
| | | salaryTitle: [ |
| | | { required: true, message: "请è¾å
¥å·¥èµä¸»é¢", trigger: "blur" }, |
| | | ], |
| | | salaryTitle: [{ required: true, message: "请è¾å
¥å·¥èµä¸»é¢", trigger: "blur" }], |
| | | deptIds: [{ required: true, message: "è¯·éæ©é¨é¨", trigger: "change" }], |
| | | salaryMonth: [ |
| | | { required: true, message: "è¯·éæ©å·¥èµæä»½", trigger: "change" }, |
| | | ], |
| | | auditUserId: [ |
| | | { required: true, message: "è¯·éæ©å®¡æ ¸äºº", trigger: "change" }, |
| | | ], |
| | | salaryMonth: [{ required: true, message: "è¯·éæ©å·¥èµæä»½", trigger: "change" }], |
| | | auditUserId: [{ required: true, message: "è¯·éæ©å®¡æ ¸äºº", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | |
| | | }); |
| | | |
| | | const loadBankOptions = () => { |
| | | return bankList().then(res => { |
| | | return bankList().then((res) => { |
| | | const list = Array.isArray(res?.data) ? res.data : []; |
| | | bankOptions.value = list |
| | | .map(b => (b?.bankName == null ? "" : String(b.bankName).trim())) |
| | | .filter(v => v !== ""); |
| | | .map((b) => (b?.bankName == null ? "" : String(b.bankName).trim())) |
| | | .filter((v) => v !== ""); |
| | | }); |
| | | }; |
| | | |
| | | const loadUserList = () => { |
| | | return userListNoPageByTenantId().then(res => { |
| | | return userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data || []; |
| | | }); |
| | | }; |
| | |
| | | // æå¹³åé¨é¨æ ä¾ä¸æä½¿ç¨ |
| | | function flattenDept(tree, list = []) { |
| | | if (!tree?.length) return list; |
| | | tree.forEach(node => { |
| | | tree.forEach((node) => { |
| | | list.push({ deptId: node.deptId, deptName: node.deptName }); |
| | | if (node.children?.length) flattenDept(node.children, list); |
| | | }); |
| | | return list; |
| | | } |
| | | |
| | | // å è½½ææé¨é¨ |
| | | const loadAllDeptOptions = () => { |
| | | listDept().then(res => { |
| | | const loadDeptOptions = () => { |
| | | listDept().then((res) => { |
| | | const tree = res.data ?? []; |
| | | deptOptions.value = flattenDept(tree); |
| | | }); |
| | | }; |
| | | |
| | | // å è½½å¯ç¨é¨é¨å表ï¼ç¨äºå¤æé¨é¨æ¯å¦å¯éï¼ |
| | | const loadAvailableDepts = (salaryMonth) => { |
| | | if (!salaryMonth) { |
| | | availableDeptIds.value = new Set(); |
| | | return; |
| | | } |
| | | staffSalaryMainAvailableDepts(salaryMonth).then(res => { |
| | | const availableDepts = Array.isArray(res.data) ? res.data : []; |
| | | availableDeptIds.value = new Set(availableDepts.map(d => d.deptId)); |
| | | }); |
| | | }; |
| | | |
| | | // 夿é¨é¨æ¯å¦ç¦ç¨ |
| | | const isDeptDisabled = (deptId) => { |
| | | // å¦ææ²¡æéæ©æä»½ï¼ææé¨é¨é½å¯é |
| | | if (!form.value.salaryMonth) return false; |
| | | // 妿已鿩æä»½ï¼åªæå¨å¯ç¨é¨é¨å表ä¸çé¨é¨æå¯é |
| | | return !availableDeptIds.value.has(deptId); |
| | | }; |
| | | |
| | | // æå»º é¨é¨-äººå æ ï¼ç¨äºæ°å¢äººåå¼¹çªï¼ |
| | |
| | | const staffList = staffRes.data ?? []; |
| | | const deptMap = new Map(); |
| | | function walk(nodes) { |
| | | nodes.forEach(node => { |
| | | nodes.forEach((node) => { |
| | | deptMap.set(node.deptId, { |
| | | id: "dept_" + node.deptId, |
| | | deptId: node.deptId, |
| | |
| | | }); |
| | | } |
| | | walk(tree); |
| | | staffList.forEach(s => { |
| | | staffList.forEach((s) => { |
| | | const deptId = s.deptId ?? s.dept_id; |
| | | const node = deptMap.get(deptId); |
| | | if (node) { |
| | |
| | | } |
| | | }); |
| | | deptStaffTree.value = Array.from(deptMap.values()).filter( |
| | | n => n.children && n.children.length > 0 |
| | | (n) => n.children && n.children.length > 0 |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | const openDialog = (type, row) => { |
| | | nextTick(() => { |
| | | resetRecalcState(); |
| | | loadDeptOptions(); |
| | | loadBankOptions(); |
| | | loadUserList(); |
| | | loadAllDeptOptions(); |
| | | employeeList.value = []; |
| | | Object.assign(form.value, { |
| | | id: undefined, |
| | |
| | | form.value.salaryTitle = row.salaryTitle ?? ""; |
| | | // deptIds å端æ¯å符串ï¼å¤ä¸ªç¨éå·åéï¼ï¼å½å表å仿¯åé deptId |
| | | form.value.deptIds = row.deptIds |
| | | ? String(row.deptIds) |
| | | .split(",") |
| | | .map(id => Number(id.trim())) |
| | | .filter(Boolean) |
| | | ? String(row.deptIds).split(",").map((id) => Number(id.trim())).filter(Boolean) |
| | | : []; |
| | | form.value.salaryMonth = row.salaryMonth ?? ""; |
| | | form.value.remark = row.remark ?? ""; |
| | |
| | | |
| | | // 妿æåå·¥æç»æ°æ®ï¼ç´æ¥åæ¾ |
| | | if (row.staffSalaryDetailList && row.staffSalaryDetailList.length > 0) { |
| | | employeeList.value = row.staffSalaryDetailList.map(e => normalizeEmployeeRow(e)); |
| | | employeeList.value = row.staffSalaryDetailList.map((e) => ({ |
| | | staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | id: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | staffName: e.staffName ?? "", |
| | | postName: e.postName ?? "", |
| | | deptName: e.deptName ?? "", |
| | | basicSalary: parseNum(e.basicSalary), |
| | | pieceSalary: parseNum(e.pieceSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialPersonal: parseNum(e.socialPersonal), |
| | | fundPersonal: parseNum(e.fundPersonal), |
| | | otherDeduct: parseNum(e.otherDeduct), |
| | | salaryTax: parseNum(e.salaryTax), |
| | | grossSalary: parseNum(e.grossSalary), |
| | | deductSalary: parseNum(e.deductSalary), |
| | | netSalary: parseNum(e.netSalary), |
| | | remark: e.remark ?? "", |
| | | })); |
| | | } |
| | | } |
| | | // ç¼è¾ç¶æä¸ææä»½æ¶ï¼å è½½å¯ç¨é¨é¨å表 |
| | | if (type === "edit" && form.value.salaryMonth) { |
| | | loadAvailableDepts(form.value.salaryMonth); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // çå¬å·¥èµæä»½ååï¼æ¸
空é¨é¨éæ©å¹¶å è½½å¯ç¨é¨é¨å表 |
| | | watch(() => form.value.salaryMonth, (newMonth, oldMonth) => { |
| | | // 忢æä»½æ¶æ¸
ç©ºå·²éæ©çé¨é¨ |
| | | if (newMonth !== oldMonth) { |
| | | form.value.deptIds = []; |
| | | } |
| | | loadAvailableDepts(newMonth); |
| | | }); |
| | | |
| | | const openAddPerson = () => { |
| | | loadDeptStaffTree(); |
| | |
| | | return; |
| | | } |
| | | const checked = tree.getCheckedNodes(); |
| | | const staffNodes = checked.filter(n => n.type === "staff"); |
| | | const existIds = new Set(employeeList.value.map(e => e.staffId || e.id)); |
| | | staffNodes.forEach(node => { |
| | | const staffNodes = checked.filter((n) => n.type === "staff"); |
| | | const existIds = new Set(employeeList.value.map((e) => e.staffId || e.id)); |
| | | staffNodes.forEach((node) => { |
| | | const id = node.staffId ?? node.id; |
| | | if (existIds.has(id)) return; |
| | | existIds.add(id); |
| | | const newRow = normalizeEmployeeRow({ |
| | | employeeList.value.push({ |
| | | staffOnJobId: id, |
| | | id, |
| | | staffName: node.label, |
| | | postName: node.postName ?? node.post ?? "", |
| | | deptName: node.deptName ?? "", |
| | | basicSalary: 0, |
| | | pieceSalary: 0, |
| | | hourlySalary: 0, |
| | | otherIncome: 0, |
| | | socialPersonal: 0, |
| | | fundPersonal: 0, |
| | | otherDeduct: 0, |
| | | salaryTax: 0, |
| | | grossSalary: 0, |
| | | deductSalary: 0, |
| | | netSalary: 0, |
| | | remark: "", |
| | | socialSupplementAmount: null, |
| | | }); |
| | | employeeList.value.push(newRow); |
| | | requestEmployeeRecalculate(newRow); |
| | | }); |
| | | addPersonVisible.value = false; |
| | | }; |
| | | |
| | | const removeEmployee = row => { |
| | | clearRowRecalcState(row); |
| | | const removeEmployee = (row) => { |
| | | employeeList.value = employeeList.value.filter( |
| | | e => (e.staffOnJobId || e.id) !== (row.staffOnJobId || row.id) |
| | | (e) => (e.staffOnJobId || e.id) !== (row.staffOnJobId || row.id) |
| | | ); |
| | | }; |
| | | |
| | | const onEmployeeSelectionChange = selection => { |
| | | const onEmployeeSelectionChange = (selection) => { |
| | | selectedEmployees.value = selection; |
| | | }; |
| | | |
| | |
| | | proxy.$modal.msgWarning("请å
å¾éè¦å é¤çåå·¥"); |
| | | return; |
| | | } |
| | | const ids = new Set(selectedEmployees.value.map(e => e.staffOnJobId || e.id)); |
| | | selectedEmployees.value.forEach(item => clearRowRecalcState(item)); |
| | | const ids = new Set(selectedEmployees.value.map((e) => e.staffOnJobId || e.id)); |
| | | employeeList.value = employeeList.value.filter( |
| | | e => !ids.has(e.staffOnJobId || e.id) |
| | | (e) => !ids.has(e.staffOnJobId || e.id) |
| | | ); |
| | | }; |
| | | |
| | |
| | | ids: form.value.deptIds, |
| | | date: form.value.salaryMonth, |
| | | }; |
| | | staffSalaryMainCalculateSalary(payload).then(res => { |
| | | staffSalaryMainCalculateSalary(payload).then((res) => { |
| | | const list = Array.isArray(res?.data) ? res.data : []; |
| | | if (!list.length) { |
| | | proxy.$modal.msgWarning("æªè®¡ç®å°å·¥èµæ°æ®"); |
| | | return; |
| | | } |
| | | resetRecalcState(); |
| | | employeeList.value = list.map(e => normalizeEmployeeRow(e)); |
| | | employeeList.value = list.map((e) => ({ |
| | | ...e, |
| | | staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | staffName: e.staffName, |
| | | postName: e.postName, |
| | | deptName: e.deptName, |
| | | basicSalary: parseNum(e.basicSalary), |
| | | pieceSalary: parseNum(e.pieceSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialPersonal: parseNum(e.socialPersonal), |
| | | fundPersonal: parseNum(e.fundPersonal), |
| | | otherDeduct: parseNum(e.otherDeduct), |
| | | salaryTax: parseNum(e.salaryTax), |
| | | grossSalary: parseNum(e.grossSalary), |
| | | deductSalary: parseNum(e.deductSalary), |
| | | netSalary: parseNum(e.netSalary), |
| | | remark: e.remark ?? "", |
| | | })); |
| | | proxy.$modal.msgSuccess("çææå"); |
| | | }); |
| | | }; |
| | | |
| | | const handleClear = () => { |
| | | proxy.$modal |
| | | .confirm("ç¡®å®æ¸
空å½ååå·¥å表åï¼") |
| | | .then(() => { |
| | | resetRecalcState(); |
| | | proxy.$modal.confirm("ç¡®å®æ¸
空å½ååå·¥å表åï¼").then(() => { |
| | | employeeList.value = []; |
| | | }) |
| | | .catch(() => {}); |
| | | }).catch(() => {}); |
| | | }; |
| | | |
| | | const handleTaxForm = () => { |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value?.validate(valid => { |
| | | formRef.value?.validate((valid) => { |
| | | if (!valid) return; |
| | | saveData(3); // 确认æäº¤ï¼ç¶æä¸º3ï¼å¾
å®¡æ ¸ï¼ |
| | | }); |
| | | }; |
| | | |
| | | const saveDraft = () => { |
| | | formRef.value?.validate(valid => { |
| | | formRef.value?.validate((valid) => { |
| | | if (!valid) return; |
| | | saveData(1); // ä¿åè稿ï¼ç¶æä¸º1ï¼èç¨¿ï¼ |
| | | }); |
| | | }; |
| | | |
| | | const saveData = status => { |
| | | const saveData = (status) => { |
| | | const payload = { |
| | | id: form.value.id, |
| | | salaryTitle: form.value.salaryTitle, |
| | |
| | | auditUserId: form.value.auditUserId, |
| | | auditUserName: auditUserName.value, |
| | | totalSalary: totalSalary.value, |
| | | staffSalaryDetailList: employeeList.value.map(e => ({ |
| | | staffSalaryDetailList: employeeList.value.map((e) => ({ |
| | | staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | staffName: e.staffName, |
| | | postName: e.postName ?? "", |
| | |
| | | socialPersonal: parseNum(e.socialPersonal), |
| | | fundPersonal: parseNum(e.fundPersonal), |
| | | otherDeduct: parseNum(e.otherDeduct), |
| | | socialSupplementAmount: parseNum(e.socialSupplementAmount), |
| | | salaryTax: parseNum(e.salaryTax), |
| | | grossSalary: parseNum(e.grossSalary), |
| | | deductSalary: parseNum(e.deductSalary), |
| | |
| | | }; |
| | | |
| | | const closeDia = () => { |
| | | resetRecalcState(); |
| | | dialogVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | |
| | | nextTick, |
| | | } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import Cookies from "js-cookie"; |
| | | import FormDia from "./components/formDia.vue"; |
| | | import BankSettingDia from "./components/bankSettingDia.vue"; |
| | | import AuditDia from "./components/auditDia.vue"; |
| | |
| | | { |
| | | name: "å®¡æ ¸", |
| | | type: "text", |
| | | disabled: (row) => { |
| | | const currentUserId = Number(userStore.id); |
| | | const auditUserId = Number(row?.auditUserId); |
| | | // ç¶æä¸æ¯å¾
å®¡æ ¸ æ å½åç¨æ·ä¸æ¯æå®çå®¡æ ¸äººï¼åç¦ç¨å®¡æ ¸æé® |
| | | return Number(row?.status) !== 3 || !currentUserId || currentUserId !== auditUserId; |
| | | }, |
| | | disabled: (row) => Number(row?.status) !== 3, |
| | | clickFun: (row) => openAudit(row), |
| | | }, |
| | | { |
| | |
| | | const operationType = ref("add"); |
| | | const currentRow = ref({}); |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const bankSetting = ref({}); |
| | | const bankDialogVisible = ref(false); |
| | | const bankDiaRef = ref(null); |
| | |
| | | |
| | | const handleExport = () => { |
| | | proxy.download( |
| | | "/staffSalaryMain/export", |
| | | { ...searchForm.value }, |
| | | "/compensationPerformance/export", |
| | | { ...searchForm.value, current: page.current, size: page.size }, |
| | | "å·¥èµè¡¨.xlsx" |
| | | ); |
| | | }; |
| | |
| | | <template> |
| | | <div> |
| | | <FormDialog v-model="dialogFormVisible" |
| | | <FormDialog |
| | | v-model="dialogFormVisible" |
| | | :operation-type="operationType" |
| | | :title="dialogTitle" |
| | | width="80%" |
| | | @close="closeDia" |
| | | @confirm="submitForm" |
| | | @cancel="closeDia"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-position="top"> |
| | | @cancel="closeDia" |
| | | > |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-position="top"> |
| | | <el-row :gutter="24"> |
| | | <!-- 左侧ï¼éç¨äººå --> |
| | | <el-col :span="8"> |
| | | <el-form-item label="éç¨äººåï¼" |
| | | prop="deptIds"> |
| | | <el-form-item label="éç¨äººåï¼" prop="deptIds"> |
| | | <div class="dept-checkbox-wrap"> |
| | | <el-checkbox-group v-model="form.deptIds" |
| | | :disabled="isDetail"> |
| | | <div v-for="dept in deptList" |
| | | <el-checkbox-group |
| | | v-model="form.deptIds" |
| | | :disabled="isDetail" |
| | | > |
| | | <div |
| | | v-for="dept in deptList" |
| | | :key="dept.deptId" |
| | | class="dept-checkbox-item"> |
| | | class="dept-checkbox-item" |
| | | > |
| | | <el-checkbox :value="dept.deptId"> |
| | | {{ dept.deptName }} |
| | | <span v-if="dept.personCount != null" |
| | | class="dept-count">{{ dept.personCount }}人</span> |
| | | <span v-if="dept.personCount != null" class="dept-count" |
| | | >{{ dept.personCount }}人</span |
| | | > |
| | | </el-checkbox> |
| | | </div> |
| | | </el-checkbox-group> |
| | |
| | | <!-- å³ä¾§ï¼åºç¡ä¿¡æ¯ + ä¿é©ç±»å --> |
| | | <el-col :span="16"> |
| | | <!-- åºç¡ä¿¡æ¯ --> |
| | | <el-card class="form-card" |
| | | shadow="never"> |
| | | <el-card class="form-card" shadow="never"> |
| | | <template #header> |
| | | <span class="card-title"><span class="card-title-line">|</span> åºç¡ä¿¡æ¯</span> |
| | | <el-icon class="card-collapse"> |
| | | <ArrowUp /> |
| | | </el-icon> |
| | | <el-icon class="card-collapse"><ArrowUp /></el-icon> |
| | | </template> |
| | | <el-form-item label="æ¹æ¡æ é¢ï¼" |
| | | prop="title"> |
| | | <el-input v-model="form.title" |
| | | <el-form-item label="æ¹æ¡æ é¢ï¼" prop="title"> |
| | | <el-input |
| | | v-model="form.title" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :disabled="isDetail" /> |
| | | :disabled="isDetail" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨ï¼" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | <el-form-item label="夿³¨ï¼" prop="remark"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :disabled="isDetail" /> |
| | | :disabled="isDetail" |
| | | /> |
| | | </el-form-item> |
| | | </el-card> |
| | | |
| | | <!-- ä¿é©ç±»å --> |
| | | <el-card class="form-card" |
| | | shadow="never"> |
| | | <el-card class="form-card" shadow="never"> |
| | | <template #header> |
| | | <span class="card-title"><span class="card-title-line">|</span> ä¿é©ç±»å</span> |
| | | <el-button v-if="!isDetail" |
| | | <el-button |
| | | v-if="!isDetail" |
| | | type="primary" |
| | | size="small" |
| | | @click="addInsuranceBenefit"> |
| | | @click="addInsuranceBenefit" |
| | | > |
| | | æ·»å ä¿é©ç¦å© |
| | | </el-button> |
| | | </template> |
| | | <el-row :gutter="16"> |
| | | <el-col v-for="(item, index) in form.insuranceBenefits" |
| | | <el-col |
| | | v-for="(item, index) in form.insuranceBenefits" |
| | | :key="item._key" |
| | | :span="12"> |
| | | :span="12" |
| | | > |
| | | <div class="insurance-benefit-card"> |
| | | <div class="insurance-benefit-title"> |
| | | ä¿é©ç¦å©{{ index + 1 }} |
| | | <el-button v-if="!isDetail && form.insuranceBenefits.length > 1" |
| | | <el-button |
| | | v-if="!isDetail && form.insuranceBenefits.length > 1" |
| | | type="danger" |
| | | link |
| | | size="small" |
| | | class="card-delete-btn" |
| | | @click="removeInsuranceBenefit(index)"> |
| | | @click="removeInsuranceBenefit(index)" |
| | | > |
| | | å é¤ |
| | | </el-button> |
| | | </div> |
| | | <el-form-item :prop="'insuranceBenefits.' + index + '.insuranceType'" |
| | | <el-form-item |
| | | :prop="'insuranceBenefits.' + index + '.insuranceType'" |
| | | label="ä¿é©ç±»åï¼" |
| | | label-width="100px" |
| | | :rules="[{ required: true, message: 'è¯·éæ©ä¿é©ç±»å', trigger: 'blur' }]" |
| | | > |
| | | <el-select v-model="item.insuranceType" |
| | | <el-select |
| | | v-model="item.insuranceType" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isDetail"> |
| | | <el-option v-for="opt in insuranceTypeOptions" |
| | | :disabled="isDetail" |
| | | > |
| | | <el-option |
| | | v-for="opt in insuranceTypeOptions" |
| | | :key="opt.value" |
| | | :label="opt.label" |
| | | :value="opt.value" /> |
| | | :value="opt.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¼´è´¹åºæ°ï¼" |
| | | label-width="100px" |
| | | :prop="'insuranceBenefits.' + index + '.paymentBase'" |
| | | :rules="[{ validator: validatePaymentBase, trigger: 'blur' }]"> |
| | | <el-form-item label="ç¼´è´¹åºæ°ï¼" label-width="100px"> |
| | | <div class="base-salary-wrap"> |
| | | <el-input v-model="item.paymentBase" |
| | | <el-input |
| | | v-model="item.paymentBase" |
| | | placeholder="æ ¹æ®åºæ¬å·¥èµç¼´çº³" |
| | | clearable |
| | | style="width: 120px" |
| | | type="number" |
| | | :disabled="isDetail || item.useBasicSalary" |
| | | @input="handlePaymentBaseInput(item)" /> |
| | | <el-checkbox v-model="item.useBasicSalary" |
| | | @change="(val) => handleUseBasicSalaryChange(item, index)" |
| | | :disabled="isDetail"> |
| | | @input="handlePaymentBaseInput(item)" |
| | | /> |
| | | <el-checkbox |
| | | v-model="item.useBasicSalary" |
| | | @change="handleUseBasicSalaryChange(item)" |
| | | :disabled="isDetail" |
| | | > |
| | | è°ç¨åºæ¬å·¥èµ |
| | | </el-checkbox> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="个人缴费æ¯ä¾ï¼" |
| | | label-width="100px" |
| | | :prop="'insuranceBenefits.' + index + '.personalRatio'" |
| | | :rules="[{ required: true, message: '请è¾å
¥ä¸ªäººç¼´è´¹æ¯ä¾', trigger: 'blur' }]"> |
| | | <el-form-item label="个人缴费æ¯ä¾ï¼" label-width="100px"> |
| | | <div class="personal-ratio-wrap"> |
| | | <el-input v-model="item.personalRatio" |
| | | <el-input |
| | | v-model="item.personalRatio" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | style="width: 100px" |
| | | type="number" |
| | | :disabled="isDetail" |
| | | :min="0" |
| | | @input="handlePersonalRatioInput(item)" /> |
| | | /> |
| | | <span class="ratio-unit">(%)</span> |
| | | <span class="ratio-plus">+</span> |
| | | <el-input v-model="item.personalFixed" |
| | | <el-input |
| | | v-model="item.personalFixed" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | style="width: 100px" |
| | | type="number" |
| | | :disabled="isDetail" |
| | | :min="0" |
| | | @input="handlePersonalFixedInput(item)" /> |
| | | /> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | computed, |
| | | } from "vue"; |
| | | import { ref, reactive, toRefs, getCurrentInstance, nextTick, computed } from "vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ArrowUp } from "@element-plus/icons-vue"; |
| | | import { listDept } from "@/api/system/dept.js"; |
| | | import { |
| | | socialSecurityAdd, |
| | | socialSecurityUpdate, |
| | | } from "@/api/personnelManagement/socialSecuritySet.js"; |
| | | import { socialSecurityAdd, socialSecurityUpdate } from "@/api/personnelManagement/socialSecuritySet.js"; |
| | | |
| | | const emit = defineEmits(["close"]); |
| | | const { proxy } = getCurrentInstance(); |
| | |
| | | |
| | | function flattenDept(tree, list = []) { |
| | | if (!tree || !tree.length) return list; |
| | | tree.forEach(node => { |
| | | tree.forEach((node) => { |
| | | list.push({ |
| | | deptId: node.deptId, |
| | | deptName: node.deptName, |
| | |
| | | } |
| | | |
| | | const loadDeptList = () => { |
| | | listDept().then(res => { |
| | | listDept().then((res) => { |
| | | const tree = res.data ?? []; |
| | | deptList.value = flattenDept(tree); |
| | | }); |
| | |
| | | form.value.insuranceBenefits.push(defaultBenefit()); |
| | | }; |
| | | |
| | | const removeInsuranceBenefit = index => { |
| | | const removeInsuranceBenefit = (index) => { |
| | | form.value.insuranceBenefits.splice(index, 1); |
| | | }; |
| | | |
| | | const handleUseBasicSalaryChange = (item, index) => { |
| | | const handleUseBasicSalaryChange = (item) => { |
| | | if (item.useBasicSalary) { |
| | | item.paymentBase = ""; |
| | | } |
| | | nextTick(() => { |
| | | formRef.value?.validateField(`insuranceBenefits.${index}.paymentBase`); |
| | | }); |
| | | }; |
| | | |
| | | const handlePaymentBaseInput = item => { |
| | | const handlePaymentBaseInput = (item) => { |
| | | if (item.paymentBase !== "" && item.paymentBase != null) { |
| | | item.useBasicSalary = false; |
| | | } |
| | | }; |
| | | |
| | | const handlePersonalRatioInput = item => { |
| | | if (item.personalRatio !== "" && item.personalRatio != null) { |
| | | const value = Number(item.personalRatio); |
| | | if (value < 0) { |
| | | item.personalRatio = ""; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handlePersonalFixedInput = item => { |
| | | if (item.personalFixed !== "" && item.personalFixed != null) { |
| | | const value = Number(item.personalFixed); |
| | | if (value < 0) { |
| | | item.personalFixed = ""; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const validatePaymentBase = (rule, value, callback) => { |
| | | const match = rule.field.match(/insuranceBenefits\.(\d+)\.paymentBase/); |
| | | if (match) { |
| | | const index = parseInt(match[1], 10); |
| | | const item = form.value.insuranceBenefits[index]; |
| | | if (item && !item.useBasicSalary && (!value || value === "")) { |
| | | callback(new Error("请è¾å
¥ç¼´è´¹åºæ°")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }; |
| | | |
| | |
| | | if (d.deptIds) { |
| | | form.value.deptIds = String(d.deptIds) |
| | | .split(",") |
| | | .filter(v => v !== "") |
| | | .map(v => { |
| | | .filter((v) => v !== "") |
| | | .map((v) => { |
| | | const num = Number(v); |
| | | return Number.isNaN(num) ? v : num; |
| | | }); |
| | |
| | | const detailList = d.schemeInsuranceDetailList || []; |
| | | form.value.insuranceBenefits = |
| | | detailList.length > 0 |
| | | ? detailList.map(b => ({ |
| | | ? detailList.map((b) => ({ |
| | | _key: Math.random().toString(36).slice(2), |
| | | insuranceType: b.insuranceType || "", |
| | | paymentBase: b.paymentBase ?? "", |
| | |
| | | closeDia(); |
| | | return; |
| | | } |
| | | formRef.value?.validate(valid => { |
| | | formRef.value?.validate((valid) => { |
| | | if (!valid) return; |
| | | const deptIds = |
| | | Array.isArray(form.value.deptIds) && form.value.deptIds.length |
| | |
| | | }) |
| | | ); |
| | | const insuranceTypes = schemeInsuranceDetailList |
| | | .map(item => item.insuranceType) |
| | | .filter(v => v) |
| | | .map((item) => item.insuranceType) |
| | | .filter((v) => v) |
| | | .join(","); |
| | | // é¨é¨åç§°ï¼å¤ä¸ªä½¿ç¨éå·éå¼ï¼æ ¹æ®éä¸ç deptIds ä¸ deptList 计ç®ï¼ |
| | | const deptNames = (deptList.value || []) |
| | | .filter(d => |
| | | (form.value.deptIds || []).some(id => String(id) === String(d.deptId)) |
| | | .filter((d) => |
| | | (form.value.deptIds || []).some( |
| | | (id) => String(id) === String(d.deptId) |
| | | ) |
| | | .map(d => d.deptName) |
| | | ) |
| | | .map((d) => d.deptName) |
| | | .join(","); |
| | | const submitData = { |
| | | id: form.value.id, |
| | |
| | | prop="contractAmounts" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="仿¬¾éé¢(å
)" |
| | | prop="paymentAmount" |
| | | <el-table-column label="å·²å
¥åºéé¢(å
)" |
| | | prop="shippedAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="éè´§éé¢(å
)" |
| | | prop="returnAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="åºä»éé¢(å
)" |
| | | prop="payableAmount" |
| | | <el-table-column label="æªå
¥åºéé¢(å
)" |
| | | prop="unshippedAmount" |
| | | show-overflow-tooltip> |
| | | <template #default="{ row, column }"> |
| | | <el-text type="danger"> |
| | | {{ formattedNumber(row, column, row.payableAmount) }} |
| | | {{ formattedNumber(row, column, row.unshippedAmount) }} |
| | | </el-text> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | @pagination="sonPaginationSearch"> |
| | | <template #payableAmountSlot="{ row }"> |
| | | <el-text type="danger"> |
| | | {{ parseFloat(row.payableAmount).toFixed(2) }} |
| | | {{ formatDecimal(row.unshippedAmount) }} |
| | | </el-text> |
| | | </template> |
| | | </PIMTable> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, toRefs } from "vue"; |
| | | import { ref, toRefs, reactive, getCurrentInstance } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | paymentLedgerList, |
| | | paymentRecordList, |
| | | } from "@/api/procurementManagement/paymentLedger.js"; |
| | | import Pagination from "../../../components/PIMTable/Pagination.vue"; |
| | | import { tableAmountFormatter, formatDecimal, buildAmountSummaryFormat } from "@/utils/numberFormat"; |
| | | |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | label: "ååéé¢(å
)", |
| | | prop: "contractAmount", |
| | | width: 200, |
| | | formatData: params => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | formatData: params => formatDecimal(params), |
| | | }, |
| | | { |
| | | label: "仿¬¾éé¢(å
)", |
| | | prop: "paymentAmount", |
| | | label: "å·²å
¥åºéé¢(å
)", |
| | | prop: "shippedAmount", |
| | | width: 200, |
| | | formatData: params => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | formatData: params => formatDecimal(params), |
| | | }, |
| | | { |
| | | label: "éè´§éé¢(å
)", |
| | | prop: "returnAmount", |
| | | width: 200, |
| | | formatData: params => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "åºä»éé¢(å
)", |
| | | label: "æªå
¥åºéé¢(å
)", |
| | | dataType: "slot", |
| | | width: 200, |
| | | prop: "payableAmount", |
| | | prop: "unshippedAmount", |
| | | slot: "payableAmountSlot", |
| | | }, |
| | | ]); |
| | |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["contractAmounts", "paymentAmount", "returnAmount", "payableAmount"], |
| | | ["contractAmounts", "shippedAmount", "unshippedAmount"], |
| | | buildAmountSummaryFormat( |
| | | ["contractAmounts", "shippedAmount", "unshippedAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | ticketsNum: { noDecimal: true }, |
| | | futureTickets: { noDecimal: true }, |
| | | } |
| | | ) |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable1 = param => { |
| | | let summarizeTable = proxy.summarizeTable( |
| | | param, |
| | | ["contractAmount", "invoiceAmount", "paymentAmount", "returnAmount"], |
| | | ["contractAmount", "shippedAmount", "unshippedAmount"], |
| | | buildAmountSummaryFormat( |
| | | ["contractAmount", "shippedAmount", "unshippedAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | ticketsNum: { noDecimal: true }, |
| | | futureTickets: { noDecimal: true }, |
| | | } |
| | | ) |
| | | ); |
| | | if (originalTableDataSon.value.length > 0) { |
| | | summarizeTable[summarizeTable.length - 1] = |
| | | originalTableDataSon.value[ |
| | | originalTableDataSon.value.length - 1 |
| | | ].payableAmount.toFixed(2); |
| | | } else { |
| | | summarizeTable[summarizeTable.length - 1] = 0.0; |
| | | } |
| | | return summarizeTable; |
| | | }; |
| | | /** æç´¢æé®æä½ */ |
| | |
| | | sonPage.size = pagination.limit; |
| | | getPaymenRecordtList(currentSupplierId.value); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | if (column.property !== "supplierName") { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | } else { |
| | | return cellValue; |
| | | } |
| | | }; |
| | | const formattedNumber = tableAmountFormatter; |
| | | getList(); |
| | | </script> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <FormDialog v-model="visible" title="éè´å°è´¦è¯¦æ
" width="70%" operation-type="detail" @close="handleClose"> |
| | | <el-form :model="form" label-width="140px" label-position="top"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ååå·ï¼"> |
| | | <span class="readonly-text">{{ form.purchaseContractNumber || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col v-if="showSalesContractBinding" :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <span class="readonly-text">{{ form.salesContractNo || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºååç§°ï¼"> |
| | | <span class="readonly-text">{{ form.supplierName || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项ç®åç§°ï¼"> |
| | | <span class="readonly-text">{{ form.projectName || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼ï¼"> |
| | | <span class="readonly-text">{{ form.paymentMethod || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¾è®¢æ¥æï¼"> |
| | | <span class="readonly-text">{{ form.executionDate || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥äººï¼"> |
| | | <span class="readonly-text">{{ form.recorderName || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥æ¥æï¼"> |
| | | <span class="readonly-text">{{ form.entryDate || '--' }}</span> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <div style="font-weight: bold; margin: 10px 0; color: #f56c6c"> |
| | | * 产åä¿¡æ¯ï¼ |
| | | </div> |
| | | <el-table :data="productData" border> |
| | | <el-table-column type="index" label="åºå·" width="60" align="center" /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" /> |
| | | <el-table-column label="æ°é" prop="quantity" /> |
| | | <el-table-column label="åºåé¢çæ°é" prop="stockReservedQuantity" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="150" /> |
| | | <template #append> |
| | | <div class="summary-row" v-if="productData.length > 0"> |
| | | <div class="summary-label">å计</div> |
| | | <div class="summary-value" style="width: 150px"> |
| | | {{ formattedNumber(null, null, sumTaxInclusiveUnitPrice) }} |
| | | </div> |
| | | <div class="summary-value" style="width: 150px"> |
| | | {{ formattedNumber(null, null, sumTaxInclusiveTotalPrice) }} |
| | | </div> |
| | | <div class="summary-value" style="width: 150px"> |
| | | {{ formattedNumber(null, null, sumTaxExclusiveTotalPrice) }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table> |
| | | |
| | | <el-row :gutter="30" style="margin-top: 20px;"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨ï¼"> |
| | | <div class="readonly-text-area">{{ form.remarks || '--' }}</div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶ææï¼"> |
| | | <div class="table-container" style="width: 100%;"> |
| | | <el-table :data="fileList" border class="attachment-table" style="width: 100%;" |
| | | :height="fileList.length > 0 ? 'auto' : '120px'"> |
| | | <el-table-column label="éä»¶åç§°" prop="originalFilename" show-overflow-tooltip min-width="200"> |
| | | <template #default="scope"> |
| | | {{ scope.row.name || scope.row.originalFilename || ('éä»¶' + (scope.$index + 1)) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="æä½" :width="150" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" class="download-link" |
| | | @click="previewFile(scope.row.previewURL || scope.row.url)"> |
| | | é¢è§ |
| | | </el-button> |
| | | <el-button link type="primary" size="small" class="download-link" |
| | | @click="downloadFile(scope.row.downloadURL || scope.row.url)"> |
| | | ä¸è½½ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </FormDialog> |
| | | <filePreview ref="filePreviewRef" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue" |
| | | import filePreview from '@/components/filePreview/index.vue' |
| | | import { getPurchaseById } from "@/api/procurementManagement/procurementLedger" |
| | | import { tableAmountFormatter } from '@/utils/numberFormat' |
| | | |
| | | const visible = ref(false) |
| | | // æ¯å¦æ¾ç¤ºéå®ååå·ç»å® |
| | | const showSalesContractBinding = false |
| | | const form = ref({}) |
| | | const productData = ref([]) |
| | | const fileList = ref([]) |
| | | const filePreviewRef = ref() |
| | | |
| | | const open = (id) => { |
| | | visible.value = true |
| | | getPurchaseById({ id: id, type: 2 }).then(res => { |
| | | form.value = res.data ? res.data : res || {} |
| | | productData.value = form.value.purchaseLedgerProductList || form.value.productData || [] |
| | | fileList.value = form.value.storageBlobVOS || [] |
| | | }) |
| | | } |
| | | |
| | | // æ´é² open æ¹æ³ç»ç¶ç»ä»¶è°ç¨ |
| | | defineExpose({ |
| | | open |
| | | }) |
| | | |
| | | const handleClose = () => { |
| | | visible.value = false |
| | | form.value = {} |
| | | productData.value = [] |
| | | fileList.value = [] |
| | | } |
| | | |
| | | const formattedNumber = tableAmountFormatter |
| | | |
| | | const sumTaxInclusiveUnitPrice = computed(() => { |
| | | return productData.value.reduce((sum, item) => { |
| | | return sum + (Number(item.taxInclusiveUnitPrice) || 0) |
| | | }, 0) |
| | | }) |
| | | |
| | | const sumTaxInclusiveTotalPrice = computed(() => { |
| | | return productData.value.reduce((sum, item) => { |
| | | return sum + (Number(item.taxInclusiveTotalPrice) || 0) |
| | | }, 0) |
| | | }) |
| | | |
| | | const sumTaxExclusiveTotalPrice = computed(() => { |
| | | return productData.value.reduce((sum, item) => { |
| | | return sum + (Number(item.taxExclusiveTotalPrice) || 0) |
| | | }, 0) |
| | | }) |
| | | |
| | | const previewFile = (url) => { |
| | | if (url) { |
| | | filePreviewRef.value.open(url) |
| | | } else { |
| | | ElMessage.warning('æä»¶å°åæ æï¼æ æ³é¢è§') |
| | | } |
| | | } |
| | | |
| | | const downloadFile = (url) => { |
| | | window.open(url, "_blank") |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .readonly-text { |
| | | width: 100%; |
| | | padding: 0 12px; |
| | | background-color: #f5f7fa; |
| | | color: #606266; |
| | | border-radius: 4px; |
| | | border: 1px solid #e4e7ed; |
| | | box-sizing: border-box; |
| | | min-height: 32px; |
| | | line-height: 32px; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .readonly-text-area { |
| | | width: 100%; |
| | | padding: 8px 12px; |
| | | background-color: #f5f7fa; |
| | | color: #606266; |
| | | border-radius: 4px; |
| | | border: 1px solid #e4e7ed; |
| | | box-sizing: border-box; |
| | | min-height: 64px; |
| | | line-height: 1.5; |
| | | white-space: pre-wrap; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .summary-row { |
| | | display: flex; |
| | | background-color: #f5f7fa; |
| | | font-weight: bold; |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .summary-label { |
| | | flex: 1; |
| | | text-align: right; |
| | | padding: 12px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .summary-value { |
| | | padding: 12px; |
| | | color: #606266; |
| | | border-left: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .table-container { |
| | | overflow-y: auto; |
| | | box-sizing: border-box; |
| | | } |
| | | </style> |
| | |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="ä¾åºååç§°ï¼"> |
| | | <el-input v-model="searchForm.supplierName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | <el-input v-model="searchForm.supplierName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="éè´ååå·ï¼"> |
| | | <el-input v-model="searchForm.purchaseContractNumber" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <el-input v-model="searchForm.purchaseContractNumber" style="width: 240px" placeholder="请è¾å
¥" |
| | | @change="handleQuery" clearable :prefix-icon="Search" /> |
| | | </el-form-item> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <el-input v-model="searchForm.salesContractNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | <el-form-item v-if="showSalesContractBinding" label="éå®ååå·ï¼"> |
| | | <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="项ç®åç§°ï¼"> |
| | | <el-input v-model="searchForm.projectName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | <el-input v-model="searchForm.projectName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="å½å
¥æ¥æï¼"> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" |
| | | type="daterange" placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item label="å
¥åºç¶æï¼"> |
| | | <el-select v-model="searchForm.stockInStatus" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 240px" |
| | | <el-select v-model="searchForm.stockInStatus" placeholder="è¯·éæ©" clearable style="width: 240px" |
| | | @change="handleQuery"> |
| | | <el-option label="å¾
å
¥åº" |
| | | value="å¾
å
¥åº" /> |
| | | <el-option label="å
¥åºä¸" |
| | | value="å
¥åºä¸" /> |
| | | <el-option label="å®å
¨å
¥åº" |
| | | value="å®å
¨å
¥åº" /> |
| | | <el-option label="å¾
å
¥åº" value="å¾
å
¥åº" /> |
| | | <el-option label="å
¥åºä¸" value="å
¥åºä¸" /> |
| | | <el-option label="å®å
¨å
¥åº" value="å®å
¨å
¥åº" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleQuery"> æç´¢ |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;"> |
| | | <el-button type="success" |
| | | plain |
| | | @click="handleBatchGenerate">æ¹éçææ°æ®</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢å°è´¦</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handleImport">导å
¥</el-button> |
| | | <el-button type="primary" @click="openForm('add')">æ°å¢å°è´¦</el-button> |
| | | <el-button type="primary" plain @click="handleImport">导å
¥</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤ |
| | | <el-button type="danger" plain @click="handleDelete">å é¤ |
| | | </el-button> |
| | | <el-button type="warning" plain @click="handlePrint">æå°åæ®</el-button> |
| | | </div> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | @expand-change="expandChange" |
| | | height="calc(100vh - 21.5em)"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | | <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary :summary-method="summarizeMainTable" |
| | | @expand-change="expandChange" height="calc(100vh - 21.5em)"> |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column type="expand"> |
| | | <template #default="props"> |
| | | <el-table :data="props.row.children" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeChildrenTable"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="产å大类" |
| | | prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | | prop="specificationModel" /> |
| | | <el-table-column label="åä½" |
| | | prop="unit" /> |
| | | <el-table-column label="å
¥åºå®¡æ ¸ç¶æ" |
| | | prop="stockInApprovalStatus" |
| | | width="120"> |
| | | <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable"> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" /> |
| | | <el-table-column label="å
¥åºå®¡æ ¸ç¶æ" prop="stockInApprovalStatus" width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStockInApprovalStatusType(scope.row.stockInApprovalStatus)" |
| | | size="small"> |
| | | <el-tag :type="getStockInApprovalStatusType(scope.row.stockInApprovalStatus)" size="small"> |
| | | {{ scope.row.stockInApprovalStatus || '--' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ°é" |
| | | prop="quantity" /> |
| | | <el-table-column label="éå®åè´§æ°é" |
| | | prop="shippedQuantity" /> |
| | | <el-table-column label="å¯ç¨æ°é" |
| | | prop="availableQuality" /> |
| | | <el-table-column label="éè´§æ°é" |
| | | prop="returnQuantity" /> |
| | | <el-table-column label="ç¨ç(%)" |
| | | prop="taxRate" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="æ°é" prop="quantity" /> |
| | | <el-table-column label="å¯ç¨æ°é" prop="availableQuality" /> |
| | | <el-table-column label="éè´§æ°é" prop="returnQuality" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="éè´ååå·" |
| | | prop="purchaseContractNumber" |
| | | width="160" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | width="160" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ä¾åºååç§°" |
| | | prop="supplierName" |
| | | width="160" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="项ç®åç§°" |
| | | prop="projectName" |
| | | width="320" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="审æ¹ç¶æ" |
| | | prop="approvalStatus" |
| | | width="100" |
| | | show-overflow-tooltip> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="éè´ååå·" prop="purchaseContractNumber" width="160" show-overflow-tooltip /> |
| | | <el-table-column v-if="showSalesContractBinding" label="éå®ååå·" prop="salesContractNo" width="160" show-overflow-tooltip /> |
| | | <el-table-column label="ä¾åºååç§°" prop="supplierName" width="160" show-overflow-tooltip /> |
| | | <el-table-column label="项ç®åç§°" prop="projectName" width="320" show-overflow-tooltip /> |
| | | <el-table-column label="审æ¹ç¶æ" prop="approvalStatus" width="100" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" |
| | | size="small"> |
| | | <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small"> |
| | | {{ approvalStatusText[scope.row.approvalStatus] || 'æªç¥ç¶æ' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¥åºç¶æ" |
| | | prop="stockInStatus" |
| | | width="100" |
| | | show-overflow-tooltip> |
| | | <el-table-column label="å
¥åºç¶æ" prop="stockInStatus" width="100" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStockInStatusType(scope.row.stockInStatus)" |
| | | size="small"> |
| | | <el-tag :type="getStockInStatusType(scope.row.stockInStatus)" size="small"> |
| | | {{ scope.row.stockInStatus || '--' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¾è®¢æ¥æ" |
| | | prop="executionDate" |
| | | width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="仿¬¾æ¹å¼" |
| | | width="100" |
| | | prop="paymentMethod" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ååéé¢(å
)" |
| | | prop="contractAmount" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="仿¬¾æ¹å¼" width="100" prop="paymentMethod" show-overflow-tooltip /> |
| | | <el-table-column label="ååéé¢(å
)" prop="contractAmount" width="200" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="å®é
ååéé¢(å
)" |
| | | prop="netContractAmount" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | :formatter="(_, __, cellValue) => formatMoney(cellValue)" /> |
| | | <el-table-column label="å½å
¥äºº" |
| | | prop="recorderName" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="å½å
¥æ¥æ" |
| | | prop="entryDate" |
| | | width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="夿³¨" |
| | | prop="remarks" |
| | | width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | width="120" |
| | | align="center"> |
| | | <el-table-column label="å½å
¥äºº" prop="recorderName" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="å½å
¥æ¥æ" prop="entryDate" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="夿³¨" prop="remarks" width="200" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="æä½" width="220" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openForm('edit', scope.row)" |
| | | :disabled="scope.row.approvalStatus === 3">ç¼è¾ |
| | | <el-button link type="primary" @click="openForm('detail', scope.row)">详æ
|
| | | </el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openFileDialog(scope.row)">éä»¶</el-button> |
| | | <el-button link type="primary" @click="openForm('edit', scope.row)" |
| | | :disabled="scope.row.stockInStatus === 'å®å
¨å
¥åº'">ç¼è¾ |
| | | </el-button> |
| | | <el-button link type="primary" @click="openFileDialog(scope.row)">éä»¶</el-button> <!--downloadOrder(scope.row.id) ä¸è½½--> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | </div> |
| | | <FormDialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢éè´å°è´¦é¡µé¢' : 'ç¼è¾éè´å°è´¦é¡µé¢'" |
| | | :width="'70%'" |
| | | :operation-type="operationType" |
| | | @close="closeDia" |
| | | @confirm="submitForm" |
| | | @cancel="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | :title="operationType === 'add' ? 'æ°å¢éè´å°è´¦é¡µé¢' : operationType === 'edit' ? 'ç¼è¾éè´å°è´¦é¡µé¢' : 'éè´å°è´¦è¯¦æ
'" :width="'70%'" |
| | | :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ååå·ï¼" |
| | | prop="purchaseContractNumber"> |
| | | <el-input v-model="form.purchaseContractNumber" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-form-item label="éè´ååå·ï¼" prop="purchaseContractNumber"> |
| | | <el-input v-model="form.purchaseContractNumber" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" |
| | | prop="salesLedgerId"> |
| | | <el-select v-model="form.salesLedgerId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | @change="salesLedgerChange"> |
| | | <el-option v-for="item in salesContractList" |
| | | :key="item.id" |
| | | :label="item.salesContractNo" |
| | | :value="item.id" /> |
| | | <el-col v-if="showSalesContractBinding" :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" prop="salesLedgerId"> |
| | | <el-select v-model="form.salesLedgerId" placeholder="è¯·éæ©" filterable clearable @change="salesLedgerChange"> |
| | | <el-option v-for="item in salesContractList" :key="item.id" :label="item.salesContractNo" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºååç§°ï¼" |
| | | prop="supplierId"> |
| | | <el-select v-model="form.supplierId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable> |
| | | <el-option v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :value="item.id">{{ item.supplierName + '---' + item.supplierType }} |
| | | </el-option> |
| | | <el-form-item label="ä¾åºååç§°ï¼" prop="supplierId"> |
| | | <el-select v-model="form.supplierId" placeholder="è¯·éæ©" filterable clearable> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id">{{ item.supplierType ? item.supplierName + '---' + item.supplierType : item.supplierName }}</el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项ç®åç§°" |
| | | prop="projectName"> |
| | | <el-input v-model="form.projectName" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-form-item label="项ç®åç§°" prop="projectName"> |
| | | <el-input v-model="form.projectName" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼"> |
| | | <el-input v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-input v-model="form.paymentMethod" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¾è®¢æ¥æï¼" |
| | | prop="executionDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.executionDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | <el-form-item label="ç¾è®¢æ¥æï¼" prop="executionDate"> |
| | | <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥äººï¼" |
| | | prop="recorderId"> |
| | | <el-select v-model="form.recorderId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | <el-form-item label="å½å
¥äººï¼" prop="recorderId"> |
| | | <el-select v-model="form.recorderId" placeholder="è¯·éæ©" clearable filterable disabled> |
| | | <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥æ¥æï¼" |
| | | prop="entryDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | <el-form-item label="å½å
¥æ¥æï¼" prop="entryDate"> |
| | | <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-form-item label="产åä¿¡æ¯ï¼" |
| | | prop="entryDate"> |
| | | <el-button type="primary" |
| | | @click="openProductForm('add')">æ·»å |
| | | </el-button> |
| | | <el-button plain |
| | | type="danger" |
| | | @click="deleteProduct">å é¤ |
| | | </el-button> |
| | | <el-form-item label="产åä¿¡æ¯ï¼" prop="entryDate"> |
| | | <el-button type="primary" @click="openProductForm('add')">æ·»å </el-button> |
| | | <el-button plain type="danger" @click="deleteProduct">å é¤</el-button> |
| | | </el-form-item> |
| | | <div class="select-button-group" |
| | | style="width: 500px; margin: 20px 0;" |
| | | v-if="operationType === 'add'"> |
| | | <el-select filterable |
| | | allow-create |
| | | :reserve-keyword="true" |
| | | :default-first-option="false" |
| | | clearable |
| | | v-model="templateName" |
| | | :input-value="filterInputValue" |
| | | @filter-change="onTemplateFilterChange" |
| | | @change="onTemplateChange" |
| | | @focus="getTemplateList" |
| | | style="width: 500px;" |
| | | placeholder="è¯·éæ©æ¨¡çæè
è¾å
¥æ°ç模çåç§°åéæ©" |
| | | <div class="select-button-group" style="width: 500px; margin: 20px 0;" v-if="operationType === 'add'"> |
| | | <el-select filterable allow-create :reserve-keyword="true" :default-first-option="false" clearable |
| | | v-model="templateName" :input-value="filterInputValue" @filter-change="onTemplateFilterChange" |
| | | @change="onTemplateChange" @focus="getTemplateList" style="width: 500px;" placeholder="è¯·éæ©æ¨¡çæè
è¾å
¥æ°ç模çåç§°åéæ©" |
| | | class="no-arrow-select"> |
| | | <el-option v-for="item in templateList" |
| | | :key="item.id || item.value" |
| | | :label="item.templateName" |
| | | <el-option v-for="item in templateList" :key="item.id || item.value" :label="item.templateName" |
| | | :value="item.templateName"> |
| | | <div style="display: flex; justify-content: space-between; align-items: center;"> |
| | | <span>{{ item.templateName }}</span> |
| | | <el-icon v-if="item.id" |
| | | class="delete-icon" |
| | | @click.stop="handleDeleteTemplate(item)" |
| | | <el-icon v-if="item.id" class="delete-icon" @click.stop="handleDeleteTemplate(item)" |
| | | style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;"> |
| | | <Delete /> |
| | | </el-icon> |
| | |
| | | </el-option> |
| | | </el-select> |
| | | <!-- æé®ï¼ä¸ Select é«åº¦å¹é
ï¼å»æå·¦ä¾§è¾¹æ¡ï¼æ ç¼è¡æ¥ --> |
| | | <el-button size="small" |
| | | style="height: 32px;margin-left: 8px;" |
| | | @click="handleButtonClick" |
| | | <el-button size="small" style="height: 32px;margin-left: 8px;" @click="handleButtonClick" |
| | | :disabled="!templateName || templateName.trim() === '' || (!currentTemplateId && isTemplateNameDuplicate)"> |
| | | ä¿å |
| | | </el-button> |
| | | </div> |
| | | </el-row> |
| | | <el-table :data="productData" |
| | | border |
| | | @selection-change="productSelected" |
| | | show-summary |
| | | <el-table :data="productData" border @selection-change="productSelected" show-summary |
| | | :summary-method="summarizeProTable"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="产å大类" |
| | | prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | | prop="specificationModel" /> |
| | | <el-table-column label="åä½" |
| | | prop="unit" |
| | | width="70" /> |
| | | <el-table-column label="æ°é" |
| | | prop="quantity" |
| | | width="70" /> |
| | | <el-table-column label="åºåé¢è¦æ°é" |
| | | prop="warnNum" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | width="80" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | <el-table-column label="æ¯å¦è´¨æ£" |
| | | prop="isChecked" |
| | | width="150"> |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" width="70" /> |
| | | <el-table-column label="æ°é" prop="quantity" width="70" /> |
| | | <!-- <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="120" show-overflow-tooltip />--> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" width="80" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="150" /> |
| | | <el-table-column label="æ¯å¦è´¨æ£" prop="isChecked" width="150"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.isChecked ? 'success' : 'info'"> |
| | | {{ scope.row.isChecked ? 'æ¯' : 'å¦' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¥åºå®¡æ ¸ç¶æ" |
| | | prop="stockInApprovalStatus" |
| | | width="120"> |
| | | <el-table-column label="å
¥åºå®¡æ ¸ç¶æ" prop="stockInApprovalStatus" width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStockInApprovalStatusType(scope.row.stockInApprovalStatus)" |
| | | size="small"> |
| | | <el-tag :type="getStockInApprovalStatusType(scope.row.stockInApprovalStatus)" size="small"> |
| | | {{ scope.row.stockInApprovalStatus || '--' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | min-width="60" |
| | | align="center"> |
| | | <el-table-column fixed="right" label="æä½" min-width="60" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | <el-button link type="primary" |
| | | @click="openProductForm('edit', scope.row, scope.$index)" |
| | | :disabled="scope.row.stockInApprovalStatus === 'å®å
¨å
¥åº'">ç¼è¾ |
| | | </el-button> |
| | |
| | | </el-table> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨Â·ï¼" |
| | | prop="remarks"> |
| | | <el-input v-model="form.remarks" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | type="textarea" |
| | | :rows="2" /> |
| | | <el-form-item label="夿³¨Â·ï¼" prop="remarks"> |
| | | <el-input v-model="form.remarks" placeholder="请è¾å
¥" clearable type="textarea" :rows="2" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶ææï¼" |
| | | prop="purchaseLedgerFiles"> |
| | | <el-form-item label="éä»¶ææï¼" prop="purchaseLedgerFiles"> |
| | | <FileUpload v-model:file-list="fileList" /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </el-form> |
| | | </FormDialog> |
| | | <!-- 导å
¥å¼¹çª --> |
| | | <FormDialog v-model="importUpload.open" |
| | | :title="importUpload.title" |
| | | :width="'600px'" |
| | | @close="importUpload.open = false" |
| | | @confirm="submitImportFile" |
| | | @cancel="importUpload.open = false"> |
| | | <el-upload ref="importUploadRef" |
| | | :limit="1" |
| | | accept=".xlsx,.xls" |
| | | :action="importUpload.url" |
| | | :headers="importUpload.headers" |
| | | :before-upload="importUpload.beforeUpload" |
| | | :on-success="importUpload.onSuccess" |
| | | :on-error="importUpload.onError" |
| | | :on-progress="importUpload.onProgress" |
| | | :on-change="importUpload.onChange" |
| | | :auto-upload="false" |
| | | drag> |
| | | <FormDialog v-model="importUpload.open" :title="importUpload.title" :width="'600px'" |
| | | @close="importUpload.open = false" @confirm="submitImportFile" @cancel="importUpload.open = false"> |
| | | <el-upload ref="importUploadRef" :limit="1" accept=".xlsx,.xls" :action="importUpload.url" |
| | | :headers="importUpload.headers" :before-upload="importUpload.beforeUpload" :on-success="importUpload.onSuccess" |
| | | :on-error="importUpload.onError" :on-progress="importUpload.onProgress" :on-change="importUpload.onChange" |
| | | :auto-upload="false" drag> |
| | | <i class="el-icon-upload"></i> |
| | | <div class="el-upload__text"> |
| | | å°æä»¶æå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em> |
| | |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | | ä»
æ¯æ xls/xlsxï¼å¤§å°ä¸è¶
è¿ 10MBã |
| | | <el-button link |
| | | type="primary" |
| | | @click="downloadTemplate">ä¸è½½å¯¼å
¥æ¨¡æ¿</el-button> |
| | | <el-button link type="primary" @click="downloadTemplate">ä¸è½½å¯¼å
¥æ¨¡æ¿</el-button> |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </FormDialog> |
| | | <FormDialog v-model="productFormVisible" |
| | | :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" |
| | | :width="'40%'" |
| | | :operation-type="productOperationType" |
| | | @close="closeProductDia" |
| | | @confirm="submitProduct" |
| | | <FormDialog v-model="productFormVisible" :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" :width="'40%'" |
| | | :operation-type="productOperationType" @close="closeProductDia" @confirm="submitProduct" |
| | | @cancel="closeProductDia"> |
| | | <el-form :model="productForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="productRules" |
| | | ref="productFormRef"> |
| | | <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产å大类ï¼" |
| | | prop="productId"> |
| | | <el-tree-select v-model="productForm.productId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" /> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产å大类ï¼" prop="productCategory"> |
| | | <el-input v-model="productForm.productCategory" |
| | | placeholder="ç¹å»éæ©" |
| | | readonly |
| | | @click="showProductSelectDialog = true" |
| | | style="cursor:pointer" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="productModelId"> |
| | | <el-input v-model="productForm.specificationModel" |
| | | placeholder="ç¹å»éæ©" |
| | | readonly |
| | | @click="showProductSelectDialog = true" |
| | | style="cursor:pointer" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" |
| | | prop="productModelId"> |
| | | <el-select v-model="productForm.productModelId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | @change="getProductModel"> |
| | | <el-option v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" /> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input v-model="productForm.unit" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç(%)ï¼" prop="taxRate"> |
| | | <el-select v-model="productForm.taxRate" placeholder="è¯·éæ©" clearable @change="mathNum"> |
| | | <el-option v-for="dict in tax_rate" :key="dict.value" :label="dict.label" :value="dict.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" |
| | | prop="unit"> |
| | | <el-input v-model="productForm.unit" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-form-item label="å«ç¨åä»·(å
)ï¼" prop="taxInclusiveUnitPrice"> |
| | | <el-input-number v-model="productForm.taxInclusiveUnitPrice" :precision="6" :step="0.000001" :min="0" clearable |
| | | style="width: 100%" @change="mathNum" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç(%)ï¼" |
| | | prop="taxRate"> |
| | | <el-select v-model="productForm.taxRate" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | @change="mathNum"> |
| | | <el-option v-for="dict in tax_rate" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="dict.value" /> |
| | | <el-form-item label="æ°éï¼" prop="quantity"> |
| | | <el-input-number :step="0.1" clearable :precision="2" :min="0" style="width: 100%" |
| | | v-model="productForm.quantity" placeholder="请è¾å
¥" @change="mathNum" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨æ»ä»·(å
)ï¼" prop="taxInclusiveTotalPrice"> |
| | | <el-input-number v-model="productForm.taxInclusiveTotalPrice" :precision="6" :step="0.000001" :min="0" |
| | | clearable style="width: 100%" @change="reverseMathNum('taxInclusiveTotalPrice')" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸å«ç¨æ»ä»·(å
)ï¼" prop="taxExclusiveTotalPrice"> |
| | | <el-input-number v-model="productForm.taxExclusiveTotalPrice" :precision="6" :step="0.000001" :min="0" |
| | | clearable style="width: 100%" @change="reverseMathNum('taxExclusiveTotalPrice')" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票类åï¼" prop="invoiceType"> |
| | | <el-select v-model="productForm.invoiceType" placeholder="è¯·éæ©" clearable> |
| | | <el-option label="墿®ç¥¨" value="墿®ç¥¨" /> |
| | | <el-option label="å¢ä¸ç¥¨" value="å¢ä¸ç¥¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <!-- <el-col :span="12">--> |
| | | <!-- <el-form-item label="åºåé¢è¦æ°éï¼" prop="warnNum">--> |
| | | <!-- <el-input-number v-model="productForm.warnNum" :precision="2" :step="0.1" :min="0" clearable--> |
| | | <!-- style="width: 100%" />--> |
| | | <!-- </el-form-item>--> |
| | | <!-- </el-col>--> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨åä»·(å
)ï¼" |
| | | prop="taxInclusiveUnitPrice"> |
| | | <el-input-number v-model="productForm.taxInclusiveUnitPrice" |
| | | :precision="2" |
| | | :step="0.1" |
| | | :min="0" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="mathNum" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ°éï¼" |
| | | prop="quantity"> |
| | | <el-input-number :step="0.1" |
| | | clearable |
| | | :precision="2" |
| | | :min="0" |
| | | style="width: 100%" |
| | | v-model="productForm.quantity" |
| | | placeholder="请è¾å
¥" |
| | | @change="mathNum" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨æ»ä»·(å
)ï¼" |
| | | prop="taxInclusiveTotalPrice"> |
| | | <el-input-number v-model="productForm.taxInclusiveTotalPrice" |
| | | :precision="2" |
| | | :step="0.1" |
| | | :min="0" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="reverseMathNum('taxInclusiveTotalPrice')" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸å«ç¨æ»ä»·(å
)ï¼" |
| | | prop="taxExclusiveTotalPrice"> |
| | | <el-input-number v-model="productForm.taxExclusiveTotalPrice" |
| | | :precision="2" |
| | | :step="0.1" |
| | | :min="0" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="reverseMathNum('taxExclusiveTotalPrice')" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票类åï¼" |
| | | prop="invoiceType"> |
| | | <el-select v-model="productForm.invoiceType" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option label="墿®ç¥¨" |
| | | value="墿®ç¥¨" /> |
| | | <el-option label="å¢ä¸ç¥¨" |
| | | value="å¢ä¸ç¥¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåé¢è¦æ°éï¼" |
| | | prop="warnNum"> |
| | | <el-input-number v-model="productForm.warnNum" |
| | | :precision="2" |
| | | :step="0.1" |
| | | :min="0" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¯å¦è´¨æ£ï¼" |
| | | prop="isChecked"> |
| | | <el-form-item label="æ¯å¦è´¨æ£ï¼" prop="isChecked"> |
| | | <el-radio-group v-model="productForm.isChecked"> |
| | | <el-radio label="æ¯" |
| | | :value="true" /> |
| | | <el-radio label="å¦" |
| | | :value="false" /> |
| | | <el-radio label="æ¯" :value="true" /> |
| | | <el-radio label="å¦" :value="false" /> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¯å¦è´¨æ£ï¼" prop="isChecked"> |
| | | <el-radio-group v-model="productForm.isChecked"> |
| | | <el-radio label="æ¯" :value="true" /> |
| | | <el-radio label="å¦" :value="false" /> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row>--> |
| | | </el-form> |
| | | </FormDialog> |
| | | <FileList v-if="fileListDialogVisible" |
| | | v-model:visible="fileListDialogVisible" |
| | | record-type="purchase_ledger" |
| | | <ProductSelectDialog v-model="showProductSelectDialog" |
| | | @confirm="handleProductSelect" |
| | | single /> |
| | | <FileList v-if="fileListDialogVisible" v-model:visible="fileListDialogVisible" record-type="purchase_ledger" |
| | | :record-id="recordId" /> |
| | | <Detail ref="detailRef" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import FileListDialog from "@/components/Dialog/FileListDialog.vue"; |
| | | import Detail from "./detail.vue"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { |
| | |
| | | productList, |
| | | getPurchaseById, |
| | | getOptions, |
| | | getPrintData, |
| | | getPurchaseTemplateList, |
| | | delPurchaseTemplate, |
| | | batchGeneratePurchaseInboundSteps, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | const FileList = defineAsyncComponent(() => |
| | | import("@/components/Dialog/FileList.vue") |
| | | ); |
| | | |
| | | const detailRef = ref(null); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { tax_rate } = proxy.useDict("tax_rate"); |
| | |
| | | const fileList = ref([]); |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; |
| | | import dayjs from "dayjs"; |
| | | import FileUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | import { tableAmountFormatter, formatDecimal, buildAmountSummaryFormat } from '@/utils/numberFormat'; |
| | | import { downloadPurchaseJpg, printPurchaseMulti } from "@/utils/documentPrint.js"; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // æ¯å¦æ¾ç¤ºéå®ååå·ç»å® |
| | | const showSalesContractBinding = false; |
| | | |
| | | // 订å审æ¹ç¶ææ¾ç¤ºææ¬ |
| | | const approvalStatusText = { |
| | |
| | | // è·åå
¥åºç¶ææ ç¾ç±»å |
| | | const getStockInStatusType = status => { |
| | | const typeMap = { |
| | | å¾
å
¥åº: "info", // å¾
å
¥åº - ç°è² |
| | | å
¥åºä¸: "warning", // å
¥åºä¸ - æ©è² |
| | | å®å
¨å
¥åº: "success", // å®å
¨å
¥åº - ç»¿è² |
| | | "å¾
å
¥åº": "info", // å¾
å
¥åº - ç°è² |
| | | "å
¥åºä¸": "warning", // å
¥åºä¸ - æ©è² |
| | | "å®å
¨å
¥åº": "success", // å®å
¨å
¥åº - ç»¿è² |
| | | }; |
| | | return typeMap[status] || ""; |
| | | }; |
| | |
| | | // è·åå
¥åºå®¡æ ¸ç¶ææ ç¾ç±»å |
| | | const getStockInApprovalStatusType = status => { |
| | | const typeMap = { |
| | | å¾
å
¥åº: "info", // å¾
å
¥åº - ç°è² |
| | | å
¥åºä¸: "warning", // å
¥åºä¸ - æ©è² |
| | | å®å
¨å
¥åº: "success", // å®å
¨å
¥åº - ç»¿è² |
| | | "å¾
å
¥åº": "info", // å¾
å
¥åº - ç°è² |
| | | "å
¥åºä¸": "warning", // å
¥åºä¸ - æ©è² |
| | | "å®å
¨å
¥åº": "success", // å®å
¨å
¥åº - ç»¿è² |
| | | }; |
| | | return typeMap[status] || ""; |
| | | }; |
| | |
| | | ); |
| | | isTemplateNameDuplicate.value = isDuplicate; |
| | | return isDuplicate; |
| | | }; |
| | | |
| | | // 夿æä»¶æ¯å¦ä¸ºå¾ç |
| | | const isImage = (filename) => { |
| | | if (!filename) return false; |
| | | const ext = filename.substring(filename.lastIndexOf('.')).toLowerCase(); |
| | | return ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'].includes(ext); |
| | | }; |
| | | |
| | | // 鲿宿¶å¨ |
| | |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [ |
| | | { required: false, message: "请è¾å
¥", trigger: "blur" }, |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | projectName: [ |
| | | { required: true, message: "请è¾å
¥é¡¹ç®åç§°", trigger: "blur" }, |
| | |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData({ |
| | | ...data.searchForm, |
| | | // 设置å½å
¥æ¥æèå´ä¸ºå½å¤© |
| | | entryDate: [ |
| | | dayjs().startOf("day").format("YYYY-MM-DD"), |
| | | dayjs().endOf("day").format("YYYY-MM-DD"), |
| | | ], |
| | | entryDateStart: dayjs().startOf("day").format("YYYY-MM-DD"), |
| | | entryDateEnd: dayjs().endOf("day").format("YYYY-MM-DD"), |
| | | }); |
| | | |
| | | // 产å表åå¼¹æ¡æ°æ® |
| | | const productFormVisible = ref(false); |
| | | const showProductSelectDialog = ref(false); |
| | | const productOperationType = ref(""); |
| | | const productOperationIndex = ref(""); |
| | | const currentId = ref(""); |
| | |
| | | isChecked: false, |
| | | }, |
| | | productRules: { |
| | | productId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | productCategory: [{ required: true, message: "è¯·éæ©äº§å大类", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©è§æ ¼åå·", trigger: "change" }], |
| | | unit: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | warnNum: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | // warnNum: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | const formattedNumber = tableAmountFormatter; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | |
| | | "futureTickets", |
| | | "futureTicketsAmount", |
| | | ], |
| | | buildAmountSummaryFormat( |
| | | [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | "ticketsAmount", |
| | | "futureTicketsAmount", |
| | | ], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | ticketsNum: { noDecimal: true }, |
| | | futureTickets: { noDecimal: true }, |
| | | } |
| | | ) |
| | | ); |
| | | }; |
| | | const paginationChange = obj => { |
| | |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable(param, ["contractAmount", "netContractAmount"]); |
| | | }; |
| | | const formatMoney = value => { |
| | | if (value === null || value === undefined || value === "") { |
| | | return "--"; |
| | | } |
| | | const num = Number(value); |
| | | return Number.isFinite(num) ? num.toFixed(2) : "--"; |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["contractAmount"], |
| | | buildAmountSummaryFormat(["contractAmount"]) |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeProTable = param => { |
| | | return proxy.summarizeTable(param, [ |
| | | return proxy.summarizeTable( |
| | | param, |
| | | [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | ], |
| | | buildAmountSummaryFormat([ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]) |
| | | ); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = async (type, row) => { |
| | | if (type === 'detail') { |
| | | detailRef.value.open(row.id); |
| | | return; |
| | | } |
| | | // ç¼è¾æ¶æ£æ¥å
¥åºç¶æï¼å®å
¨å
¥åºæ¶ä¸è½ç¼è¾ |
| | | if (type === "edit" && row) { |
| | | if (row.stockInStatus === "å®å
¨å
¥åº") { |
| | | if (row.stockInStatus === 'å®å
¨å
¥åº') { |
| | | proxy.$modal.msgWarning("å®å
¨å
¥åºç¶æçè®°å½ä¸è½ç¼è¾"); |
| | | return; |
| | | } |
| | |
| | | ); |
| | | |
| | | form.value.entryDate = getCurrentDate(); |
| | | form.value.recorderId = userStore.id; |
| | | |
| | | if (type === "edit" && row?.id) { |
| | | if (type === "add") { |
| | | // æ°å¢æ¶çæéè´ååå· |
| | | try { |
| | | const purchaseNoRes = await createPurchaseNo(); |
| | | if (purchaseNoRes?.data) { |
| | | form.value.purchaseContractNumber = purchaseNoRes.data; |
| | | } |
| | | } catch (error) { |
| | | console.error("çæéè´ååå·å¤±è´¥:", error); |
| | | proxy.$modal.msgWarning("çæéè´ååå·å¤±è´¥"); |
| | | } |
| | | } else if (type === "edit" && row?.id) { |
| | | // ç¼è¾æ¶å è½½æ°æ® |
| | | currentId.value = row.id; |
| | | try { |
| | |
| | | form.value = { ...purchaseRes, stockInStatus: row.stockInStatus }; |
| | | fileList.value = purchaseRes.storageBlobVOS || []; |
| | | // ä½¿ç¨ productList æ¥å£è·å产åå表ï¼ä»¥è·åå
¥åºå®¡æ ¸ç¶æ |
| | | const productRes = await productList({ |
| | | salesLedgerId: row.id, |
| | | type: 2, |
| | | }); |
| | | const productRes = await productList({ salesLedgerId: row.id, type: 2 }); |
| | | productData.value = productRes.data || []; |
| | | } catch (error) { |
| | | console.error("å è½½éè´å°è´¦æ°æ®å¤±è´¥:", error); |
| | |
| | | if (form.value.salesLedgerId == -1) { |
| | | form.value.salesLedgerId = null; |
| | | } |
| | | console.log(form.value, "form.value==========="); |
| | | dialogFormVisible.value = true; |
| | | } catch (error) { |
| | | console.error("æå¼è¡¨å失败:", error); |
| | |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate(async valid => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | if (productData.value.length > 0) { |
| | | // æ°å¢æ¶ï¼éè¦ä»æ¯ä¸ªäº§å对象ä¸å é¤ id åæ®µ |
| | |
| | | delete submitData.id; |
| | | } |
| | | |
| | | // 妿éè´ååå·ä¸ºç©ºï¼åæ ¹æ®å½å
¥æ¥æèªå¨çæ |
| | | if (!submitData.purchaseContractNumber) { |
| | | try { |
| | | const purchaseNoRes = await createPurchaseNo(submitData.entryDate); |
| | | if (purchaseNoRes?.data) { |
| | | submitData.purchaseContractNumber = purchaseNoRes.data; |
| | | } |
| | | } catch (error) { |
| | | console.error("çæéè´ååå·å¤±è´¥:", error); |
| | | proxy.$modal.msgWarning("çæéè´ååå·å¤±è´¥"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | addOrEditPurchase(submitData).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProductForm = async (type, row, index) => { |
| | | // ç¼è¾æ¶æ£æ¥äº§åå
¥åºå®¡æ ¸ç¶æï¼å®å
¨å
¥åºæ¶ä¸è½ç¼è¾ |
| | | if (type === "edit" && row && row.stockInApprovalStatus === "å®å
¨å
¥åº") { |
| | | if (type === "edit" && row && row.stockInApprovalStatus === 'å®å
¨å
¥åº') { |
| | | proxy.$modal.msgWarning("å®å
¨å
¥åºç¶æç产åä¸è½ç¼è¾"); |
| | | return; |
| | | } |
| | |
| | | await nextTick(); |
| | | } |
| | | }; |
| | | |
| | | // ProductSelectDialog éä¸åè° |
| | | const handleProductSelect = (products) => { |
| | | if (!products || !products.length) return; |
| | | const p = products[0]; |
| | | productForm.value.productId = p.productId; |
| | | productForm.value.productCategory = p.productName || ''; |
| | | productForm.value.productModelId = p.id; |
| | | productForm.value.specificationModel = p.model || ''; |
| | | productForm.value.unit = p.unit || ''; |
| | | showProductSelectDialog.value = false; |
| | | }; |
| | | |
| | | const getProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | |
| | | }; |
| | | // å é¤äº§å |
| | | const deleteProduct = () => { |
| | | if (productData.value.length <= 1) { |
| | | proxy.$modal.msgWarning("éè´å°è´¦è³å°éè¦ä¿ç䏿¡äº§åæ°æ®"); |
| | | return; |
| | | } |
| | | if (productSelectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | // æ£æ¥éä¸ç产å䏿¯å¦æå®å
¨å
¥åºç |
| | | const hasFullyStocked = productSelectedRows.value.some( |
| | | row => row.stockInApprovalStatus === "å®å
¨å
¥åº" |
| | | row => row.stockInApprovalStatus === 'å®å
¨å
¥åº' |
| | | ); |
| | | if (hasFullyStocked) { |
| | | proxy.$modal.msgWarning("éä¸ç产åä¸å
å«å®å
¨å
¥åºç产åï¼æ æ³å é¤"); |
| | |
| | | }); |
| | | }; |
| | | |
| | | const handleBatchGenerate = async () => { |
| | | // æ¹éæå°éè´å - å¤éåå¹¶é¢è§ |
| | | const handlePrint = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | proxy.$modal.msgWarning("è¯·éæ©è¦æå°çæ°æ®"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map((item) => item.id); |
| | | |
| | | ElMessageBox.confirm("确认æ¹éçææ°æ®ï¼", "æ¹éçæ", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }) |
| | | .then(() => { |
| | | proxy.$modal.loading("æ£å¨æ¹éçææ°æ®ï¼è¯·ç¨å..."); |
| | | batchGeneratePurchaseInboundSteps({ ids }) |
| | | .then((res) => { |
| | | proxy.$modal.msgSuccess("æ¹éçææå"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError("æ¹éçæå¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | proxy.$modal.loading("æ£å¨å è½½æå°æ°æ®..."); |
| | | try { |
| | | const orders = []; |
| | | let companyName = ''; |
| | | for (const row of selectedRows.value) { |
| | | const res = await getPrintData(row.id); |
| | | const data = res.data || res; |
| | | companyName = data.companyName || companyName; |
| | | orders.push({ ledger: data.ledger, products: data.products }); |
| | | } |
| | | printPurchaseMulti(companyName, orders); |
| | | } catch (e) { |
| | | console.error('æå°å¤±è´¥:', e); |
| | | proxy.$modal.msgError("æå°å¤±è´¥"); |
| | | } finally { |
| | | proxy.$modal.closeLoading(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | |
| | | if (field === "taxInclusiveTotalPrice") { |
| | | // å·²ç¥å«ç¨æ»ä»·åæ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = ( |
| | | productForm.value.taxInclusiveUnitPrice = formatDecimal( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | Number(productForm.value.quantity) |
| | | ).toFixed(2); |
| | | ); |
| | | // ç¡®ä¿ç»æä¸ä¸ºè´æ° |
| | | if (Number(productForm.value.taxInclusiveUnitPrice) < 0) { |
| | | productForm.value.taxInclusiveUnitPrice = "0"; |
| | |
| | | } |
| | | } |
| | | // åç®ä¸å«ç¨æ»ä»· |
| | | productForm.value.taxExclusiveTotalPrice = ( |
| | | productForm.value.taxExclusiveTotalPrice = formatDecimal( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | (1 + taxRate / 100) |
| | | ).toFixed(2); |
| | | ); |
| | | // ç¡®ä¿ç»æä¸ä¸ºè´æ° |
| | | if (Number(productForm.value.taxExclusiveTotalPrice) < 0) { |
| | | productForm.value.taxExclusiveTotalPrice = "0"; |
| | | } |
| | | } else if (field === "taxExclusiveTotalPrice") { |
| | | // åç®å«ç¨æ»ä»· |
| | | productForm.value.taxInclusiveTotalPrice = ( |
| | | productForm.value.taxInclusiveTotalPrice = formatDecimal( |
| | | Number(productForm.value.taxExclusiveTotalPrice) * |
| | | (1 + taxRate / 100) |
| | | ).toFixed(2); |
| | | ); |
| | | // ç¡®ä¿ç»æä¸ä¸ºè´æ° |
| | | if (Number(productForm.value.taxInclusiveTotalPrice) < 0) { |
| | | productForm.value.taxInclusiveTotalPrice = "0"; |
| | | } |
| | | // å·²ç¥æ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = ( |
| | | productForm.value.taxInclusiveUnitPrice = formatDecimal( |
| | | Number(productForm.value.taxInclusiveTotalPrice) / |
| | | Number(productForm.value.quantity) |
| | | ).toFixed(2); |
| | | ); |
| | | // ç¡®ä¿ç»æä¸ä¸ºè´æ° |
| | | if (Number(productForm.value.taxInclusiveUnitPrice) < 0) { |
| | | productForm.value.taxInclusiveUnitPrice = "0"; |
| | |
| | | }; |
| | | // éå®ååéæ©æ¹åæ¹æ³ |
| | | const salesLedgerChange = async row => { |
| | | console.log("row", row); |
| | | var index = salesContractList.value.findIndex(item => item.id == row); |
| | | console.log("index", index); |
| | | if (index > -1) { |
| | | await querygProductInfoByContractNo(); |
| | | } |
| | |
| | | contractNo: form.value.salesLedgerId, |
| | | }); |
| | | if (code == 200) { |
| | | productData.value = data || []; |
| | | productData.value = data; |
| | | } |
| | | }; |
| | | |
| | |
| | | const openFileDialog = async row => { |
| | | recordId.value = row.id; |
| | | fileListDialogVisible.value = true; |
| | | }; |
| | | |
| | | // ä¸è½½éè´å |
| | | const downloadOrder = async (id) => { |
| | | try { |
| | | const res = await getPrintData(id); |
| | | const data = res.data || res; |
| | | downloadPurchaseJpg(data.companyName, data.ledger, data.products, 'éè´å_' + (data.ledger.purchaseContractNumber || id) + '.jpg'); |
| | | } catch (e) { |
| | | console.error('ä¸è½½å¤±è´¥:', e); |
| | | } |
| | | }; |
| | | |
| | | // æå°éè´å |
| | | const printOrder = async (id) => { |
| | | try { |
| | | const res = await getPrintData(id); |
| | | const data = res.data || res; |
| | | printPurchase(data.companyName, data.ledger, data.products); |
| | | } catch (e) { |
| | | console.error('æå°å¤±è´¥:', e); |
| | | } |
| | | }; |
| | | |
| | | // å 餿¨¡æ¿ |
| | |
| | | } |
| | | |
| | | .no-arrow-select { |
| | | --el-select-suffix-icon-color: transparent; /* éèé»è®¤ä¸æç®å¤´ */ |
| | | --el-select-suffix-icon-color: transparent; |
| | | /* éèé»è®¤ä¸æç®å¤´ */ |
| | | } |
| | | |
| | | .readonly-text { |
| | | display: inline-block; |
| | | width: 100%; |
| | | padding: 0 15px; |
| | | height: 32px; |
| | | line-height: 32px; |
| | | background-color: var(--el-fill-color-light); |
| | | border-radius: var(--el-border-radius-base); |
| | | color: var(--el-text-color-regular); |
| | | font-size: var(--el-font-size-base); |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .readonly-text-area { |
| | | width: 100%; |
| | | min-height: 52px; |
| | | padding: 5px 15px; |
| | | background-color: var(--el-fill-color-light); |
| | | border-radius: var(--el-border-radius-base); |
| | | color: var(--el-text-color-regular); |
| | | font-size: var(--el-font-size-base); |
| | | box-sizing: border-box; |
| | | white-space: pre-wrap; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .detail-file-list { |
| | | width: 100%; |
| | | padding: 10px 15px; |
| | | background-color: var(--el-fill-color-light); |
| | | border-radius: var(--el-border-radius-base); |
| | | } |
| | | |
| | | .detail-file-item { |
| | | line-height: 24px; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .detail-file-item:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .select-button-group { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æç´¢åºå --> |
| | | <el-card class="search-card" |
| | | shadow="never"> |
| | | <el-form :model="searchForm" |
| | | :inline="true" |
| | | class="search-form"> |
| | | <el-card class="search-card" shadow="never"> |
| | | <el-form :model="searchForm" :inline="true" class="search-form"> |
| | | <el-form-item label="计ååç§°"> |
| | | <el-input v-model="searchForm.planName" |
| | | placeholder="请è¾å
¥è®¡ååç§°" |
| | | clearable /> |
| | | <el-input v-model="searchForm.planName" placeholder="请è¾å
¥è®¡ååç§°" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ"> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="è¯·éæ©ç¶æ" |
| | | clearable |
| | | style="width: 150px"> |
| | | <el-option label="å¯ç¨" |
| | | value="active" /> |
| | | <el-option label="ç¦ç¨" |
| | | value="disabled" /> |
| | | <el-select v-model="searchForm.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 150px"> |
| | | <el-option label="å¯ç¨" value="active" /> |
| | | <el-option label="ç¦ç¨" value="disabled" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleSearch"> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | <el-button type="primary" @click="handleSearch"> |
| | | <el-icon><Search /></el-icon> |
| | | æç´¢ |
| | | </el-button> |
| | | <el-button @click="handleReset"> |
| | | <el-icon> |
| | | <Refresh /> |
| | | </el-icon> |
| | | <el-icon><Refresh /></el-icon> |
| | | éç½® |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- æä½æé® --> |
| | | <el-card class="table-card" |
| | | shadow="never"> |
| | | <el-card class="table-card" shadow="never"> |
| | | <div class="table-header"> |
| | | <div class="table-title">éè´è®¡åå表</div> |
| | | <div class="table-actions"> |
| | | <el-button type="primary" |
| | | @click="handleAdd"> |
| | | <el-icon> |
| | | <Plus /> |
| | | </el-icon> |
| | | <el-button type="primary" @click="handleAdd"> |
| | | <el-icon><Plus /></el-icon> |
| | | æ°å¢è®¡å |
| | | </el-button> |
| | | <el-button type="info" |
| | | @click="handleExport"> |
| | | <el-icon> |
| | | <Download /> |
| | | </el-icon> |
| | | <el-button type="info" @click="handleExport"> |
| | | <el-icon><Download /></el-icon> |
| | | å¯¼åº |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æ°æ®è¡¨æ ¼ --> |
| | | <el-table v-loading="loading" |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="tableData" |
| | | stripe |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="planName" |
| | | label="计ååç§°" |
| | | min-width="150" /> |
| | | <el-table-column prop="description" |
| | | label="æè¿°" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="formula" |
| | | label="计ç®å
¬å¼" |
| | | min-width="200" |
| | | show-overflow-tooltip> |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column prop="planName" label="计ååç§°" min-width="150" /> |
| | | <el-table-column prop="description" label="æè¿°" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="formula" label="计ç®å
¬å¼" min-width="200" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <el-tag type="info" |
| | | size="small">{{ row.formula }}</el-tag> |
| | | <el-tag type="info" size="small">{{ row.formula }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" |
| | | label="ç¶æ" |
| | | width="80" |
| | | align="center"> |
| | | <el-table-column prop="status" label="ç¶æ" width="80" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.status === 'active' ? 'success' : 'info'" |
| | | size="small"> |
| | | <el-tag :type="row.status === 'active' ? 'success' : 'info'" size="small"> |
| | | {{ row.status === 'active' ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="updateTime" |
| | | label="æåè®¡ç®æ¶é´" |
| | | width="160" /> |
| | | <el-table-column label="æä½" |
| | | width="200" |
| | | fixed="right" |
| | | align="center"> |
| | | <el-table-column prop="updateTime" label="æåè®¡ç®æ¶é´" width="160" /> |
| | | <el-table-column label="æä½" width="200" fixed="right" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="handleEdit(row)">ç¼è¾</el-button> |
| | | <el-button type="success" |
| | | link |
| | | @click="handleCalculate(row)">计ç®</el-button> |
| | | <el-button type="danger" |
| | | link |
| | | @click="handleDelete(row)">å é¤</el-button> |
| | | <el-button type="primary" link @click="handleEdit(row)">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleCalculate(row)">计ç®</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination v-model:current-page="pagination.current" |
| | | <el-pagination |
| | | v-model:current-page="pagination.current" |
| | | v-model:page-size="pagination.size" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" /> |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æ°å¢/ç¼è¾å¯¹è¯æ¡ --> |
| | | <FormDialog v-model="dialogVisible" |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | :title="dialogType === 'add' ? 'æ°å¢éè´è®¡å' : 'ç¼è¾éè´è®¡å'" |
| | | :width="'1000px'" |
| | | :operation-type="dialogType" |
| | | :close-on-click-modal="false" |
| | | @close="dialogVisible = false" |
| | | @confirm="handleSubmit" |
| | | @cancel="dialogVisible = false"> |
| | | @cancel="dialogVisible = false" |
| | | > |
| | | <div class="form-container"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <div class="form-section"> |
| | | <div class="section-title">åºæ¬ä¿¡æ¯</div> |
| | | <el-form ref="formRef" |
| | | <el-form |
| | | ref="formRef" |
| | | :model="formData" |
| | | :rules="formRules" |
| | | label-width="120px"> |
| | | label-width="120px" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¼ç " |
| | | prop="code"> |
| | | <el-input v-model="formData.code" |
| | | placeholder="ä¿ååèªå¨çæ" |
| | | disabled /> |
| | | <el-form-item label="ç¼ç " prop="code"> |
| | | <el-input v-model="formData.code" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åç§°" |
| | | prop="planName" |
| | | required> |
| | | <el-input v-model="formData.planName" |
| | | placeholder="请è¾å
¥è®¡ååç§°" /> |
| | | <el-form-item label="åç§°" prop="planName" required> |
| | | <el-input v-model="formData.planName" placeholder="请è¾å
¥è®¡ååç§°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="æè¿°" |
| | | prop="description"> |
| | | <el-input v-model="formData.description" |
| | | |
| | | <el-form-item label="æè¿°" prop="description"> |
| | | <el-input |
| | | v-model="formData.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥è®¡åæè¿°" /> |
| | | placeholder="请è¾å
¥è®¡åæè¿°" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æ" |
| | | prop="status"> |
| | | <el-select v-model="formData.status" |
| | | placeholder="è¯·éæ©ç¶æ" |
| | | style="width: 100%"> |
| | | <el-option label="å¯ç¨" |
| | | value="active" /> |
| | | <el-option label="ç¦ç¨" |
| | | value="disabled" /> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-select v-model="formData.status" placeholder="è¯·éæ©ç¶æ" style="width: 100%"> |
| | | <el-option label="å¯ç¨" value="active" /> |
| | | <el-option label="ç¦ç¨" value="disabled" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´" prop="createTime"> |
| | | <el-date-picker v-model="formCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" /> |
| | | <el-form-item label="æ¯å¦ç³»ç»é¢ç½®"> |
| | | <el-checkbox v-model="formData.isSystemPreset">æ¯</el-checkbox> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- 计ç®åæ° --> |
| | | <div class="form-section"> |
| | | <div class="section-title">计ç®åæ°</div> |
| | | <el-tabs v-model="activeTab" |
| | | class="param-tabs"> |
| | | <el-tab-pane label="鿱忰" |
| | | name="demand"> |
| | | <el-tabs v-model="activeTab" class="param-tabs"> |
| | | <el-tab-pane label="鿱忰" name="demand"> |
| | | <div class="checkbox-group"> |
| | | <el-checkbox v-model="formData.considerExistingStock">èèç°æåºå</el-checkbox> |
| | | <el-checkbox v-model="formData.warehouseControl">ä»åºè¿è¡MRPçæ§å¶</el-checkbox> |
| | |
| | | <el-checkbox v-model="formData.negativeStockAsDemand">è´åºåä½ä¸ºéæ±</el-checkbox> |
| | | </div> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="计ç®åæ°" |
| | | name="calculation"> |
| | | <el-tab-pane label="计ç®åæ°" name="calculation"> |
| | | <div class="checkbox-group"> |
| | | <el-checkbox v-model="formData.considerExistingStock">èèç°æåºå</el-checkbox> |
| | | <el-checkbox v-model="formData.warehouseControl">ä»åºè¿è¡MRPçæ§å¶</el-checkbox> |
| | |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | |
| | | <!-- æ±æ»åå¹¶é项 --> |
| | | <div class="form-section"> |
| | | <div class="section-title">æ±æ»åå¹¶é项</div> |
| | |
| | | <el-checkbox v-model="formData.summaryDemandDate">éæ±æ¥æ</el-checkbox> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 计ç®å
¬å¼ --> |
| | | <div class="form-section"> |
| | | <div class="section-title">计ç®å
¬å¼</div> |
| | | <div class="formula-input-section"> |
| | | <el-form-item label="计ç®å
¬å¼" |
| | | prop="formula" |
| | | required> |
| | | <el-input v-model="formData.formula" |
| | | <el-form-item label="计ç®å
¬å¼" prop="formula" required> |
| | | <el-input |
| | | v-model="formData.formula" |
| | | placeholder="ä¾å¦: é¢è®¡åºåºæ°é - ç°æåºå + å®å
¨åºå - é¢è®¡å
¥åºæ°é" |
| | | @input="validateFormula" /> |
| | | @input="validateFormula" |
| | | /> |
| | | </el-form-item> |
| | | <div class="formula-help"> |
| | | <el-text type="info" |
| | | size="small"> |
| | | <el-text type="info" size="small"> |
| | | æ¯æåéï¼é¢è®¡åºåºæ°éãç°æåºåãå®å
¨åºåãé¢è®¡å
¥åºæ°é |
| | | </el-text> |
| | | </div> |
| | |
| | | </div> |
| | | </div> |
| | | </FormDialog> |
| | | |
| | | <!-- 产åéæ©å¯¹è¯æ¡ --> |
| | | <FormDialog v-model="productSelectDialogVisible" |
| | | <FormDialog |
| | | v-model="productSelectDialogVisible" |
| | | title="éæ©äº§å" |
| | | :width="'800px'" |
| | | :close-on-click-modal="false" |
| | | @close="productSelectDialogVisible = false" |
| | | @confirm="handleConfirmProductSelection" |
| | | @cancel="productSelectDialogVisible = false"> |
| | | @cancel="productSelectDialogVisible = false" |
| | | > |
| | | <div class="product-select"> |
| | | <el-alert title="è¯·éæ©è¦è®¡ç®ç产å" |
| | | <el-alert |
| | | title="è¯·éæ©è¦è®¡ç®ç产å" |
| | | type="info" |
| | | :closable="false" |
| | | show-icon> |
| | | show-icon |
| | | > |
| | | <template #default> |
| | | <p>éæ©äº§ååï¼ç³»ç»å°æ ¹æ®å½å计ç®å
¬å¼å产ååºåæ
åµè¿è¡è®¡ç®ã</p> |
| | | </template> |
| | | </el-alert> |
| | | <el-table v-loading="productLoading" |
| | | |
| | | <el-table |
| | | v-loading="productLoading" |
| | | :data="productList" |
| | | @selection-change="handleProductSelectionChange" |
| | | stripe |
| | | border |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column type="selection" |
| | | width="55" /> |
| | | <el-table-column prop="productCategory" |
| | | label="产å大类" |
| | | min-width="150" /> |
| | | <el-table-column prop="specificationModel" |
| | | label="è§æ ¼åå·" |
| | | width="120" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="ç°æåºå" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="å®å
¨åºå" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="é¢è®¡åºåº" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="é¢è®¡å
¥åº" |
| | | width="100" |
| | | align="right" /> |
| | | style="width: 100%; margin-top: 20px;" |
| | | > |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column prop="productCategory" label="产å大类" min-width="150" /> |
| | | <el-table-column prop="specificationModel" label="产åè§æ ¼" width="120" /> |
| | | <el-table-column prop="inboundNum0" label="ç°æåºå" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="å®å
¨åºå" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="é¢è®¡åºåº" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum0" label="é¢è®¡å
¥åº" width="100" align="right" /> |
| | | </el-table> |
| | | </div> |
| | | </FormDialog> |
| | | |
| | | <!-- 计ç®ç»æå¯¹è¯æ¡ --> |
| | | <FormDialog v-model="calculateDialogVisible" |
| | | <FormDialog |
| | | v-model="calculateDialogVisible" |
| | | title="éè´è®¡ç®ç»æ" |
| | | :width="'1000px'" |
| | | :close-on-click-modal="false" |
| | | @close="calculateDialogVisible = false" |
| | | @confirm="handleCreatePurchaseOrder" |
| | | @cancel="calculateDialogVisible = false"> |
| | | @cancel="calculateDialogVisible = false" |
| | | > |
| | | <div class="calculate-result"> |
| | | <el-alert title="计ç®ç»æ" |
| | | <el-alert |
| | | title="计ç®ç»æ" |
| | | type="success" |
| | | :closable="false" |
| | | show-icon> |
| | | show-icon |
| | | > |
| | | <template #default> |
| | | <p>åºäºå½åé
ç½®ç计ç®å
¬å¼ååºåæ
åµï¼ç³»ç»å·²è®¡ç®åºå产åçéè´éæ±ã</p> |
| | | </template> |
| | | </el-alert> |
| | | <el-table :data="calculateResult" |
| | | stripe |
| | | border |
| | | style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="productCategory" |
| | | label="产å大类" |
| | | min-width="150" /> |
| | | <el-table-column prop="specificationModel" |
| | | label="è§æ ¼åå·" |
| | | width="120" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="ç°æåºå" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="å®å
¨åºå" |
| | | width="100" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum" |
| | | label="é¢è®¡åºåºæ°é" |
| | | width="120" |
| | | align="right" /> |
| | | <el-table-column prop="inboundNum0" |
| | | label="é¢è®¡å
¥åºæ°é" |
| | | width="120" |
| | | align="right" /> |
| | | <el-table-column prop="weeklyNetDemand" |
| | | label="æå¨åéæ±" |
| | | width="120" |
| | | align="right"> |
| | | |
| | | <el-table :data="calculateResult" stripe border style="width: 100%; margin-top: 20px;"> |
| | | <el-table-column prop="productCategory" label="产å大类" min-width="150" /> |
| | | <el-table-column prop="specificationModel" label="产åè§æ ¼" width="120" /> |
| | | <el-table-column prop="inboundNum0" label="ç°æåºå" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="å®å
¨åºå" width="100" align="right" /> |
| | | <el-table-column prop="inboundNum" label="é¢è®¡åºåºæ°é" width="120" align="right" /> |
| | | <el-table-column prop="inboundNum0" label="é¢è®¡å
¥åºæ°é" width="120" align="right" /> |
| | | <el-table-column prop="weeklyNetDemand" label="æå¨åéæ±" width="120" align="right"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.weeklyNetDemand > 0 ? 'warning' : 'success'" |
| | | size="small"> |
| | | <el-tag :type="row.weeklyNetDemand > 0 ? 'warning' : 'success'" size="small"> |
| | | {{ row.weeklyNetDemand }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="suggestedPurchase" |
| | | label="建议éè´" |
| | | width="100" |
| | | align="right"> |
| | | <el-table-column prop="suggestedPurchase" label="建议éè´" width="100" align="right"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.suggestedPurchase > 0 ? 'danger' : 'success'" |
| | | size="small"> |
| | | <el-tag :type="row.suggestedPurchase > 0 ? 'danger' : 'success'" size="small"> |
| | | {{ row.suggestedPurchase }} |
| | | </el-tag> |
| | | </template> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { ref, reactive, onMounted, getCurrentInstance, computed } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Search, Refresh, Plus, Download } from "@element-plus/icons-vue"; |
| | | import { |
| | | listPage, |
| | | add, |
| | | update, |
| | | del, |
| | | listPageCopy, |
| | | } from "@/api/procurementManagement/procurementPlan.js"; |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import {ref, reactive, onMounted, getCurrentInstance} from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Search, Refresh, Plus, Download } from '@element-plus/icons-vue' |
| | | import {listPage,add,update,del,listPageCopy} from "@/api/procurementManagement/procurementPlan.js" |
| | | |
| | | // ååºå¼æ°æ® |
| | | const loading = ref(false); |
| | | const submitLoading = ref(false); |
| | | const dialogVisible = ref(false); |
| | | const productSelectDialogVisible = ref(false); |
| | | const calculateDialogVisible = ref(false); |
| | | const dialogType = ref("add"); |
| | | const productLoading = ref(false); |
| | | const selectedProducts = ref([]); |
| | | const currentPlan = ref(null); |
| | | const loading = ref(false) |
| | | const submitLoading = ref(false) |
| | | const dialogVisible = ref(false) |
| | | const productSelectDialogVisible = ref(false) |
| | | const calculateDialogVisible = ref(false) |
| | | const dialogType = ref('add') |
| | | const productLoading = ref(false) |
| | | const selectedProducts = ref([]) |
| | | const currentPlan = ref(null) |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | planName: "", |
| | | status: "", |
| | | }); |
| | | planName: '', |
| | | status: '' |
| | | }) |
| | | |
| | | // åé¡µæ°æ® |
| | | const pagination = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | | }); |
| | | size: 20 |
| | | }) |
| | | |
| | | // è¡¨åæ°æ® |
| | | const formData = reactive({ |
| | | code: "", |
| | | planName: "", |
| | | description: "", |
| | | status: "", |
| | | code: '', |
| | | planName: '', |
| | | description: '', |
| | | status: '', |
| | | isSystemPreset: false, |
| | | formula: "", |
| | | createTime: "", |
| | | formula: '', |
| | | // 计ç®åæ° |
| | | considerExistingStock: false, |
| | | warehouseControl: false, |
| | |
| | | // æ±æ»åå¹¶é项 |
| | | summaryMaterial: false, |
| | | summaryAuxAttributes: false, |
| | | summaryDemandDate: false, |
| | | }); |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (formData.createTime ? String(formData.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | formData.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | summaryDemandDate: false |
| | | }) |
| | | |
| | | // å½åæ¿æ´»çæ ç¾é¡µ |
| | | const activeTab = ref("demand"); |
| | | const activeTab = ref('demand') |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | | planName: [{ required: true, message: "请è¾å
¥è®¡ååç§°", trigger: "blur" }], |
| | | status: [{ required: true, message: "è¯·éæ©ç¶æ", trigger: "change" }], |
| | | formula: [{ required: true, message: "请è¾å
¥è®¡ç®å
¬å¼", trigger: "blur" }], |
| | | }; |
| | | planName: [ |
| | | { required: true, message: '请è¾å
¥è®¡ååç§°', trigger: 'blur' } |
| | | ], |
| | | status: [ |
| | | { required: true, message: 'è¯·éæ©ç¶æ', trigger: 'change' } |
| | | ], |
| | | formula: [ |
| | | { required: true, message: '请è¾å
¥è®¡ç®å
¬å¼', trigger: 'blur' } |
| | | ] |
| | | } |
| | | |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = ref([]); |
| | | const tableData = ref([]) |
| | | |
| | | // 产ååè¡¨æ°æ® |
| | | const productList = ref([ |
| | | { |
| | | id: 4, |
| | | productName: "产åD", |
| | | productCode: "PD004", |
| | | productName: '产åD', |
| | | productCode: 'PD004', |
| | | existingStock: 90, |
| | | safetyStock: 40, |
| | | expectedOutbound: 160, |
| | | expectedInbound: 35, |
| | | }, |
| | | ]); |
| | | expectedInbound: 35 |
| | | } |
| | | ]) |
| | | |
| | | // 计ç®ç»ææ°æ® |
| | | const calculateResult = ref([ |
| | | { |
| | | productName: "产åA", |
| | | productName: '产åA', |
| | | existingStock: 100, |
| | | safetyStock: 50, |
| | | expectedOutbound: 200, |
| | | expectedInbound: 30, |
| | | weeklyNetDemand: 120, |
| | | suggestedPurchase: 150, |
| | | suggestedPurchase: 150 |
| | | }, |
| | | { |
| | | productName: "产åB", |
| | | productName: '产åB', |
| | | existingStock: 80, |
| | | safetyStock: 30, |
| | | expectedOutbound: 150, |
| | | expectedInbound: 20, |
| | | weeklyNetDemand: 100, |
| | | suggestedPurchase: 120, |
| | | }, |
| | | ]); |
| | | const total = ref(0); |
| | | suggestedPurchase: 120 |
| | | } |
| | | ]) |
| | | const total = ref(0) |
| | | // æ¹æ³ |
| | | const handleSearch = () => { |
| | | pagination.current = 1; |
| | | loadData(); |
| | | }; |
| | | pagination.current = 1 |
| | | loadData() |
| | | } |
| | | |
| | | const handleReset = () => { |
| | | Object.assign(searchForm, { |
| | | planName: "", |
| | | status: "", |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | planName: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | |
| | | const loadData = () => { |
| | | loading.value = true; |
| | | loading.value = true |
| | | listPage({ ...searchForm, ...pagination }).then(res => { |
| | | if (res.code === 200) { |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | loading.value = false; |
| | | tableData.value = res.data.records |
| | | total.value = res.data.total |
| | | loading.value = false |
| | | } |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | const handleAdd = () => { |
| | | dialogType.value = "add"; |
| | | resetForm(); |
| | | formData.createTime = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | dialogVisible.value = true; |
| | | }; |
| | | dialogType.value = 'add' |
| | | resetForm() |
| | | // èªå¨çæç¼ç |
| | | formData.code = 'CGJH' + String(Date.now()).slice(-4) |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | | const handleEdit = row => { |
| | | dialogType.value = "edit"; |
| | | Object.assign(formData, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | const handleEdit = (row) => { |
| | | dialogType.value = 'edit' |
| | | Object.assign(formData, row) |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | | const handleDelete = async row => { |
| | | const handleDelete = async (row) => { |
| | | try { |
| | | await ElMessageBox.confirm("ç¡®å®è¦å é¤è¿ä¸ªéè´è®¡ååï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }); |
| | | let ids = [row.id]; |
| | | await ElMessageBox.confirm('ç¡®å®è¦å é¤è¿ä¸ªéè´è®¡ååï¼', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }) |
| | | let ids = [row.id] |
| | | del(ids).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("å 餿å"); |
| | | loadData(); |
| | | ElMessage.success('å 餿å') |
| | | loadData() |
| | | } |
| | | }); |
| | | }) |
| | | } catch { |
| | | // ç¨æ·åæ¶å é¤ |
| | | } |
| | | }; |
| | | } |
| | | |
| | | const handleSubmit = async () => { |
| | | try { |
| | | // 表åéªè¯ |
| | | if (!formData.planName || !formData.formula) { |
| | | ElMessage.error("请填åå¿
填项"); |
| | | return; |
| | | ElMessage.error('请填åå¿
填项') |
| | | return |
| | | } |
| | | |
| | | submitLoading.value = true; |
| | | submitLoading.value = true |
| | | |
| | | if (dialogType.value === "add") { |
| | | if (dialogType.value === 'add') { |
| | | add(formData).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("æ°å¢æå"); |
| | | dialogVisible.value = false; |
| | | loadData(); |
| | | ElMessage.success('æ°å¢æå') |
| | | dialogVisible.value = false |
| | | loadData() |
| | | } |
| | | }); |
| | | }) |
| | | } else { |
| | | // ç¼è¾ |
| | | update(formData).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("ç¼è¾æå"); |
| | | dialogVisible.value = false; |
| | | loadData(); |
| | | ElMessage.success('ç¼è¾æå') |
| | | dialogVisible.value = false |
| | | loadData() |
| | | } |
| | | }); |
| | | }) |
| | | } |
| | | |
| | | |
| | | } catch (error) { |
| | | ElMessage.error("æä½å¤±è´¥"); |
| | | ElMessage.error('æä½å¤±è´¥') |
| | | } finally { |
| | | submitLoading.value = false; |
| | | submitLoading.value = false |
| | | } |
| | | }; |
| | | } |
| | | |
| | | const resetForm = () => { |
| | | Object.assign(formData, { |
| | | code: "", |
| | | planName: "", |
| | | description: "", |
| | | status: "", |
| | | code: '', |
| | | planName: '', |
| | | description: '', |
| | | status: '', |
| | | isSystemPreset: false, |
| | | formula: "é¢è®¡åºåºæ°é - ç°æåºå + å®å
¨åºå - é¢è®¡å
¥åºæ°é", |
| | | formula: 'é¢è®¡åºåºæ°é - ç°æåºå + å®å
¨åºå - é¢è®¡å
¥åºæ°é', |
| | | // 计ç®åæ° |
| | | considerExistingStock: false, |
| | | warehouseControl: false, |
| | |
| | | // æ±æ»åå¹¶é项 |
| | | summaryMaterial: false, |
| | | summaryAuxAttributes: false, |
| | | summaryDemandDate: false, |
| | | }); |
| | | activeTab.value = "demand"; |
| | | }; |
| | | summaryDemandDate: false |
| | | }) |
| | | activeTab.value = 'demand' |
| | | } |
| | | |
| | | const validateFormula = () => { |
| | | // ç®åçå
¬å¼éªè¯ |
| | | const formula = formData.formula; |
| | | const formula = formData.formula |
| | | if (formula && !/^[a-zA-Z\u4e00-\u9fa5\s\*\+\-\/\(\)\d\.]+$/.test(formula)) { |
| | | ElMessage.warning("å
¬å¼æ ¼å¼å¯è½ä¸æ£ç¡®ï¼è¯·æ£æ¥"); |
| | | ElMessage.warning('å
¬å¼æ ¼å¼å¯è½ä¸æ£ç¡®ï¼è¯·æ£æ¥') |
| | | } |
| | | }; |
| | | } |
| | | |
| | | const handleCalculate = row => { |
| | | currentPlan.value = row; |
| | | productSelectDialogVisible.value = true; |
| | | loadProductList(); |
| | | }; |
| | | const handleCalculate = (row) => { |
| | | currentPlan.value = row |
| | | productSelectDialogVisible.value = true |
| | | loadProductList() |
| | | } |
| | | |
| | | const loadProductList = () => { |
| | | productLoading.value = true; |
| | | productLoading.value = true |
| | | // 模æå è½½äº§åæ°æ® |
| | | listPageCopy({ size: -1 }).then(res => { |
| | | if (res.code === 200) { |
| | | productList.value = res.data.records; |
| | | productLoading.value = false; |
| | | productList.value = res.data.records |
| | | productLoading.value = false |
| | | } |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | const handleProductSelectionChange = selection => { |
| | | selectedProducts.value = selection; |
| | | }; |
| | | const handleProductSelectionChange = (selection) => { |
| | | selectedProducts.value = selection |
| | | } |
| | | |
| | | const handleConfirmProductSelection = () => { |
| | | if (selectedProducts.value.length === 0) { |
| | | ElMessage.warning("è¯·éæ©è¦è®¡ç®ç产å"); |
| | | return; |
| | | ElMessage.warning('è¯·éæ©è¦è®¡ç®ç产å') |
| | | return |
| | | } |
| | | |
| | | ElMessage.success(`æ£å¨è®¡ç® ${currentPlan.value.planName} çéè´éæ±...`); |
| | | productSelectDialogVisible.value = false; |
| | | ElMessage.success(`æ£å¨è®¡ç® ${currentPlan.value.planName} çéè´éæ±...`) |
| | | productSelectDialogVisible.value = false |
| | | |
| | | // æ ¹æ®éæ©ç产åå计ç®å
¬å¼è¿è¡è®¡ç® |
| | | calculateWithSelectedProducts(); |
| | | }; |
| | | calculateWithSelectedProducts() |
| | | } |
| | | |
| | | const calculateWithSelectedProducts = () => { |
| | | // 模æè®¡ç®è¿ç¨ |
| | |
| | | const result = selectedProducts.value.map(product => { |
| | | // è¿éåºè¯¥æ ¹æ®å®é
ç计ç®å
¬å¼è¿è¡è®¡ç® |
| | | // 示ä¾ï¼é¢è®¡åºåºæ°é - ç°æåºå + å®å
¨åºå - é¢è®¡å
¥åºæ°é |
| | | const weeklyNetDemand = |
| | | product.inboundNum - |
| | | product.inboundNum0 + |
| | | product.inboundNum - |
| | | product.inboundNum0; |
| | | const suggestedPurchase = Math.max(0, weeklyNetDemand); |
| | | const weeklyNetDemand = product.inboundNum - product.inboundNum0 + product.inboundNum - product.inboundNum0 |
| | | const suggestedPurchase = Math.max(0, weeklyNetDemand) |
| | | |
| | | return { |
| | | productCategory: product.productCategory, |
| | |
| | | inboundNum0: product.inboundNum0, |
| | | inboundNum: product.inboundNum, |
| | | weeklyNetDemand: weeklyNetDemand, |
| | | suggestedPurchase: suggestedPurchase, |
| | | }; |
| | | }); |
| | | suggestedPurchase: suggestedPurchase |
| | | } |
| | | }) |
| | | |
| | | calculateResult.value = result; |
| | | calculateDialogVisible.value = true; |
| | | }; |
| | | calculateResult.value = result |
| | | calculateDialogVisible.value = true |
| | | } |
| | | |
| | | |
| | | const handleCreatePurchaseOrder = () => { |
| | | calculateDialogVisible.value = false; |
| | | }; |
| | | calculateDialogVisible.value = false |
| | | } |
| | | const { proxy } = getCurrentInstance(); |
| | | const handleExport = () => { |
| | | ElMessageBox.confirm("å
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | } |
| | | |
| | | const handleSizeChange = size => { |
| | | pagination.size = size; |
| | | loadData(); |
| | | }; |
| | | |
| | | const handleCurrentChange = current => { |
| | | pagination.current = current; |
| | | loadData(); |
| | | }; |
| | | const handleSizeChange = (size) => { |
| | | pagination.size = size |
| | | loadData() |
| | | } |
| | | |
| | | const handleCurrentChange = (current) => { |
| | | pagination.current = current |
| | | loadData() |
| | | } |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | loadData(); |
| | | }); |
| | | loadData() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | import { procurementBusinessSummaryListPage } from '@/api/procurementManagement/procurementReport' |
| | | import { productTreeList } from '@/api/basicData/product' |
| | | import { formatDecimal, formatCurrency } from '@/utils/numberFormat' |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | |
| | | prop: 'returnAmount', |
| | | width: 120, |
| | | formatData: (val) => { |
| | | return val ? `Â¥${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : 'Â¥0.00' |
| | | return val ? formatCurrency(val) : 'Â¥0' |
| | | } |
| | | }, |
| | | { |
| | | label: '鿬¾å¨é', |
| | | prop: 'purchaseAmount', |
| | | formatData: (val) => { |
| | | return val ? `Â¥${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : 'Â¥0.00' |
| | | return val ? formatCurrency(val) : 'Â¥0' |
| | | } |
| | | }, |
| | | { |
| | |
| | | prop: 'averagePrice', |
| | | width: 120, |
| | | formatData: (val) => { |
| | | return val ? `Â¥${parseFloat(val).toFixed(2)}` : 'Â¥0.00' |
| | | return val ? formatCurrency(val) : 'Â¥0' |
| | | } |
| | | }, |
| | | { |
| | |
| | | <el-option label="ä¾åºåB" value="ä¾åºåB" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´"> |
| | | <el-date-picker v-model="formCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨"> |
| | | <el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨ä¿¡æ¯" /> |
| | | </el-form-item> |
| | |
| | | |
| | | <script setup> |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import { ref, reactive, computed } from 'vue' |
| | | import dayjs from 'dayjs' |
| | | import { ref, reactive } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | const loading = ref(false) |
| | |
| | | |
| | | const formData = reactive({ |
| | | supplierName: '', |
| | | remark: '', |
| | | createTime: '' |
| | | }) |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (formData.createTime ? String(formData.createTime).split(' ')[0] : ''), |
| | | set: (value) => { |
| | | formData.createTime = value ? `${value} ${dayjs().format('HH:mm:ss')}` : '' |
| | | } |
| | | remark: '' |
| | | }) |
| | | |
| | | const mockData = [ |
| | |
| | | const openDialog = (type, row = {}) => { |
| | | dialogType.value = type |
| | | if (type === 'edit' && row.id) { |
| | | Object.assign(formData, { supplierName: row.supplierName, remark: row.remark, createTime: row.createTime || '' }) |
| | | Object.assign(formData, { supplierName: row.supplierName, remark: row.remark }) |
| | | } else { |
| | | Object.assign(formData, { supplierName: '', remark: '', createTime: dayjs().format('YYYY-MM-DD HH:mm:ss') }) |
| | | Object.assign(formData, { supplierName: '', remark: '' }) |
| | | } |
| | | dialogVisible.value = true |
| | | } |
| | |
| | | if (dialogType.value === 'add') { |
| | | const newOrder = { |
| | | id: Date.now(), |
| | | orderNo: '', |
| | | orderNo: `PO${Date.now()}`, |
| | | supplierName: formData.supplierName, |
| | | status: 'draft', |
| | | totalAmount: 0, |
| | | createTime: formData.createTime, |
| | | createTime: new Date().toLocaleString(), |
| | | remark: formData.remark |
| | | } |
| | | tableData.value.unshift(newOrder) |
| | |
| | | </el-form-item> |
| | | |
| | | <el-form-item |
| | | label="åè´§ç±»å" |
| | | prop="shippingType" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©åè´§ç±»å', |
| | | trigger: 'change', |
| | | } |
| | | ]" |
| | | > |
| | | <el-select |
| | | v-model="formState.shippingType" |
| | | placeholder="è¯·éæ©åè´§ç±»å" |
| | | style="width: 240px" |
| | | @change="handleShippingTypeChange" |
| | | > |
| | | <el-option label="货车" :value="1" /> |
| | | <el-option label="å¿«é" :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item |
| | | v-if="formState.shippingType === 1" |
| | | label="å货车çå·" |
| | | prop="truckPlateNo" |
| | | > |
| | | <el-input |
| | | v-model="formState.truckPlateNo" |
| | | placeholder="请è¾å
¥å货车çå·" |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <template v-else-if="formState.shippingType === 2"> |
| | | <el-form-item |
| | | label="å¿«éå
¬å¸" |
| | | prop="expressCompany" |
| | | > |
| | | <el-input |
| | | v-model="formState.expressCompany" |
| | | placeholder="请è¾å
¥å¿«éå
¬å¸" |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item |
| | | label="å¿«éåå·" |
| | | prop="expressNo" |
| | | > |
| | | <el-input |
| | | v-model="formState.expressNo" |
| | | placeholder="请è¾å
¥å¿«éåå·" |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | </template> |
| | | |
| | | <el-form-item |
| | | label="ä¾åºååç§°" |
| | | prop="supplierId" |
| | | :rules="[ |
| | |
| | | <el-table-column label="æ°é" |
| | | prop="stockInNum" |
| | | width="100" /> |
| | | <el-table-column label="éå®åè´§æ°é" |
| | | prop="saleOutQuantity" |
| | | width="120" /> |
| | | <el-table-column label="å¯éè´§æ°é" |
| | | prop="unQuantity" |
| | | width="130" /> |
| | | <el-table-column label="å·²éè´§æ°é" |
| | | width="130"> |
| | | <template #default="scope"> |
| | | {{ formattedNumber(scope.row, null, scope.row.totalReturnNum || 0) }} |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éè´§æ°é" |
| | |
| | | <script setup> |
| | | import {ref, computed, getCurrentInstance, watch, defineAsyncComponent} from "vue"; |
| | | import {createPurchaseReturnOrder} from "@/api/procurementManagement/purchase_return_order.js"; |
| | | import {getOptions, purchaseReturnableList} from "@/api/procurementManagement/procurementLedger.js"; |
| | | import {getOptions, purchaseList} from "@/api/procurementManagement/procurementLedger.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | const ProductList = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/ProductList.vue")); |
| | | const props = defineProps({ |
| | |
| | | no: '', |
| | | isDefaultNo: true, |
| | | returnType: 0, |
| | | shippingType: undefined, |
| | | truckPlateNo: '', |
| | | expressCompany: '', |
| | | expressNo: '', |
| | | incomeType: undefined, |
| | | remark: '', |
| | | supplierId: undefined, |
| | |
| | | // æ¯å¦å±ç¤ºäº§ååè¡¨æ°æ® |
| | | const isShowProductsModal = ref(false) |
| | | |
| | | const handleShippingTypeChange = (val) => { |
| | | if (val === 1) { |
| | | formState.value.expressCompany = ''; |
| | | formState.value.expressNo = ''; |
| | | } else if (val === 2) { |
| | | formState.value.truckPlateNo = ''; |
| | | } else { |
| | | formState.value.truckPlateNo = ''; |
| | | formState.value.expressCompany = ''; |
| | | formState.value.expressNo = ''; |
| | | } |
| | | }; |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | |
| | | const toNumber = (val) => { |
| | | const num = Number(val) |
| | | return Number.isNaN(num) ? 0 : num |
| | | } |
| | | |
| | | /** å·²éè´§æ°é = å
¥åºè¡æ»æ°é â å½åå¯éè´§æ°éï¼å©ä½ï¼ */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0) |
| | | const un = Number(row?.unQuantity ?? 0) |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0 |
| | | return Math.max(total - un, 0) |
| | | } |
| | | |
| | | const getReturnTotal = (row) => { |
| | |
| | | } |
| | | |
| | | const closeModal = () => { |
| | | formState.value.shippingType = undefined; |
| | | formState.value.truckPlateNo = ''; |
| | | formState.value.expressCompany = ''; |
| | | formState.value.expressNo = ''; |
| | | isShow.value = false; |
| | | }; |
| | | |
| | |
| | | const fetchPurchaseLedgerOptions = () => { |
| | | purchaseLedgerOptions.value = [] |
| | | if (formState.value.supplierId) { |
| | | purchaseReturnableList({ supplierId: formState.value.supplierId }).then((res) => { |
| | | purchaseList({supplierId: formState.value.supplierId,approvalStatus:3}).then((res) => { |
| | | purchaseLedgerOptions.value = res.rows; |
| | | }); |
| | | } |
| | |
| | | <el-table-column label="æ°é" |
| | | prop="stockInNum" |
| | | width="70" /> |
| | | <el-table-column label="éå®åè´§æ°é" |
| | | prop="saleOutQuantity" |
| | | width="120" /> |
| | | <el-table-column label="å¯éè´§æ°é" |
| | | prop="unQuantity" |
| | | width="130" /> |
| | | <el-table-column label="å·²éè´§æ°é" |
| | | width="130"> |
| | | <template #default="scope"> |
| | | {{ formattedNumber(scope.row, null, scope.row.totalReturnNum || 0) }} |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="åºåé¢è¦æ°é" |
| | |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | |
| | | /** å·²éè´§æ°é = å
¥åºè¡æ»æ°é â å½åå¯éè´§æ°éï¼å©ä½ï¼ */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0) |
| | | const un = Number(row?.unQuantity ?? 0) |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0 |
| | | return Math.max(total - un, 0) |
| | | } |
| | | |
| | | const handleChangeSelection = (val) => { |
| | | selectedRows.value = val; |
| | | } |
| | |
| | | @click="handleDetail(row)" |
| | | >详æ
</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | size="small" |
| | | :disabled="row.stockOutApproved" |
| | | :style="{ color: row.stockOutApproved ? '#c0c4cc' : undefined }" |
| | | @click="handleDelete(row)" |
| | | >{{ row.stockOutApproved }}</el-button |
| | | <el-button link size="small" @click="handleDelete(row)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </PIMTable> |
| | |
| | | <el-descriptions-item label="éæäºº">{{ |
| | | detailData.returnUserName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§ç±»å">{{ |
| | | getShippingTypeLabel(detailData.shippingType) |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item v-if="String(detailData.shippingType) === '1'" label="å货车çå·">{{ |
| | | detailData.truckPlateNo || "--" |
| | | }}</el-descriptions-item> |
| | | <template v-else-if="String(detailData.shippingType) === '2'"> |
| | | <el-descriptions-item label="å¿«éå
¬å¸">{{ |
| | | detailData.expressCompany || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éåå·">{{ |
| | | detailData.expressNo || "--" |
| | | }}</el-descriptions-item> |
| | | </template> |
| | | <el-descriptions-item label="æ´åææ£é¢">{{ |
| | | formatAmount(detailData.totalDiscountAmount) |
| | | }}</el-descriptions-item> |
| | |
| | | /> |
| | | <el-table-column label="åä½" prop="unit" width="80" /> |
| | | <el-table-column label="æ°é" prop="stockInNum" width="80" /> |
| | | <el-table-column label="éå®åè´§æ°é" prop="saleOutQuantity" width="110" /> |
| | | <el-table-column label="å¯éè´§æ°é" |
| | | prop="unQuantity" |
| | | width="100" /> |
| | | <el-table-column label="å·²éè´§æ°é" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ formatAmount(scope.row.totalReturnNum || 0) }} |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="120" /> --> |
| | |
| | | prop: "returnUserName", |
| | | width: 110, |
| | | }, |
| | | { |
| | | label: "åè´§ç±»å", |
| | | prop: "shippingType", |
| | | width: 100, |
| | | formatData: (val) => getShippingTypeLabel(val), |
| | | }, |
| | | { label: "å货车çå·", prop: "truckPlateDisplay", width: 140 }, |
| | | { label: "å¿«éå
¬å¸", prop: "expressCompanyDisplay", width: 140 }, |
| | | { label: "å¿«éåå·", prop: "expressNoDisplay", width: 150 }, |
| | | |
| | | { |
| | | label: "æ´åææ£é¢", |
| | |
| | | }, |
| | | { |
| | | name: "å é¤", |
| | | disabled: (row) => !!row.stockOutApproved, |
| | | clickFun: (row) => { |
| | | handleDelete(row); |
| | | }, |
| | |
| | | findPurchaseReturnOrderListPage({ ...searchForm.value, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = (res.data.records || []).map((row) => ({ |
| | | ...row, |
| | | truckPlateDisplay: String(row?.shippingType) === "1" ? (row?.truckPlateNo || "--") : "--", |
| | | expressCompanyDisplay: String(row?.shippingType) === "2" ? (row?.expressCompany || "--") : "--", |
| | | expressNoDisplay: String(row?.shippingType) === "2" ? (row?.expressNo || "--") : "--", |
| | | })); |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(() => { |
| | |
| | | ); |
| | | }; |
| | | |
| | | const getShippingTypeLabel = (value) => { |
| | | const shippingTypeMap = { |
| | | 1: "货车", |
| | | 2: "å¿«é", |
| | | }; |
| | | return shippingTypeMap[String(value)] || shippingTypeMap[value] || "--"; |
| | | }; |
| | | |
| | | const formatAmount = (value) => { |
| | | if (value === null || value === undefined || value === "") { |
| | | return "--"; |
| | |
| | | return num.toFixed(2); |
| | | }; |
| | | |
| | | /** å·²éè´§æ°é = å
¥åºè¡æ»æ°é â å½åå¯éè´§æ°éï¼å©ä½ï¼ */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0); |
| | | const un = Number(row?.unQuantity ?? 0); |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0; |
| | | return Math.max(total - un, 0); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | |
| | | margin-top: unset; |
| | | } |
| | | </style> |
| | | |
| | |
| | | <el-input v-model="formData.inspector" placeholder="请è¾å
¥è´¨æ£åå§å" /> |
| | | </el-form-item> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´"> |
| | | <el-date-picker v-model="formCreateTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-form-item label="夿³¨"> |
| | | <el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨ä¿¡æ¯" /> |
| | | </el-form-item> |
| | |
| | | |
| | | <script setup> |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import { ref, reactive, computed } from 'vue' |
| | | import dayjs from 'dayjs' |
| | | import { ref, reactive } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | const loading = ref(false) |
| | |
| | | supplierName: '', |
| | | products: [], |
| | | inspector: '', |
| | | remark: '', |
| | | createTime: '' |
| | | }) |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (formData.createTime ? String(formData.createTime).split(' ')[0] : ''), |
| | | set: (value) => { |
| | | formData.createTime = value ? `${value} ${dayjs().format('HH:mm:ss')}` : '' |
| | | } |
| | | remark: '' |
| | | }) |
| | | |
| | | const mockData = [ |
| | |
| | | arrivalNo: row.arrivalNo, |
| | | supplierName: row.supplierName, |
| | | inspector: row.inspector, |
| | | remark: row.remark, |
| | | createTime: row.createTime || '' |
| | | remark: row.remark |
| | | }) |
| | | } else { |
| | | Object.assign(formData, { |
| | |
| | | supplierName: '', |
| | | products: [], |
| | | inspector: '', |
| | | remark: '', |
| | | createTime: dayjs().format('YYYY-MM-DD HH:mm:ss') |
| | | remark: '' |
| | | }) |
| | | } |
| | | dialogVisible.value = true |
| | |
| | | if (dialogType.value === 'add') { |
| | | const newInspection = { |
| | | id: Date.now(), |
| | | inspectionNo: '', |
| | | inspectionNo: `QI${Date.now()}`, |
| | | arrivalNo: formData.arrivalNo, |
| | | supplierName: formData.supplierName, |
| | | status: 'pending', |
| | | qualifiedQuantity: totalQualified, |
| | | unqualifiedQuantity: totalUnqualified, |
| | | inspectionTime: formData.createTime, |
| | | inspectionTime: new Date().toLocaleString(), |
| | | inspector: formData.inspector, |
| | | remark: formData.remark |
| | | } |
| | |
| | | <el-form-item label="æ è¯ç±»å"> |
| | | <span>{{ currentProduct.identifierType }}</span> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´"> |
| | | <el-date-picker v-model="createTimeDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%"></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="çææ°é" |
| | | prop="generateQuantity"> |
| | | <el-input-number v-model="generateQuantity" |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Search, Loading, Download } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | |
| | | const generateQuantity = ref(1); |
| | | const codeRule = ref(""); |
| | | const customPrefix = ref(""); |
| | | const createTime = ref(dayjs().format("YYYY-MM-DD HH:mm:ss")); |
| | | const createTimeDate = computed({ |
| | | get: () => (createTime.value ? String(createTime.value).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | createTime.value = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | const newBatchNo = ref(""); |
| | | const reassignReason = ref(""); |
| | | const formRef = ref(); |
| | |
| | | currentProduct.value.batchNo |
| | | }_${String(i).padStart(3, "0")}`; |
| | | } else if (codeRule.value === "æ¶é´æ³+éæºæ°") { |
| | | identifierCode = ""; |
| | | identifierCode = `TS_${Date.now()}_${Math.floor(Math.random() * 1000)}`; |
| | | } else if (codeRule.value === "èªå®ä¹è§å") { |
| | | identifierCode = ""; |
| | | identifierCode = `${customPrefix.value || "CUSTOM"}_${Date.now()}_${i}`; |
| | | } |
| | | |
| | | newIdentifiers.push({ |
| | |
| | | identifierType: currentProduct.value.identifierType, |
| | | identifierCode: identifierCode, |
| | | status: "å·²çæ", |
| | | generateTime: createTime.value, |
| | | generateTime: new Date().toLocaleString(), |
| | | remark: "æ¹éçæ", |
| | | }); |
| | | } |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | if (!Number.isFinite(numberValue)) { |
| | | return 0; |
| | | } |
| | | return Number(numberValue.toFixed(2)); |
| | | return Number(numberValue.toFixed(4)); |
| | | }; |
| | | |
| | | const syncDemandedQuantityTree = (items, parentDemandedQuantity = null) => { |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | if (!Number.isFinite(numberValue)) { |
| | | return 0; |
| | | } |
| | | return Number(numberValue.toFixed(2)); |
| | | return Number(numberValue.toFixed(4)); |
| | | }; |
| | | |
| | | const syncDemandedQuantityTree = ( |
| | |
| | | }; |
| | | |
| | | const fetchProcessOptions = async () => { |
| | | const { data } = await list({ size: -1, current: -1 }); |
| | | const { data } = await list({}); |
| | | console.log(data, "dataValue.dataList"); |
| | | dataValue.processOptions = normalizeListData(data); |
| | | }; |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="table_list"> |
| | | <el-form :inline="true" :model="queryParams" class="search-bar" @submit.prevent> |
| | | <el-form-item label="产ååç§°"> |
| | | <el-input |
| | | v-model="queryParams.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°" |
| | | clearable |
| | | style="width: 220px" |
| | | @keyup.enter="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼åå·"> |
| | | <el-input |
| | | v-model="queryParams.productModelName" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | clearable |
| | | style="width: 220px" |
| | | @keyup.enter="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">æ¥è¯¢</el-button> |
| | | <el-button @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div style="text-align: right; margin-bottom: 10px;"> |
| | | <el-button type="primary" |
| | | @click="handleAdd">æ°å¢</el-button> |
| | |
| | | dataType: "slot", |
| | | slot: "detail", |
| | | minWidth: 140, |
| | | align: 'center' |
| | | }, |
| | | { |
| | | label: "产ååç§°", |
| | |
| | | const operationType = ref("add"); // add | edit |
| | | const formRef = ref(null); |
| | | const showProductSelectDialog = ref(false); |
| | | const queryParams = reactive({ |
| | | productName: "", |
| | | productModelName: "", |
| | | }); |
| | | |
| | | // BOM导å
¥åæ° |
| | | const upload = reactive({ |
| | |
| | | getList(); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | const resetQuery = () => { |
| | | queryParams.productName = ""; |
| | | queryParams.productModelName = ""; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | listPage({ |
| | | current: page.current, |
| | | size: page.size, |
| | | productName: queryParams.productName || undefined, |
| | | productModelName: queryParams.productModelName || undefined, |
| | | }) |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | |
| | | prop="batchNo" |
| | | min-width="150" /> |
| | | <el-table-column label="éæ±æ°é" |
| | | prop="demandedQuantity" |
| | | min-width="110" /> |
| | | min-width="110"> |
| | | <template #default="{ row }"> |
| | | {{ stripTrailingZeros(row.demandedQuantity) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计éåä½" |
| | | prop="unit" |
| | | width="100" /> |
| | | <el-table-column label="é¢ç¨æ°é" |
| | | prop="pickQuantity" |
| | | min-width="110" /> |
| | | min-width="110"> |
| | | <template #default="{ row }"> |
| | | {{ stripTrailingZeros(row.pickQuantity) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è¡¥ææ°é" |
| | | min-width="120"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="handleViewSupplementRecord(row)"> |
| | | {{ row.feedingQty ?? 0 }} |
| | | {{ stripTrailingZeros(row.feedingQty) ?? 0 }} |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éææ°é" |
| | | min-width="110"> |
| | | <template #default="{ row }"> |
| | | {{ row.returnQty ?? 0 }} |
| | | {{ stripTrailingZeros(row.returnQty) ?? 0 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å®é
æ°é" |
| | |
| | | <template #default="{ row }"> |
| | | <el-input-number v-model="row.actualQty" |
| | | :min="0" |
| | | :precision="3" |
| | | :step="1" |
| | | controls-position="right" |
| | | placeholder="è¾å
¥å®é
æ°é" |
| | | :formatter="value => stripTrailingZeros(value)" |
| | | :parser="value => parseFloat(value) || 0" |
| | | style="width: 100%;" |
| | | :disabled="row.returned || orderRow?.end" |
| | | @change="val => handleActualQtyChange(row, val)" /> |
| | |
| | | prop="unit" |
| | | min-width="100" /> |
| | | <el-table-column label="éææ±æ»æ°é" |
| | | prop="returnQtyTotal" |
| | | min-width="140" /> |
| | | min-width="140"> |
| | | <template #default="{ row }"> |
| | | {{ stripTrailingZeros(row.returnQtyTotal) }} |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | |
| | | updateMaterialPickingLedger, |
| | | } from "@/api/productionManagement/productionOrder.js"; |
| | | |
| | | const stripTrailingZeros = val => { |
| | | const str = String(val ?? ""); |
| | | if (str.includes(".")) { |
| | | return parseFloat(str).toString(); |
| | | } |
| | | return str; |
| | | }; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { type: Boolean, default: false }, |
| | | orderRow: { type: Object, default: null }, |
| | |
| | | <div> |
| | | <el-dialog v-model="dialogVisible" |
| | | title="颿å°è´¦" |
| | | width="1200px" |
| | | width="1350px" |
| | | @close="handleClose"> |
| | | <div class="material-toolbar"> |
| | | <el-button type="primary" |
| | |
| | | border |
| | | row-key="tempId"> |
| | | <el-table-column label="å·¥åºåç§°" |
| | | min-width="140"> |
| | | min-width="80"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.bom === true">{{ row.operationName || "-" }}</span> |
| | | <el-select v-else |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åæåç§°" |
| | | min-width="140"> |
| | | min-width="120"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.bom === true">{{ row.materialName || "-" }}</span> |
| | | <el-button v-else |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åæåå·" |
| | | min-width="140"> |
| | | min-width="120"> |
| | | <template #default="{ row }"> |
| | | {{ row.materialModel || "-" }} |
| | | </template> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éæ±æ°é" |
| | | min-width="120"> |
| | | min-width="100"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.bom === true">{{ row.demandedQuantity ?? "-" }}</span> |
| | | <span v-if="row.bom === true">{{ stripTrailingZeros(row.demandedQuantity) ?? "-" }}</span> |
| | | <el-input-number v-else |
| | | v-model="row.demandedQuantity" |
| | | :min="0" |
| | | :precision="3" |
| | | :step="1" |
| | | controls-position="right" |
| | | :formatter="value => stripTrailingZeros(value)" |
| | | :parser="value => parseFloat(value) || 0" |
| | | style="width: 100%;" |
| | | @change="val => handleRequiredQtyChange(row, val)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计éåä½" |
| | | width="100"> |
| | | width="80"> |
| | | <template #default="{ row }"> |
| | | {{ row.unit || "-" }} |
| | | </template> |
| | |
| | | <template #default="{ row }"> |
| | | <el-input-number v-model="row.pickQty" |
| | | :min="0" |
| | | :precision="3" |
| | | :step="1" |
| | | controls-position="right" |
| | | :formatter="value => stripTrailingZeros(value)" |
| | | :parser="value => parseFloat(value) || 0" |
| | | style="width: 100%;" /> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | const currentMaterialSelectRowIndex = ref(-1); |
| | | let materialTempId = 0; |
| | | |
| | | const createMaterialRow = (row = {}) => ({ |
| | | const getDefaultBatchNo = batchNoList => { |
| | | if (!Array.isArray(batchNoList) || batchNoList.length === 0) return []; |
| | | return [batchNoList[0]]; |
| | | }; |
| | | |
| | | const createMaterialRow = (row = {}) => { |
| | | const batchNoList = Array.isArray(row.batchNoList) ? row.batchNoList : []; |
| | | const batchNo = row.batchNo |
| | | ? typeof row.batchNo === "string" |
| | | ? row.batchNo.split(",") |
| | | : row.batchNo |
| | | : getDefaultBatchNo(batchNoList); |
| | | |
| | | return { |
| | | tempId: row.id || `temp_${++materialTempId}`, |
| | | id: row.id, |
| | | processId: row.processId || row.technologyOperationId, |
| | |
| | | materialModel: row.materialModel || row.model || "", |
| | | demandedQuantity: Number(row.requiredQty ?? row.demandedQuantity ?? 0), |
| | | unit: row.unit || "", |
| | | pickQty: Number(row.pickQty ?? row.pickQuantity ?? 0), |
| | | batchNo: row.batchNo |
| | | ? typeof row.batchNo === "string" |
| | | ? row.batchNo.split(",") |
| | | : row.batchNo |
| | | : [], |
| | | batchNoList: row.batchNoList || [], |
| | | }); |
| | | pickQty: Number(row.pickQty ?? row.pickQuantity ?? row.requiredQty ?? row.demandedQuantity ?? 0), |
| | | batchNo, |
| | | batchNoList, |
| | | }; |
| | | }; |
| | | |
| | | const getProcessOptions = async () => { |
| | | if (!props.orderRow?.id) return; |
| | |
| | | } |
| | | ); |
| | | |
| | | const stripTrailingZeros = val => { |
| | | const str = String(val); |
| | | if (str.includes(".")) { |
| | | return parseFloat(str).toString(); |
| | | } |
| | | return str; |
| | | }; |
| | | |
| | | const handleClose = () => { |
| | | materialTableData.value = []; |
| | | currentMaterialSelectRowIndex.value = -1; |
| | |
| | | product.materialName || product.productName || product.name || ""; |
| | | row.materialModel = product.materialModel || product.model || ""; |
| | | row.unit = product.unit || product.measureUnit || ""; |
| | | row.batchNoList = product.batchNoList; |
| | | row.batchNoList = Array.isArray(product.batchNoList) ? product.batchNoList : []; |
| | | row.batchNo = getDefaultBatchNo(row.batchNoList); |
| | | currentMaterialSelectRowIndex.value = -1; |
| | | materialProductDialogVisible.value = false; |
| | | }; |
| | |
| | | prop="unit" |
| | | width="100" /> |
| | | <el-table-column label="éæ±æ°é" |
| | | prop="demandedQuantity" |
| | | width="100" /> |
| | | min-width="100"> |
| | | <template #default="{ row }"> |
| | | {{ stripTrailingZeros(row.demandedQuantity) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é¢ç¨æ°é" |
| | | prop="pickQuantity" |
| | | width="100" /> |
| | | min-width="100"> |
| | | <template #default="{ row }"> |
| | | {{ stripTrailingZeros(row.pickQuantity) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="已补æ°é" |
| | | prop="feedingQty" |
| | | width="100" /> |
| | | min-width="100"> |
| | | <template #default="{ row }"> |
| | | {{ stripTrailingZeros(row.feedingQty) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è¡¥ææ°é" |
| | | min-width="150"> |
| | | <template #default="{ row }"> |
| | | <el-input-number v-model="row.newSupplementQty" |
| | | :min="0" |
| | | :precision="3" |
| | | :step="1" |
| | | controls-position="right" |
| | | placeholder="è¾å
¥è¡¥ææ°é" |
| | | :formatter="value => stripTrailingZeros(value)" |
| | | :parser="value => parseFloat(value) || 0" |
| | | style="width: 100%;" /> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | updateMaterialPickingLedger, |
| | | } from "@/api/productionManagement/productionOrder.js"; |
| | | |
| | | const stripTrailingZeros = val => { |
| | | const str = String(val ?? ""); |
| | | if (str.includes(".")) { |
| | | return parseFloat(str).toString(); |
| | | } |
| | | return str; |
| | | }; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { type: Boolean, default: false }, |
| | | orderRow: { type: Object, default: null }, |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 160px" |
| | | style="width: 160px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°:"> |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 160px" |
| | | style="width: 160px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼:"> |
| | |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 160px" |
| | | style="width: 160px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 160px" |
| | | style="width: 160px;" |
| | | @change="handleQuery"> |
| | | <el-option label="å¾
å¼å§" |
| | | value="1" /> |
| | |
| | | value="2" /> |
| | | <el-option label="已宿" |
| | | value="3" /> |
| | | <!-- <el-option label="已忶"--> |
| | | <!-- value="4" />--> |
| | | <el-option label="已忶" |
| | | value="4" /> |
| | | <el-option label="å·²ç»æ" |
| | | value="5" /> |
| | | </el-select> |
| | |
| | | :tableLoading="tableLoading" |
| | | :row-class-name="tableRowClassName" |
| | | :isSelection="true" |
| | | :selectable="(row) => !row.endOrder" |
| | | :selectable="row => !row.endOrder" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="pagination"> |
| | | <template #completionStatus="{ row }"> |
| | | <el-progress :percentage="toProgressPercentage(row?.completionStatus)" |
| | | :color="progressColor(toProgressPercentage(row?.completionStatus))" |
| | | :status=" |
| | | toProgressPercentage(row?.completionStatus) >= 100 |
| | | ? 'success' |
| | | : '' |
| | | " /> |
| | | :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" /> |
| | | </template> |
| | | <template #processRouteStatus="{ row }"> |
| | | <div v-if="row.processRouteStatus && row.processRouteStatus.length" |
| | |
| | | <div class="step-circle" |
| | | :class="{ 'is-completed': item.percentage >= 100 }"> |
| | | <span class="step-percentage" |
| | | :style="{ |
| | | color: |
| | | item.percentage >= 70 |
| | | ? item.percentage >= 100 |
| | | ? '#67c23a' |
| | | : '#f56c6c' |
| | | : '#000', |
| | | }">{{ item.percentage }}%</span> |
| | | :style="{ color: item.percentage >= 70 ? item.percentage >= 100 ? '#67c23a' : '#f56c6c' : '#000' }">{{ item.percentage }}%</span> |
| | | </div> |
| | | <div class="step-name">{{ item.name }}</div> |
| | | </div> |
| | |
| | | <el-form-item label="å·¥èºè·¯çº¿"> |
| | | <el-select v-model="bindForm.routeId" |
| | | placeholder="è¯·éæ©å·¥èºè·¯çº¿" |
| | | style="width: 100%" |
| | | style="width: 100%;" |
| | | :loading="bindRouteLoading"> |
| | | <el-option v-for="item in routeOptions" |
| | | :key="item.id" |
| | |
| | | <!-- æ¥æºæ°æ®å¼¹çª --> |
| | | <el-dialog v-model="sourceDataDialogVisible" |
| | | title="æ¥æºæ°æ®" |
| | | width="1200px" |
| | | top="5vh" |
| | | class="source-data-dialog" |
| | | append-to-body> |
| | | width="1200px"> |
| | | <div v-if="sourceRowData" |
| | | class="applyno-summary1"> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">产ååç§°ï¼</span> |
| | | <span class="summary-value"> |
| | | <el-tag type="primary">{{ |
| | | sourceRowData.productName || "-" |
| | | }}</el-tag> |
| | | <el-tag type="primary">{{ sourceRowData.productName || '-' }}</el-tag> |
| | | </span> |
| | | </div> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">è§æ ¼ï¼</span> |
| | | <span class="summary-value">{{ sourceRowData.model || "-" }}</span> |
| | | <span class="summary-value">{{ sourceRowData.model || '-' }}</span> |
| | | </div> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">订åéæ±æ°éï¼</span> |
| | |
| | | <div class="info-grid"> |
| | | <div class="info-item"> |
| | | <div class="info-label">计åå·</div> |
| | | <div class="info-value">{{ item.mpsNo || "-" }}</div> |
| | | <div class="info-value">{{ item.mpsNo || '-' }}</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">æ°æ®æ¥æº</div> |
| | | <div class="info-value"> |
| | | <el-tag :type="item.source === 'éå®' ? 'primary' : 'warning'"> |
| | | {{ item.source || "æªç¥" }} |
| | | {{ item.source || 'æªç¥' }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">ååå·</div> |
| | | <div class="info-value"> |
| | | {{ item.salesContractNo || "-" }} |
| | | </div> |
| | | <div class="info-value">{{ item.salesContractNo || '-' }}</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">客æ·åç§°</div> |
| | | <div class="info-value">{{ item.customerName || "-" }}</div> |
| | | <div class="info-value">{{ item.customerName || '-' }}</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">项ç®åç§°</div> |
| | | <div class="info-value">{{ item.projectName || "-" }}</div> |
| | | <div class="info-value">{{ item.projectName || '-' }}</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">计åéæ±æ°é</div> |
| | | <div class="info-value"> |
| | | {{ item.qtyRequired || 0 }} {{ item.unit || "" }} |
| | | </div> |
| | | <div class="info-value">{{ item.qtyRequired || 0 }} {{ item.unit || '' }}</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">åä½</div> |
| | | <div class="info-value">{{ item.unit || "-" }}</div> |
| | | <div class="info-value">{{ item.unit || '-' }}</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">éæ±æ¥æ</div> |
| | | <div class="info-value"> |
| | | {{ |
| | | item.requiredDate |
| | | ? dayjs(item.requiredDate).format("YYYY-MM-DD") |
| | | : "-" |
| | | }} |
| | | </div> |
| | | <div class="info-value">{{ item.requiredDate ? dayjs(item.requiredDate).format('YYYY-MM-DD') : '-' }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </el-dialog> |
| | | <MaterialLedgerDialog v-model="materialDialogVisible" |
| | | :order-row="currentMaterialOrder" |
| | | @saved="getList" /> |
| | | /> |
| | | <MaterialDetailDialog v-model="materialDetailDialogVisible" |
| | | :order-row="currentMaterialDetailOrder" |
| | | @confirmed="getList" /> |
| | |
| | | getProductOrderSource, |
| | | updateProductOrder, |
| | | } from "@/api/productionManagement/productionOrder.js"; |
| | | import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js"; |
| | | import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js"; |
| | | import MaterialLedgerDialog from "@/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue"; |
| | | import MaterialDetailDialog from "@/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue"; |
| | |
| | | { |
| | | name: "ç»å®å·¥èºè·¯çº¿", |
| | | type: "text", |
| | | showHide: row => |
| | | !row.processRouteCode && !row.endOrder && row.status !== 3, |
| | | showHide: row => !row.processRouteCode && !row.endOrder, |
| | | clickFun: row => { |
| | | openBindRouteDialog(row, "add"); |
| | | }, |
| | |
| | | { |
| | | name: "æ´æ¢å·¥èºè·¯çº¿", |
| | | type: "text", |
| | | showHide: row => |
| | | row.processRouteCode && !row.endOrder && row.status !== 3, |
| | | showHide: row => row.processRouteCode && !row.endOrder, |
| | | clickFun: row => { |
| | | openBindRouteDialog(row, "change"); |
| | | }, |
| | |
| | | if (!Number.isFinite(n)) return 0; |
| | | if (n <= 0) return 0; |
| | | if (n >= 100) return 100; |
| | | return parseFloat(n.toFixed(2)); |
| | | return Math.round(n); |
| | | }; |
| | | |
| | | // 30/50/80/100 åæ®µé¢è²ï¼çº¢/æ©/è/绿 |
| | |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | // æé ä¸ä¸ªæ°ç对象ï¼ä¸å
å«entryDateåæ®µ |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | productOrderListPage(params) |
| | | .then(async res => { |
| | | const records = res.data.records || []; |
| | | // 为æ¯ä¸ªè®¢åæ¥è¯¢å¯¹åºçå·¥åºè¿åº¦æ°æ® |
| | | const processPromises = records.map(async item => { |
| | | if (item.npsNo) { |
| | | try { |
| | | const workOrderRes = await productWorkOrderPage({ |
| | | npsNo: item.npsNo, |
| | | size: 100, |
| | | }); |
| | | const workOrders = workOrderRes.data.records || []; |
| | | // æç
§å·¥åºé¡ºåºæåºï¼å¦ææé¡ºåºå段ï¼å设为 orderNum ææè¿å顺åºï¼ |
| | | // 转æ¢ä¸º processRouteStatus æ ¼å¼ |
| | | const processRouteStatus = workOrders.map(wo => ({ |
| | | name: wo.operationName || "æªç¥å·¥åº", |
| | | percentage: wo.completionStatus > 100 ? 100 : wo.completionStatus, |
| | | })); |
| | | return { ...item, processRouteStatus }; |
| | | } catch (error) { |
| | | console.error(`è·åå·¥å ${item.npsNo} è¿åº¦å¤±è´¥:`, error); |
| | | return { ...item, processRouteStatus: [] }; |
| | | } |
| | | } |
| | | return { ...item, processRouteStatus: [] }; |
| | | }); |
| | | |
| | | tableData.value = await Promise.all(processPromises); |
| | | .then(res => { |
| | | tableData.value = res.data.records || []; |
| | | page.total = res.data.total; |
| | | tableLoading.value = false; |
| | | }) |
| | |
| | | technologyRoutingId: data.technologyRoutingId, |
| | | orderId, |
| | | type: "order", |
| | | editable: !row.endOrder && row.status !== 3, |
| | | editable: !row.endOrder, |
| | | }, |
| | | }); |
| | | } catch (e) { |
| | |
| | | |
| | | .source-table-container { |
| | | margin-top: 20px; |
| | | flex: 1; |
| | | min-height: 0; |
| | | max-height: 500px; |
| | | overflow: auto; |
| | | } |
| | | |
| | | .source-data-cards-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | flex: 1; |
| | | min-height: 0; |
| | | max-height: none; |
| | | max-height: 500px; |
| | | overflow-y: auto; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .source-data-dialog { |
| | | .el-dialog { |
| | | display: flex; |
| | | flex-direction: column; |
| | | max-height: 90vh; |
| | | } |
| | | |
| | | .el-dialog__body { |
| | | flex: 1; |
| | | min-height: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | import { productionProductMainListPage } from "@/api/productionManagement/productionProductMain.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import InputModal from "@/views/productionManagement/productionReporting/Input.vue"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | |
| | | { |
| | | label: "æ¥å·¥åå·", |
| | | prop: "productNo", |
| | | width: 120, |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "æ¥å·¥äººå", |
| | | prop: "nickName", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "å·¥æ¶ï¼hï¼", |
| | | width: 100, |
| | | prop: "workHour", |
| | | }, |
| | | // { |
| | | // label: "å·¥æ¶ï¼hï¼", |
| | | // width: 100, |
| | | // prop: "workHour", |
| | | // }, |
| | | { |
| | | label: "å·¥åº", |
| | | prop: "process", |
| | | width: 120, |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "å·¥åç¼å·", |
| | | prop: "workOrderNo", |
| | | width: 120, |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "éå®ååå·", |
| | |
| | | { |
| | | label: "å建æ¶é´", |
| | | prop: "createTime", |
| | | width: 120, |
| | | width: 160, |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | }, |
| | | { |
| | | dataType: "action", |
| | |
| | | border> |
| | | <el-descriptions-item label="ç产订åå·">{{ rowData.productionOrderDto?.npsNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产ååç§°">{{ rowData.productionOrderDto?.productName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="è§æ ¼åå·">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产åè§æ ¼">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item> |
| | | <!-- <el-descriptions-item label="ç©æç¼ç ">{{ rowData.productionOrderDto?.materialCode || '-' }}</el-descriptions-item> --> |
| | | <el-descriptions-item label="è®¡åæ°é">{{ rowData.productionOrderDto?.quantity || 0 }} <span class="unit">{{ rowData.productionOrderDto?.unit || '-' }}</span></el-descriptions-item> |
| | | <el-descriptions-item label="å½åç¶æ"> |
| | |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ rowData.productionOrderDto?.customerName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¼å§æ¥æ">{{ parseTime(rowData.productionOrderDto?.startTime) || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¼å§æ¥æ">{{ parseTime(rowData.productionOrderDto?.startTime,'{y}-{m}-{d}') || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="宿è¿åº¦"> |
| | | <el-progress :percentage="rowData.productionOrderDto?.completionStatus>=100?100:rowData.productionOrderDto?.completionStatus" |
| | | :color="customColors(rowData.productionOrderDto?.completionStatus)" |
| | |
| | | <el-descriptions-item label="ç产工åå·">{{ detailData.workOrder.workOrderNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="è®¡åæ°é">{{ detailData.workOrder.planQuantity || 0 }}</el-descriptions-item> |
| | | <el-descriptions-item label="宿æ°é">{{ detailData.workOrder.completeQuantity || 0 }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®é
å¼å§æ¶é´">{{ parseTime(detailData.workOrder.actualStartTime) || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®é
ç»ææ¶é´">{{ parseTime(detailData.workOrder.actualEndTime) || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®é
å¼å§æ¶é´">{{ parseTime(detailData.workOrder.actualStartTime, '{y}-{m}-{d}') || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®é
ç»ææ¶é´">{{ parseTime(detailData.workOrder.actualEndTime, '{y}-{m}-{d}') || '-' }}</el-descriptions-item> |
| | | <!-- <el-descriptions-item label="宿è¿åº¦"> |
| | | <el-progress :percentage="detailData.workOrder.completionStatus >= 100 ? 100 : (detailData.workOrder.completionStatus || 0)" |
| | | :color="customColors(detailData.workOrder.completionStatus)" |
| | |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="showInput(row.id)">æ¥çæå
¥</el-button> |
| | | @click="showInput(row.productionProductMainId)">æ¥çæå
¥</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="showParamDetail(row.productionOperationParamList)">åæ°è¯¦æ
</el-button> |
| | |
| | | :key="record.id" |
| | | class="quality-record-block"> |
| | | <div class="detail-section"> |
| | | <h3 class="section-title">æ£æµè®°å½ {{ index + 1 }} - {{ parseTime(record.createTime) }}</h3> |
| | | <h3 class="section-title">æ£æµè®°å½ {{ index + 1 }} - {{ parseTime(record.createTime, '{y}-{m}-{d}') }}</h3> |
| | | <el-descriptions :column="3" |
| | | border> |
| | | <el-descriptions-item label="æ£æµæ¥æ">{{ parseTime(record.createTime) || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ£æµæ¥æ">{{ parseTime(record.createTime, '{y}-{m}-{d}') || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥å·¥åå·">{{ record.reportNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ£éªå">{{ record.userName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥å·¥äººå">{{ record.userName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产ååç§°">{{ record.productName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="è§æ ¼åå·">{{ record.model || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ°é">{{ record.quantity || 0 }} {{ record.unit || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ£æµåä½">{{ record.checkCompany || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ£æµç»æ"> |
| | | <el-tag :type="record.checkResult === 'åæ ¼' ? 'success' : 'danger'"> |
| | | {{ record.checkResult || 'å¾
æ£æµ' }} |
| | | <el-descriptions-item label="æ£éªå">{{ record.checkName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="åæ ¼ç"> |
| | | <el-tag :type="getPassRateTagType(record)"> |
| | | {{ getPassRateDisplay(record) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | |
| | | return "#67c23a"; |
| | | }; |
| | | |
| | | const getPassRate = (record) => { |
| | | const qualified = Number(record.qualifiedQuantity) || 0; |
| | | const unqualified = Number(record.unqualifiedQuantity) || 0; |
| | | const total = qualified + unqualified; |
| | | if (total === 0) return null; |
| | | return Number(((qualified / total) * 100).toFixed(2)); |
| | | }; |
| | | |
| | | const getPassRateDisplay = (record) => { |
| | | const rate = getPassRate(record); |
| | | if (rate === null) return 'â'; |
| | | return `${rate}%`; |
| | | }; |
| | | |
| | | const getPassRateTagType = (record) => { |
| | | const rate = getPassRate(record); |
| | | if (rate === null) return 'info'; |
| | | if (rate >= 100) return 'success'; |
| | | if (rate >= 75) return 'primary'; |
| | | return 'danger'; |
| | | }; |
| | | |
| | | // 模ææç´¢æ¹æ³ |
| | | const handleNpsNoSearch = async query => { |
| | | npsNoLoading.value = true; |
| | |
| | | <span class="info-value">{{ transferCardRowData.productName }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="info-label">è§æ ¼åå·</span> |
| | | <span class="info-label">产åè§æ ¼</span> |
| | | <span class="info-value">{{ transferCardRowData.model }}</span> |
| | | </div> |
| | | <!-- <div class="info-item"> |
| | |
| | | if (!Number.isFinite(n)) return 0; |
| | | if (n <= 0) return 0; |
| | | if (n >= 100) return 100; |
| | | return parseFloat(n.toFixed(2)); |
| | | return Math.round(n); |
| | | }; |
| | | const progressColor = percentage => { |
| | | const p = toProgressPercentage(percentage); |
| | |
| | | if (!Number.isFinite(n)) return 0; |
| | | if (n <= 0) return 0; |
| | | if (n >= 100) return 100; |
| | | return parseFloat(n.toFixed(2)); |
| | | return Math.round(n); |
| | | }; |
| | | |
| | | const progressColor = percentage => { |
| | |
| | | <span class="info-value">{{ transferCardRowData.productName }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="info-label">è§æ ¼åå·</span> |
| | | <span class="info-label">产åè§æ ¼</span> |
| | | <span class="info-value">{{ transferCardRowData.model }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | |
| | | <el-input v-model.number="reportForm.quantity" |
| | | type="number" |
| | | min="0" |
| | | step="1" |
| | | step="0.0001" |
| | | style="width: 300px" |
| | | placeholder="请è¾å
¥çäº§åæ ¼æ°é" |
| | | @input="handleQuantityInput" /> |
| | |
| | | <el-input v-model.number="reportForm.scrapQty" |
| | | type="number" |
| | | min="0" |
| | | step="1" |
| | | step="0.0001" |
| | | style="width: 300px" |
| | | placeholder="请è¾å
¥æ¥åºæ°é" |
| | | @input="handleScrapQtyInput" /> |
| | |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | :loading="reportLoading" |
| | | @click="handleReport">ç¡®å®</el-button> |
| | | <el-button @click="reportDialogVisible = false">åæ¶</el-button> |
| | | </span> |
| | |
| | | dataType: "action", |
| | | fixed: "right", |
| | | operation: [ |
| | | { |
| | | /*{ |
| | | name: "æµè½¬å¡", |
| | | clickFun: row => { |
| | | downloadAndPrintWorkOrder(row); |
| | | }, |
| | | }, |
| | | },*/ |
| | | { |
| | | name: "éä»¶", |
| | | clickFun: row => { |
| | |
| | | const transferCardQrUrl = ref(""); |
| | | const transferCardRowData = ref(null); |
| | | const reportDialogVisible = ref(false); |
| | | const reportLoading = ref(false); |
| | | const fileDialogVisible = ref(false); |
| | | const currentWorkOrderId = ref(null); |
| | | const reportFormRef = ref(null); |
| | |
| | | const reportForm = reactive({ |
| | | planQuantity: 0, |
| | | quantity: null, |
| | | scrapQty: null, |
| | | scrapQty: 0, |
| | | userName: "", |
| | | workOrderId: "", |
| | | reportWork: "", |
| | |
| | | return; |
| | | } |
| | | const num = Number(value); |
| | | // æ´æ°ä¸å¤§äºçäº1 |
| | | if (isNaN(num) || !Number.isInteger(num) || num < 0) { |
| | | if (isNaN(num) || num < 0) { |
| | | callback(new Error("çäº§åæ ¼æ°éå¿
须大äºçäº0")); |
| | | return; |
| | | } |
| | |
| | | return; |
| | | } |
| | | const num = Number(value); |
| | | // æ´æ°ä¸å¤§äºçäº0 |
| | | if (isNaN(num) || !Number.isInteger(num) || num < 0) { |
| | | if (isNaN(num) || num < 0) { |
| | | callback(new Error("æ¥åºæ°éå¿
须大äºçäº0")); |
| | | return; |
| | | } |
| | |
| | | if (isNaN(num)) { |
| | | return; |
| | | } |
| | | // 妿å°äº1ï¼æ¸
é¤ |
| | | if (num < 0) { |
| | | reportForm.quantity = null; |
| | | return; |
| | | } |
| | | // 妿æ¯å°æ°åæ´æ°é¨å |
| | | if (!Number.isInteger(num)) { |
| | | const intValue = Math.floor(num); |
| | | // 妿忴åå°äº1ï¼æ¸
é¤ |
| | | if (intValue < 0) { |
| | | reportForm.quantity = null; |
| | | return; |
| | | } |
| | | reportForm.quantity = intValue; |
| | | return; |
| | | } |
| | | reportForm.quantity = num; |
| | |
| | | return; |
| | | } |
| | | const num = Number(value); |
| | | // 妿æ¯NaNï¼ä¿æåå¼ |
| | | if (isNaN(num)) { |
| | | return; |
| | | } |
| | | // 妿æ¯è´æ°ï¼æ¸
é¤è¾å
¥ |
| | | if (num < 0) { |
| | | reportForm.scrapQty = null; |
| | | return; |
| | | } |
| | | // 妿æ¯å°æ°ï¼åæ´æ°é¨å |
| | | if (!Number.isInteger(num)) { |
| | | reportForm.scrapQty = Math.floor(num); |
| | | return; |
| | | } |
| | | // ææçéè´æ´æ°ï¼å
æ¬0ï¼ |
| | | reportForm.scrapQty = num; |
| | | }; |
| | | |
| | |
| | | if (!Number.isFinite(n)) return 0; |
| | | if (n <= 0) return 0; |
| | | if (n >= 100) return 100; |
| | | return parseFloat(n.toFixed(2)); |
| | | return Math.round(n); |
| | | }; |
| | | const subtractQuantity = (minuend, subtrahend, precision = 4) => { |
| | | const factor = 10 ** precision; |
| | | const a = Math.round((Number(minuend) || 0) * factor); |
| | | const b = Math.round((Number(subtrahend) || 0) * factor); |
| | | return Math.max(0, (a - b) / factor); |
| | | }; |
| | | const progressColor = percentage => { |
| | | const p = toProgressPercentage(percentage); |
| | |
| | | } |
| | | } |
| | | currentReportRowData.value = row; |
| | | const planQuantity = Number(row.planQuantity || 0); |
| | | const completeQuantity = Number(row.completeQuantity || 0); |
| | | const remainingQuantity = Math.max(0, planQuantity - completeQuantity); |
| | | reportForm.planQuantity = remainingQuantity; |
| | | reportForm.planQuantity = subtractQuantity( |
| | | row.planQuantity, |
| | | row.completeQuantity |
| | | ); |
| | | reportForm.quantity = |
| | | row.quantity !== undefined && row.quantity !== null ? row.quantity : null; |
| | | reportForm.productProcessRouteItemId = row.productProcessRouteItemId; |
| | | reportForm.workOrderId = row.id; |
| | | reportForm.reportWork = row.reportWork; |
| | | reportForm.productMainId = row.productMainId; |
| | | reportForm.scrapQty = |
| | | row.scrapQty !== undefined && row.scrapQty !== null ? row.scrapQty : null; |
| | | reportForm.productionOrderRoutingOperationId = |
| | | row.productionOrderRoutingOperationId; |
| | | reportForm.scrapQty = 0; |
| | | reportForm.productionOrderRoutingOperationId = row.productionOrderRoutingOperationId; |
| | | reportForm.productionOrderId = row.productionOrderId; |
| | | if (row.type == 0) { |
| | | reportForm.workHour = row.workHour || 0; |
| | |
| | | productionOperationParamList: productionOperationParamList, |
| | | }; |
| | | |
| | | reportLoading.value = true; |
| | | addProductMain(submitParams) |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("æ¥å·¥æå"); |
| | |
| | | ElMessageBox.alert("æ¥å·¥å¤±è´¥", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | }); |
| | | }) |
| | | .finally(() => { |
| | | reportLoading.value = false; |
| | | }); |
| | | }); |
| | | }; |
| | |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼åå·:" |
| | | <el-form-item label="产åè§æ ¼:" |
| | | prop="model"> |
| | | <el-input v-model="searchForm.model" |
| | | placeholder="请è¾å
¥" |
| | |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col> |
| | | <el-form-item label="è§æ ¼åå·"> |
| | | <el-form-item label="产åè§æ ¼"> |
| | | <div class="info-display">{{ mergeForm.model || '-' }}</div> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | @change="onBlur" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´"> |
| | | <el-date-picker v-model="mergeCreateTimeDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" |
| | | :loading="mergeSubmitLoading" |
| | | @click="handleMergeSubmit">ç¡®å®ä¸å</el-button> |
| | | <el-button @click="isShowNewModal = false">åæ¶</el-button> |
| | | </span> |
| | |
| | | prop="mpsNo"> |
| | | <el-input v-model="form.mpsNo" |
| | | disabled |
| | | placeholder="ä¿ååèªå¨çæ" /> |
| | | placeholder="æ°å¢åèªå¨çæ" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productId"> |
| | |
| | | @change="handleProductChange" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | <el-form-item label="产åè§æ ¼" |
| | | prop="productModelId"> |
| | | <el-select v-model="form.productModelId" |
| | | @change="handleChangeSpecification" |
| | |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | placeholder="è¯·éæ©æ¿è¯ºæ¥æ" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´" |
| | | prop="createTime"> |
| | | <el-date-picker v-model="formCreateTimeDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | placeholder="è¯·éæ©å建æ¶é´" /> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | label: "产åè§æ ¼", |
| | | prop: "model", |
| | | width: "150px", |
| | | className: "spec-cell", |
| | |
| | | Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0); |
| | | mergeForm.planCompleteTime = row.requiredDate || ""; |
| | | mergeForm.productId = row.productId || ""; |
| | | mergeForm.createTime = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | mergeForm.ids = [row.id]; |
| | | sumAssignedQuantity.value = |
| | | Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0); |
| | |
| | | type: "danger", |
| | | link: true, |
| | | showHide: row => { |
| | | return row.status == 0 && row.source != "éå®"; |
| | | return row.status == 0; |
| | | }, |
| | | clickFun: row => { |
| | | handleDelete(row); |
| | |
| | | |
| | | // åå¹¶ä¸åå¼¹çªæ§å¶ |
| | | const isShowNewModal = ref(false); |
| | | const mergeSubmitLoading = ref(false); |
| | | // åå¹¶ä¸åè¡¨åæ°æ® |
| | | const mergeForm = reactive({ |
| | | productName: "", |
| | |
| | | totalAssignedQuantity: 0, |
| | | planCompleteTime: "", |
| | | productId: "", |
| | | createTime: "", |
| | | }); |
| | | const mergeCreateTimeDate = computed({ |
| | | get: () => |
| | | mergeForm.createTime ? String(mergeForm.createTime).split(" ")[0] : "", |
| | | set: value => { |
| | | mergeForm.createTime = value |
| | | ? `${value} ${dayjs().format("HH:mm:ss")}` |
| | | : ""; |
| | | }, |
| | | }); |
| | | |
| | | // 导å
¥ç¸å
³ |
| | |
| | | requiredDate: "", |
| | | promisedDeliveryDate: "", |
| | | remark: "", |
| | | createTime: "", |
| | | }); |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""), |
| | | set: value => { |
| | | form.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | }, |
| | | }); |
| | | const rules = reactive({ |
| | | productId: [{ required: true, message: "è¯·éæ©äº§å", trigger: "change" }], |
| | | productModelId: [ |
| | | { required: true, message: "è¯·éæ©è§æ ¼åå·", trigger: "change" }, |
| | | { required: true, message: "è¯·éæ©äº§åè§æ ¼", trigger: "change" }, |
| | | ], |
| | | qtyRequired: [{ required: true, message: "请è¾å
¥æ°é", trigger: "blur" }], |
| | | requiredDate: [ |
| | |
| | | }); |
| | | }; |
| | | |
| | | // éä¸çè§æ ¼åå·ID |
| | | // éä¸ç产åè§æ ¼ID |
| | | const selectedProductModelId = ref(""); |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | // 妿æéä¸çè¡ï¼è®°å½ç¬¬ä¸ä¸ªéä¸è¡çè§æ ¼åå·ID |
| | | // 妿æéä¸çè¡ï¼è®°å½ç¬¬ä¸ä¸ªéä¸è¡ç产åè§æ ¼ID |
| | | if (selection.length > 0) { |
| | | selectedProductModelId.value = selection[0].productModelId; |
| | | } else { |
| | | // å¦ææ²¡æéä¸çè¡ï¼æ¸
ç©ºè§æ ¼åå·ID |
| | | // å¦ææ²¡æéä¸çè¡ï¼æ¸
空产åè§æ ¼ID |
| | | selectedProductModelId.value = ""; |
| | | } |
| | | }; |
| | |
| | | if (!selectedProductModelId.value) { |
| | | return true; |
| | | } |
| | | // 妿æéä¸çè¡ï¼åªæè§æ ¼åå·IDç¸åçè¡æå¯éæ© |
| | | // 妿æéä¸çè¡ï¼åªæäº§åè§æ ¼IDç¸åçè¡æå¯éæ© |
| | | return row.productModelId === selectedProductModelId.value; |
| | | }; |
| | | // æåæ°æ®æé®æä½ |
| | |
| | | mergeForm.totalAssignedQuantity = totalAssignedQuantity; |
| | | mergeForm.planCompleteTime = firstRow.requiredDate || ""; |
| | | mergeForm.productId = firstRow.productId || ""; |
| | | mergeForm.createTime = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | mergeForm.ids = selectedRows.value.map(row => row.id); |
| | | |
| | | // æå¼å¼¹çª |
| | |
| | | const payload = { |
| | | ...mergeForm, |
| | | }; |
| | | mergeSubmitLoading.value = true; |
| | | productionPlanCombine(payload) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | |
| | | .catch(err => { |
| | | console.error("åå¹¶ä¸åå¼å¸¸ï¼", err); |
| | | ElMessage.error("ç³»ç»å¼å¸¸ï¼åå¹¶ä¸å失败"); |
| | | }) |
| | | .finally(() => { |
| | | mergeSubmitLoading.value = false; |
| | | }); |
| | | // å¯ä»¥éæ©å·æ°å表æå
¶ä»æä½ |
| | | }; |
| | |
| | | requiredDate: "", |
| | | promisedDeliveryDate: "", |
| | | remark: "", |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | dialogVisible.value = true; |
| | | fetchProductOptions(); |
| | |
| | | requiredDate: row.requiredDate || "", |
| | | promisedDeliveryDate: row.promisedDeliveryDate || "", |
| | | remark: row.remark || "", |
| | | createTime: row.createTime || "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | fetchProductOptions(); |
| | |
| | | :action="uploadUrl" |
| | | :on-success="handleUploadSuccess" |
| | | :on-error="handleUploadError" |
| | | name="file" |
| | | name="files" |
| | | :show-file-list="false" |
| | | :headers="headers" |
| | | style="display: inline;margin-right: 10px" |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <filePreview ref="filePreviewRef" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {ref, reactive, getCurrentInstance} from "vue"; |
| | | import {Search} from "@element-plus/icons-vue"; |
| | | import { |
| | | qualityInspectParamDel, |
| | |
| | | } from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | import {getToken} from "@/utils/auth.js"; |
| | | import filePreview from '@/components/filePreview/index.vue' |
| | | import { |
| | | qualityInspectFileAdd, |
| | | qualityInspectFileDel, |
| | | qualityInspectFileListPage |
| | | } from "@/api/qualityManagement/qualityInspectFile.js"; |
| | | attachmentList, |
| | | createAttachment, |
| | | deleteAttachment |
| | | } from "@/api/basicData/storageAttachment.js"; |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const currentId = ref('') |
| | | const selectedRows = ref([]); |
| | | const filePreviewRef = ref() |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "æä»¶åç§°", |
| | |
| | | clickFun: (row) => { |
| | | downLoadFile(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "é¢è§", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | lookFile(row); |
| | | }, |
| | | } |
| | | ], |
| | | }, |
| | |
| | | const headers = ref({ |
| | | Authorization: "Bearer " + getToken(), |
| | | }); |
| | | const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // ä¸ä¼ çå¾çæå¡å¨å°å |
| | | const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // ä¸ä¼ çæå¡å¨å°å |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (row) => { |
| | |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | qualityInspectFileListPage({inspectId: currentId.value, ...page}).then(res => { |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | attachmentList({recordId: currentId.value, recordType: 'quality_inspect'}).then(res => { |
| | | tableData.value = (res.data || []).map(item => ({ |
| | | ...item, |
| | | name: item.originalFilename || item.name |
| | | })); |
| | | page.total = res.data ? res.data.length : 0; |
| | | }) |
| | | } |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | let pendingFiles = []; |
| | | let uploadTimer = null; |
| | | |
| | | // ä¸ä¼ æåå¤ç |
| | | function handleUploadSuccess(res, file) { |
| | | // 妿ä¸ä¼ æå |
| | | if (res.code == 200) { |
| | | const fileRow = {} |
| | | fileRow.name = res.data.originalName |
| | | fileRow.url = res.data.tempPath |
| | | uploadFile(fileRow) |
| | | if (res.code == 200 && res.data && res.data.length > 0) { |
| | | const newFiles = res.data.map(item => ({ |
| | | ...item, |
| | | name: item.originalFilename || item.name |
| | | })); |
| | | |
| | | pendingFiles = [...pendingFiles, ...newFiles]; |
| | | |
| | | // 鲿å¤çï¼é¿å
夿件忶ä¸ä¼ 导è´å表被è¦ç |
| | | if (uploadTimer) clearTimeout(uploadTimer); |
| | | uploadTimer = setTimeout(() => { |
| | | const mergedFiles = [...(tableData.value || []), ...pendingFiles]; |
| | | const storageAttachmentDTO = { |
| | | recordType: 'quality_inspect', |
| | | recordId: currentId.value, |
| | | application: "file", |
| | | storageBlobDTOs: mergedFiles |
| | | }; |
| | | createAttachment(storageAttachmentDTO).then(r => { |
| | | proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); |
| | | getList() |
| | | }).catch(() => { |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); |
| | | }).finally(() => { |
| | | pendingFiles = []; // éç½® |
| | | }); |
| | | }, 500); |
| | | } else { |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); |
| | | } |
| | | } |
| | | function uploadFile(file) { |
| | | file.inspectId = currentId.value; |
| | | qualityInspectFileAdd(file).then(res => { |
| | | proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); |
| | | getList() |
| | | }) |
| | | } |
| | | // ä¸ä¼ 失败å¤ç |
| | | function handleUploadError() { |
| | |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | ids = selectedRows.value.map((item) => item.storageAttachmentId); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | qualityInspectFileDel(ids).then((res) => { |
| | | deleteAttachment(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // é¢è§éä»¶ |
| | | const lookFile = (row) => { |
| | | filePreviewRef.value.open(row.url) |
| | | } |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢åºåæ£éª' : operationType === 'view' ? 'æ¥çåºåæ£éª' : 'ç¼è¾åºåæ£éª'" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | @close="closeDia" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产ååç§°ï¼" |
| | | prop="productId"> |
| | | <el-tree-select v-model="form.productId" |
| | | <el-form-item label="产ååç§°ï¼" prop="productId"> |
| | | <el-tree-select |
| | | v-model="form.productId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="isViewMode || operationType === 'edit'" |
| | | style="width: 100%" /> |
| | | 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="isViewMode || operationType === 'edit'" |
| | | filterable |
| | | readonly |
| | | @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" /> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="productModelId"> |
| | | <el-select v-model="form.productModelId" placeholder="è¯·éæ©" clearable :disabled="isViewMode || operationType === 'edit'" |
| | | filterable readonly @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææ éæ©ï¼" |
| | | prop="testStandardId"> |
| | | <el-select v-model="form.testStandardId" |
| | | <el-form-item label="ææ éæ©ï¼" prop="testStandardId"> |
| | | <el-select |
| | | v-model="form.testStandardId" |
| | | placeholder="è¯·éæ©ææ " |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%" |
| | | :disabled="isViewMode"> |
| | | <el-option v-for="item in testStandardOptions" |
| | | :disabled="isViewMode" |
| | | > |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" /> |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" |
| | | prop="unit"> |
| | | <el-input v-model="form.unit" |
| | | placeholder="请è¾å
¥" |
| | | disabled /> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input v-model="form.unit" placeholder="请è¾å
¥" disabled/> |
| | | </el-form-item> |
| | | </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="isViewMode || processQuantityDisabled" /> |
| | | <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="isViewMode || processQuantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµåä½ï¼" |
| | | prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :disabled="isViewMode" /> |
| | | <el-form-item label="æ¯å¦æ½æ£ï¼" |
| | | prop="isSampling"> |
| | | <el-radio-group v-model="form.isSampling" |
| | | class="sampling-radio-group" |
| | | :disabled="isViewMode" |
| | | @change="handleSamplingChange"> |
| | | <el-radio-button :label="1">æ¯</el-radio-button> |
| | | <el-radio-button :label="0">å¦</el-radio-button> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµç»æï¼" |
| | | prop="checkResult"> |
| | | <el-select v-model="form.checkResult" |
| | | :disabled="isViewMode"> |
| | | <el-option label="åæ ¼" |
| | | value="åæ ¼" /> |
| | | <el-option label="ä¸åæ ¼" |
| | | value="ä¸åæ ¼" /> |
| | | <el-option label="é¨ååæ ¼" |
| | | value="é¨ååæ ¼" /> |
| | | </el-select> |
| | | <el-col :span="12" |
| | | v-if="form.isSampling === 1"> |
| | | <el-form-item label="æ½æ£æ°éï¼" |
| | | prop="samplingQuantity"> |
| | | <el-input-number :step="0.01" |
| | | :min="0" |
| | | style="width: 100%" |
| | | v-model="form.samplingQuantity" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :precision="2" |
| | | :disabled="isViewMode" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£éªåï¼" |
| | | prop="checkName"> |
| | | <el-select v-model="form.checkName" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | :disabled="isViewMode"> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | <el-form-item label="æ£æµåä½ï¼" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请è¾å
¥" clearable :disabled="isViewMode"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åæ ¼çï¼"> |
| | | <el-tag :type="passRateTagType">{{ passRateDisplayText }}</el-tag> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£éªåï¼" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="è¯·éæ©" clearable :disabled="isViewMode"> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" |
| | | :value="item.nickName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµæ¥æï¼" |
| | | prop="checkTime"> |
| | | <el-date-picker v-model="form.checkTime" |
| | | <el-form-item label="æ£æµæ¥æï¼" prop="checkTime"> |
| | | <el-date-picker |
| | | v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="isViewMode" /> |
| | | :disabled="isViewMode" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <PIMTable rowKey="id" |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400"> |
| | | height="400" |
| | | > |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" |
| | | clearable |
| | | :disabled="isViewMode" /> |
| | | <el-input v-model="row.testValue" clearable :disabled="isViewMode"/> |
| | | </template> |
| | | </PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <template v-if="!isViewMode"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </template> |
| | | <el-button v-else |
| | | @click="closeDia">å
³é</el-button> |
| | | <el-button v-else @click="closeDia">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | computed, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import {ref, reactive, toRefs, computed, getCurrentInstance, nextTick} from "vue"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { |
| | | qualityInspectAdd, |
| | | qualityInspectUpdate, |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | qualityInspectDetailByProductId, |
| | | getQualityTestStandardParamByTestStandardId, |
| | | } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { qualityInspectParamInfo } from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | |
| | | qualifiedQuantity: "", |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | isSampling: 0, |
| | | samplingQuantity: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | testStandardId: [ |
| | | { required: false, message: "è¯·éæ©ææ ", trigger: "change" }, |
| | | ], |
| | | testStandardId: [{ required: false, message: "è¯·éæ©ææ ", trigger: "change" }], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请è¾å
¥", trigger: "change" }], |
| | | isSampling: [{ required: true, message: "è¯·éæ©æ¯å¦æ½æ£", trigger: "change" }], |
| | | samplingQuantity: [{ required: true, message: "请è¾å
¥æ½æ£æ°é", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | // æ¯å¦ä¸ºæ¥çæ¨¡å¼ |
| | | const isViewMode = computed(() => operationType.value === "view"); |
| | | const isViewMode = computed(() => operationType.value === 'view'); |
| | | |
| | | const passRateValue = computed(() => { |
| | | const fromApi = form.value.passRate; |
| | | if (fromApi != null && fromApi !== '') { |
| | | const n = Number(fromApi); |
| | | if (!Number.isNaN(n)) return n; |
| | | } |
| | | const qualified = Number(form.value.qualifiedQuantity) || 0; |
| | | const unqualified = Number(form.value.unqualifiedQuantity) || 0; |
| | | const total = qualified + unqualified; |
| | | if (!total || total === 0) return null; |
| | | return (qualified / total) * 100; |
| | | }); |
| | | |
| | | const passRateDisplayText = computed(() => { |
| | | const params = passRateValue.value; |
| | | if (params == null || params === '') return 'â'; |
| | | const n = Number(params); |
| | | if (Number.isNaN(n)) return 'â'; |
| | | return `${n.toFixed(2)}%`; |
| | | }); |
| | | |
| | | const passRateTagType = computed(() => { |
| | | const params = passRateValue.value; |
| | | if (params == null || params === '') return 'info'; |
| | | const n = Number(params); |
| | | if (Number.isNaN(n)) return 'info'; |
| | | if (n >= 100) return 'success'; |
| | | if (n >= 90) return 'warning'; |
| | | return 'danger'; |
| | | }); |
| | | // ç¼è¾æ¶ï¼productMainId æ purchaseLedgerId 任䏿å¼åå·¥åºãæ°éç½®ç° |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | |
| | | { |
| | | label: "æ£éªå¼", |
| | | prop: "testValue", |
| | | dataType: "slot", |
| | | slot: "slot", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | |
| | | const [userListsRes] = await Promise.all([ |
| | | userListNoPage(), |
| | | getProductOptions(), |
| | | getOptions().then(res => { |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }), |
| | | }) |
| | | ]); |
| | | userList.value = userListsRes.data; |
| | | |
| | | form.value = {}; |
| | | form.value = { isSampling: 0, samplingQuantity: "" } |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | |
| | | if (operationType.value === "edit" || operationType.value === "view") { |
| | | if (operationType.value === 'edit' || operationType.value === 'view') { |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // å
è®¾ç½®è¡¨åæ°æ®ï¼ä½ææ¶æ¸
空 testStandardIdï¼çé项å è½½å®æåå设置 |
| | | form.value = { ...row, testStandardId: "" }; |
| | | currentProductId.value = row.productId || 0; |
| | | const { passRate, ...rowWithoutPassRate } = row; |
| | | form.value = { ...rowWithoutPassRate, testStandardId: '' }; |
| | | currentProductId.value = row.productId || 0 |
| | | // æ¸
空éªè¯ç¶æï¼é¿å
æ°æ®å è½½è¿ç¨ä¸çæ ¡éªéªç |
| | | nextTick(() => { |
| | | proxy.$refs.formRef?.clearValidate(); |
| | |
| | | // ç¼è¾æ¨¡å¼ä¸ï¼å¹¶è¡å è½½è§æ ¼åå·åææ é项 |
| | | if (currentProductId.value) { |
| | | // 设置产ååç§° |
| | | form.value.productName = findNodeById( |
| | | productOptions.value, |
| | | currentProductId.value |
| | | ); |
| | | form.value.productName = findNodeById(productOptions.value, currentProductId.value); |
| | | |
| | | // å¹¶è¡å è½½è§æ ¼åå·åææ é项 |
| | | const params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2, |
| | | inspectType: 2 |
| | | }; |
| | | |
| | | Promise.all([ |
| | | modelList({ id: currentProductId.value }), |
| | | qualityInspectDetailByProductId(params), |
| | | qualityInspectDetailByProductId(params) |
| | | ]).then(([modelRes, testStandardRes]) => { |
| | | // è®¾ç½®è§æ ¼åå·é项 |
| | | modelOptions.value = modelRes || []; |
| | | // å¦æè¡¨åä¸å·²æ productModelIdï¼è®¾ç½®å¯¹åºç model å unit |
| | | if (form.value.productModelId && modelOptions.value.length > 0) { |
| | | const selectedModel = modelOptions.value.find( |
| | | item => item.id == form.value.productModelId |
| | | ); |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel) { |
| | | form.value.model = selectedModel.model || ""; |
| | | form.value.unit = selectedModel.unit || ""; |
| | | form.value.model = selectedModel.model || ''; |
| | | form.value.unit = selectedModel.unit || ''; |
| | | } |
| | | } |
| | | |
| | |
| | | nextTick(() => { |
| | | if (savedTestStandardId) { |
| | | // ç¡®ä¿ç±»åå¹é
ï¼item.id å¯è½æ¯æ°åæåç¬¦ä¸²ï¼ |
| | | const matchedOption = testStandardOptions.value.find( |
| | | item => |
| | | item.id == savedTestStandardId || |
| | | String(item.id) === String(savedTestStandardId) |
| | | const matchedOption = testStandardOptions.value.find(item => |
| | | item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // ç¡®ä¿ä½¿ç¨å¹é
项ç idï¼ä¿æç±»åä¸è´ï¼ |
| | | form.value.testStandardId = matchedOption.id; |
| | | } else { |
| | | // 妿æ¾ä¸å°å¹é
项ï¼å°è¯ç´æ¥ä½¿ç¨åå¼ |
| | | console.warn( |
| | | "æªæ¾å°å¹é
çææ é项ï¼testStandardId:", |
| | | savedTestStandardId, |
| | | "å¯ç¨é项:", |
| | | testStandardOptions.value |
| | | ); |
| | | console.warn('æªæ¾å°å¹é
çææ é项ï¼testStandardId:', savedTestStandardId, 'å¯ç¨é项:', testStandardOptions.value); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | } |
| | | } |
| | |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } |
| | | }; |
| | | } |
| | | const getProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const getModels = value => { |
| | | const getModels = (value) => { |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value; |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then(res => { |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | }) |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | const handleChangeModel = value => { |
| | | form.value.model = |
| | | modelOptions.value.find(item => item.id == value)?.model || ""; |
| | | form.value.unit = |
| | | modelOptions.value.find(item => item.id == value)?.unit || ""; |
| | | }; |
| | | const handleChangeModel = (value) => { |
| | | form.value.model = modelOptions.value.find(item => item.id == value)?.model || ''; |
| | | form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || ''; |
| | | } |
| | | |
| | | const handleQualifiedQuantityChange = value => { |
| | | const handleQualifiedQuantityChange = (value) => { |
| | | if (value === null || value === undefined) { |
| | | form.value.qualifiedQuantity = 0; |
| | | return; |
| | |
| | | form.value.unqualifiedQuantity = Math.max(0, quantity - qualified); |
| | | }; |
| | | |
| | | const handleUnqualifiedQuantityChange = value => { |
| | | const handleUnqualifiedQuantityChange = (value) => { |
| | | if (value === null || value === undefined) { |
| | | form.value.unqualifiedQuantity = 0; |
| | | return; |
| | | } |
| | | const quantity = parseFloat(form.value.quantity) || 0; |
| | | const unqualified = parseFloat(value) || 0; |
| | | form.value.unqualifiedQuantity = |
| | | unqualified > quantity ? quantity : unqualified; |
| | | form.value.unqualifiedQuantity = unqualified > quantity?quantity:unqualified; |
| | | form.value.qualifiedQuantity = Math.max(0, quantity - unqualified); |
| | | }; |
| | | |
| | |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | |
| | | if (valid) { |
| | | form.value.inspectType = 2; |
| | | if (operationType.value === "add") { |
| | | tableData.value.forEach(item => { |
| | | tableData.value.forEach((item) => { |
| | | delete item.id; |
| | | }); |
| | | } |
| | |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | } |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | |
| | | } |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2, |
| | | inspectType: 2 |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // ä¿å䏿æ¡éé¡¹æ°æ® |
| | |
| | | // æ¸
ç©ºè¡¨æ ¼æ°æ®ï¼çå¾
ç¨æ·éæ©ææ |
| | | tableData.value = []; |
| | | // æ¸
ç©ºææ éæ© |
| | | form.value.testStandardId = ""; |
| | | form.value.testStandardId = ''; |
| | | }); |
| | | }; |
| | | } |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = testStandardId => { |
| | | const handleTestStandardChange = (testStandardId) => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | getQualityTestStandardParamByTestStandardId(testStandardId).then(res => { |
| | | tableData.value = res.data || []; |
| | | tableData.value = tableData.value.map(item => ({ |
| | | ...item, |
| | | id: null, |
| | | id: null |
| | | })); |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·åæ ååæ°å¤±è´¥:", error); |
| | | }).catch(error => { |
| | | console.error('è·åæ ååæ°å¤±è´¥:', error); |
| | | tableData.value = []; |
| | | }) |
| | | .finally(() => { |
| | | }).finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | const getQualityInspectParamList = id => { |
| | | } |
| | | const getQualityInspectParamList = (id) => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }); |
| | | }; |
| | | } |
| | | // æ¯å¦æ½æ£åæ¢ |
| | | const handleSamplingChange = (value) => { |
| | | if (value !== 1) { |
| | | form.value.samplingQuantity = ""; |
| | | nextTick(() => { |
| | | proxy.$refs?.formRef?.clearValidate?.("samplingQuantity"); |
| | | }); |
| | | } |
| | | } |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | tableData.value = []; |
| | | testStandardOptions.value = []; |
| | | form.value.testStandardId = ""; |
| | | form.value.testStandardId = ''; |
| | | dialogFormVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | | emit('close'); |
| | | } |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .sampling-radio-group { |
| | | height: 32px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form mb20"> |
| | | <el-form ref="searchFormRef" |
| | | :model="searchForm" |
| | | class="demo-form-inline"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName"> |
| | | <el-input v-model="searchForm.productName" |
| | | <div> |
| | | <span class="search_title">产ååç§°ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥äº§ååç§°æç´¢" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="æ£æµæ¥æ" |
| | | prop="entryDate"> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="è¯·éæ©" |
| | | @change="handleQuery" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="éå®åå·" |
| | | prop="salesContractNo"> |
| | | <el-input v-model="searchForm.salesContractNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥éå®åå·æç´¢" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="ç产工åå·" |
| | | prop="workOrderNo"> |
| | | <el-input v-model="searchForm.workOrderNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥ç产工åå·æç´¢" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <!-- æé® --> |
| | | <el-col :span="4"> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getList"> |
| | | æç´¢ |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <div class="actions"> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢</el-button> |
| | | :prefix-icon="Search" |
| | | /> |
| | | <span style="margin-left: 10px" class="search_title">æ£æµæ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange"/> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢ |
| | | </el-button |
| | | > |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">æ°å¢</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"></PIMTable> |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | </div> |
| | | <InspectionFormDia ref="inspectionFormDia" |
| | | @close="handleQuery"></InspectionFormDia> |
| | | <FormDia ref="formDia" |
| | | @close="handleQuery"></FormDia> |
| | | <files-dia ref="filesDia" |
| | | @close="handleQuery"></files-dia> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | title="ç¼è¾æ£éªå" |
| | | width="30%" |
| | | <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia> |
| | | <FormDia ref="formDia" @close="handleQuery"></FormDia> |
| | | <files-dia ref="filesDia" ></files-dia> |
| | | <el-dialog v-model="dialogFormVisible" title="ç¼è¾æ£éªå" width="30%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-form-item label="æ£éªåï¼" |
| | | prop="checkName"> |
| | | <el-select v-model="form.checkName" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-form-item label="æ£éªåï¼" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="è¯·éæ©" clearable> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" |
| | | :value="item.nickName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue"; |
| | | import InspectionFormDia from "@/views/qualityManagement/finalInspection/components/inspectionFormDia.vue"; |
| | | import FormDia from "@/views/qualityManagement/finalInspection/components/formDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | downloadQualityInspect, |
| | | qualityInspectDel, |
| | | qualityInspectListPage, |
| | | qualityInspectUpdate, |
| | | submitQualityInspect, |
| | | qualityInspectListPage, qualityInspectUpdate, |
| | | submitQualityInspect |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue"; |
| | | import dayjs from "dayjs"; |
| | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | productName: "", |
| | | salesContractNo: "", |
| | | workOrderNo: "", |
| | | entryDate: undefined, // å½å
¥æ¥æ |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | |
| | | { |
| | | label: "æ£æµæ¥æ", |
| | | prop: "checkTime", |
| | | width: 120, |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "éå®åå·", |
| | | prop: "salesContractNo", |
| | | width: 120, |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "ç产工åå·", |
| | | prop: "workOrderNo", |
| | | width: 120, |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "æ£éªå", |
| | |
| | | { |
| | | label: "æ»æ°é", |
| | | prop: "quantity", |
| | | width: 100, |
| | | width: 100 |
| | | }, |
| | | { |
| | | label: "åæ ¼æ°é", |
| | | prop: "qualifiedQuantity", |
| | | width: 100, |
| | | width: 100 |
| | | }, |
| | | { |
| | | label: "ä¸åæ ¼æ°é", |
| | | prop: "unqualifiedQuantity", |
| | | width: 100, |
| | | width: 100 |
| | | }, |
| | | { |
| | | label: "æ£æµåä½", |
| | | prop: "checkCompany", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "åæ ¼ç", |
| | | prop: "passRate", |
| | | width: 100, |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | if (!params) return ""; |
| | | const rate = parseFloat(params); |
| | | if (rate < 90) { |
| | | return "danger"; |
| | | } else if (rate === 100) { |
| | | return "success"; |
| | | } else { |
| | | return "warning"; |
| | | } |
| | | formatData: (params) => { |
| | | if (params == null || params === '') return 'â'; |
| | | const n = Number(params); |
| | | if (Number.isNaN(n)) return 'â'; |
| | | return `${n.toFixed(2)}%`; |
| | | }, |
| | | }, |
| | | { |
| | | label: "æ£æµåä½", |
| | | prop: "checkCompany", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "æ£æµç»æ", |
| | | prop: "checkResult", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | if (params == "ä¸åæ ¼") { |
| | | return "danger"; |
| | | } else if (params == "åæ ¼") { |
| | | return "success"; |
| | | } else { |
| | | return "danger"; |
| | | } |
| | | formatType: (params) => { |
| | | if (params == null || params === '') return 'info'; |
| | | const n = Number(params); |
| | | if (Number.isNaN(n)) return 'info'; |
| | | if (n === 100) return 'success'; |
| | | if (n >= 75) return 'primary'; |
| | | if (n >= 50) return 'warning'; |
| | | if (n >= 25) return 'danger'; |
| | | return 'danger'; |
| | | }, |
| | | }, |
| | | { |
| | | label: "æäº¤ç¶æ", |
| | | prop: "inspectState", |
| | | formatData: params => { |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | if (params) { |
| | | return "å·²æäº¤"; |
| | | } else { |
| | | return "æªæäº¤"; |
| | | } |
| | | }, |
| | | formatType: (params) => { |
| | | if (params) { |
| | | return "success"; |
| | | } else { |
| | | return "info"; |
| | | } |
| | | }, |
| | | }, |
| | |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | }, |
| | | disabled: row => { |
| | | disabled: (row) => { |
| | | // å·²æäº¤åç¦ç¨ |
| | | if (row.inspectState == 1) return true; |
| | | // 妿æ£éªåæå¼ï¼åªæå½åç»å½ç¨æ·è½ç¼è¾ |
| | |
| | | return row.checkName !== userStore.nickName; |
| | | } |
| | | return false; |
| | | }, |
| | | } |
| | | }, |
| | | { |
| | | name: "æ¥ç", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | openForm("view", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "éä»¶", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | openFilesFormDia(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "æäº¤", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | submit(row.id); |
| | | }, |
| | | disabled: row => { |
| | | loading: (row) => submitLoadingId.value === row.id, |
| | | disabled: (row) => { |
| | | // å·²æäº¤åç¦ç¨ |
| | | if (row.inspectState == 1) return true; |
| | | // 妿æ£éªåæå¼ï¼åªæå½åç»å½ç¨æ·è½æäº¤ |
| | |
| | | return row.checkName !== userStore.nickName; |
| | | } |
| | | return false; |
| | | }, |
| | | } |
| | | }, |
| | | { |
| | | name: "åé
æ£éªå", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | if (!row.checkName) { |
| | | open(row); |
| | | open(row) |
| | | } else { |
| | | proxy.$modal.msgError("æ£éªåå·²åå¨"); |
| | | } |
| | | }, |
| | | disabled: row => { |
| | | disabled: (row) => { |
| | | return row.inspectState == 1 || row.checkName; |
| | | }, |
| | | } |
| | | }, |
| | | { |
| | | name: "ä¸è½½", |
| | | type: "text", |
| | | clickFun: row => { |
| | | clickFun: (row) => { |
| | | downLoadFile(row); |
| | | }, |
| | | }, |
| | |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const currentRow = ref(null); |
| | | const submitLoadingId = ref(null); |
| | | const currentRow = ref(null) |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | total: 0 |
| | | }); |
| | | const formDia = ref(); |
| | | const filesDia = ref(); |
| | | const inspectionFormDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const formDia = ref() |
| | | const filesDia = ref() |
| | | const inspectionFormDia = ref() |
| | | const {proxy} = getCurrentInstance() |
| | | const userStore = useUserStore() |
| | | const userList = ref([]); |
| | | const form = ref({ |
| | | checkName: "", |
| | | checkName: "" |
| | | }); |
| | | const dialogFormVisible = ref(false); |
| | | |
| | | const changeDaterange = value => { |
| | | const changeDaterange = (value) => { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | if (value) { |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = obj => { |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | qualityInspectListPage({ ...params, inspectType: 2 }) |
| | | .then(res => { |
| | | params.entryDate = undefined |
| | | qualityInspectListPage({...params, inspectType: 2}).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records.map(item => { |
| | | const quantity = parseFloat(item.quantity); |
| | | const qualifiedQuantity = parseFloat(item.qualifiedQuantity); |
| | | let passRate = null; |
| | | if (!isNaN(quantity) && !isNaN(qualifiedQuantity) && quantity > 0) { |
| | | passRate = ((qualifiedQuantity / quantity) * 100).toFixed(2) + "%"; |
| | | } |
| | | return { |
| | | ...item, |
| | | passRate: passRate, |
| | | }; |
| | | }); |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | // æå¼æ°å¢æ£éªå¼¹æ¡ |
| | | const openInspectionForm = (type, row) => { |
| | | nextTick(() => { |
| | | inspectionFormDia.value?.openDialog(type, row); |
| | | }); |
| | | inspectionFormDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | const openFilesFormDia = (type, row) => { |
| | | nextTick(() => { |
| | | filesDia.value?.openDialog(type, row); |
| | | }); |
| | | filesDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | qualityInspectDel(ids).then(res => { |
| | | qualityInspectDel(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download( |
| | | "/quality/qualityInspect/export", |
| | | { inspectType: 2 }, |
| | | "åºåæ£éª.xlsx" |
| | | ); |
| | | proxy.download("/quality/qualityInspect/export", {inspectType: 2}, "åºåæ£éª.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // æä»· |
| | | const submit = async id => { |
| | | const res = await submitQualityInspect({ id: id }); |
| | | // æäº¤ |
| | | const submit = async (id) => { |
| | | submitLoadingId.value = id; |
| | | try { |
| | | const res = await submitQualityInspect({ id }); |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | getList(); |
| | | const row = tableData.value.find(item => item.id === id); |
| | | if (row) { |
| | | row.inspectState = 1; |
| | | } |
| | | }; |
| | | } |
| | | } finally { |
| | | submitLoadingId.value = null; |
| | | } |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | |
| | | if (currentRow.value) { |
| | | const data = { |
| | | ...form.value, |
| | | id: currentRow.value.id, |
| | | }; |
| | | id: currentRow.value.id |
| | | } |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | }) |
| | | } |
| | | }; |
| | | |
| | | const open = async row => { |
| | | const open = async (row) => { |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | currentRow.value = row; |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | currentRow.value = row |
| | | dialogFormVisible.value = true |
| | | } |
| | | |
| | | const downLoadFile = row => { |
| | | downloadQualityInspect({ id: row.id }).then(blobData => { |
| | | const downLoadFile = (row) => { |
| | | downloadQualityInspect({id: row.id}).then((blobData) => { |
| | | const blob = new Blob([blobData], { |
| | | type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", |
| | | }); |
| | | const downloadUrl = window.URL.createObjectURL(blob); |
| | | type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
| | | }) |
| | | const downloadUrl = window.URL.createObjectURL(blob) |
| | | |
| | | const link = document.createElement("a"); |
| | | link.href = downloadUrl; |
| | | link.download = "åæææ£éªæ¥å.docx"; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | const link = document.createElement('a') |
| | | link.href = downloadUrl |
| | | link.download = 'åæææ£éªæ¥å.docx' |
| | | document.body.appendChild(link) |
| | | link.click() |
| | | |
| | | document.body.removeChild(link); |
| | | window.URL.revokeObjectURL(downloadUrl); |
| | | }); |
| | | document.body.removeChild(link) |
| | | window.URL.revokeObjectURL(downloadUrl) |
| | | }) |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| | | <style scoped></style> |
| | |
| | | <template> |
| | | <div class="app-container metric-binding"> |
| | | <el-row :gutter="16" |
| | | class="metric-binding-row"> |
| | | <el-row :gutter="16" class="metric-binding-row"> |
| | | <!-- å·¦ä¾§ï¼æ£æµæ åå表 --> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="14" |
| | | :xl="14" |
| | | class="left-col"> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="14" :xl="14" class="left-col"> |
| | | <div class="panel left-panel"> |
| | | <PIMTable rowKey="id" |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="standardColumns" |
| | | :tableData="standardTableData" |
| | | :page="page" |
| | |
| | | :tableLoading="tableLoading" |
| | | :rowClick="handleTableRowClick" |
| | | @pagination="handlePagination" |
| | | :total="page.total"> |
| | | :total="page.total" |
| | | > |
| | | <template #standardNoCell="{ row }"> |
| | | <span class="clickable-link" |
| | | @click="handleStandardRowClick(row)"> |
| | | <span class="clickable-link" @click="handleStandardRowClick(row)"> |
| | | {{ row.standardNo }} |
| | | </span> |
| | | </template> |
| | | |
| | | <!-- 表头æç´¢ --> |
| | | <template #standardNoHeader> |
| | | <el-input v-model="searchForm.standardNo" |
| | | <el-input |
| | | v-model="searchForm.standardNo" |
| | | placeholder="æ åç¼å·" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #standardNameHeader> |
| | | <el-input v-model="searchForm.standardName" |
| | | <el-input |
| | | v-model="searchForm.standardName" |
| | | placeholder="æ ååç§°" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #inspectTypeHeader> |
| | | <el-select v-model="searchForm.inspectType" |
| | | <el-select |
| | | v-model="searchForm.inspectType" |
| | | placeholder="ç±»å«" |
| | | clearable |
| | | size="small" |
| | | style="width: 120px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery"> |
| | | <el-option label="åæææ£éª" |
| | | value="0" /> |
| | | <el-option label="è¿ç¨æ£éª" |
| | | value="1" /> |
| | | <el-option label="åºåæ£éª" |
| | | value="2" /> |
| | | @clear="handleQuery" |
| | | > |
| | | <el-option label="åæææ£éª" value="0" /> |
| | | <el-option label="è¿ç¨æ£éª" value="1" /> |
| | | <el-option label="åºåæ£éª" value="2" /> |
| | | </el-select> |
| | | </template> |
| | | <template #stateHeader> |
| | | <el-select v-model="searchForm.state" |
| | | <el-select |
| | | v-model="searchForm.state" |
| | | placeholder="ç¶æ" |
| | | clearable |
| | | size="small" |
| | | style="width: 110px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery"> |
| | | <el-option label="è稿" |
| | | value="0" /> |
| | | <el-option label="éè¿" |
| | | value="1" /> |
| | | <el-option label="æ¤é" |
| | | value="2" /> |
| | | @clear="handleQuery" |
| | | > |
| | | <el-option label="è稿" value="0" /> |
| | | <el-option label="éè¿" value="1" /> |
| | | <el-option label="æ¤é" value="2" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-col> |
| | | |
| | | <!-- å³ä¾§ï¼ç»å®å表 --> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="10" |
| | | :xl="10" |
| | | class="right-col"> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="10" :xl="10" class="right-col"> |
| | | <div class="panel right-panel"> |
| | | <div class="right-header"> |
| | | <div class="title">ç»å®å
³ç³»</div> |
| | | <div class="desc" |
| | | v-if="currentStandard"> |
| | | <div class="desc" v-if="currentStandard"> |
| | | å½åæ£æµæ åç¼å·ï¼<span class="link-text">{{ currentStandard.standardNo }}</span> |
| | | </div> |
| | | <div class="desc" |
| | | v-else>è¯·éæ©å·¦ä¾§æ£æµæ å</div> |
| | | <div class="desc" v-else>è¯·éæ©å·¦ä¾§æ£æµæ å</div> |
| | | </div> |
| | | |
| | | <div class="right-toolbar"> |
| | | <el-button type="primary" |
| | | :disabled="!currentStandard" |
| | | @click="openBindingDialog">æ·»å ç»å®</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | :disabled="!currentStandard" |
| | | @click="handleBatchUnbind">å é¤</el-button> |
| | | <el-button type="primary" :disabled="!currentStandard" @click="openBindingDialog">æ·»å ç»å®</el-button> |
| | | <el-button type="danger" plain :disabled="!currentStandard" @click="handleBatchUnbind">å é¤</el-button> |
| | | </div> |
| | | <el-table v-loading="bindingLoading" |
| | | |
| | | <el-table |
| | | v-loading="bindingLoading" |
| | | :data="bindingTableData" |
| | | border |
| | | :row-class-name="() => 'row-center'" |
| | | class="center-table" |
| | | style="width: 100%" |
| | | height="calc(100vh - 220px)" |
| | | @selection-change="handleBindingSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="48" |
| | | align="center" /> |
| | | <el-table-column type="index" |
| | | label="åºå·" |
| | | width="60" |
| | | align="center" /> |
| | | <el-table-column prop="productName" |
| | | label="产ååç§°" |
| | | min-width="140" /> |
| | | <el-table-column label="æä½" |
| | | width="120" |
| | | fixed="right" |
| | | align="center"> |
| | | @selection-change="handleBindingSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="48" align="center" /> |
| | | <el-table-column type="index" label="åºå·" width="60" align="center" /> |
| | | <el-table-column prop="productName" label="产ååç§°" min-width="140" /> |
| | | <el-table-column label="æä½" width="120" fixed="right" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button link |
| | | type="danger" |
| | | size="small" |
| | | @click="handleUnbind(row)">å é¤</el-button> |
| | | <el-button link type="danger" size="small" @click="handleUnbind(row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- æ·»å ç»å®å¼¹æ¡ --> |
| | | <el-dialog v-model="bindingDialogVisible" |
| | | <el-dialog |
| | | v-model="bindingDialogVisible" |
| | | title="æ·»å ç»å®" |
| | | width="520px" |
| | | @close="closeBindingDialog"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产å"> |
| | | <PIMTreeSelect v-model="selectedProductIds" |
| | | multiple |
| | | filterable |
| | | collapse-tags |
| | | collapse-tags-tooltip |
| | | placeholder="è¯·éæ©äº§åï¼å¯å¤éï¼" |
| | | width="640px" |
| | | @close="closeBindingDialog" |
| | | > |
| | | <div class="binding-dialog"> |
| | | <el-input |
| | | v-model="productSearchKeyword" |
| | | placeholder="æç´¢äº§å" |
| | | clearable |
| | | prefix-icon="Search" |
| | | class="binding-search" |
| | | /> |
| | | <el-tree |
| | | ref="productTreeRef" |
| | | :key="productTreeKey" |
| | | v-loading="productTreeLoading" |
| | | :data="productTreeData" |
| | | show-checkbox |
| | | check-strictly |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | node-key="id" |
| | | :props="{ label: 'label', children: 'children', disabled: 'disabled' }" |
| | | :filter-node-method="filterProductNode" |
| | | class="product-binding-tree" |
| | | @check="handleProductTreeCheck" |
| | | /> |
| | | <div v-if="selectedProductLabels.length" class="selected-products"> |
| | | <div class="selected-title">已鿩ï¼{{ selectedProductLabels.length }}ï¼</div> |
| | | <div class="selected-tags"> |
| | | <el-tag |
| | | v-for="item in selectedProductLabels" |
| | | :key="item.id" |
| | | closable |
| | | type="info" |
| | | @close="removeSelectedProduct(item.id)" |
| | | > |
| | | {{ item.label }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="closeBindingDialog">åæ¶</el-button> |
| | | <el-button type="primary" |
| | | @click="submitBinding">ç¡®å®</el-button> |
| | | <el-button type="primary" @click="submitBinding">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import PIMTreeSelect from "@/components/PIMTreeSelect/index.vue"; |
| | | import { productTreeList } from "@/api/basicData/product.js"; |
| | | import { qualityTestStandardListPage } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { productProcessListPage } from "@/api/basicData/productProcess.js"; |
| | | import { ref, reactive, toRefs, computed, watch, nextTick, onMounted, getCurrentInstance } from 'vue' |
| | | import { ElMessageBox } from 'element-plus' |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | import { productTreeList } from '@/api/basicData/product.js' |
| | | import { |
| | | qualityTestStandardListPage |
| | | } from '@/api/qualityManagement/metricMaintenance.js' |
| | | import { productProcessListPage } from '@/api/basicData/productProcess.js' |
| | | import { |
| | | qualityTestStandardBindingList, |
| | | qualityTestStandardBindingAdd, |
| | | qualityTestStandardBindingDel, |
| | | } from "@/api/qualityManagement/qualityTestStandardBinding.js"; |
| | | qualityTestStandardBindingDel |
| | | } from '@/api/qualityManagement/qualityTestStandardBinding.js' |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | // 左侧æ ååè¡¨ï¼æ´è¡å
容å±
ä¸ï¼é
åæ ·å¼ï¼ |
| | | const rowClassNameCenter = () => "row-center"; |
| | | const rowClassNameCenter = () => 'row-center' |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | standardNo: "", |
| | | standardName: "", |
| | | state: "", |
| | | inspectType: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | standardNo: '', |
| | | standardName: '', |
| | | state: '', |
| | | inspectType: '' |
| | | } |
| | | }) |
| | | const { searchForm } = toRefs(data) |
| | | |
| | | // 左侧 |
| | | const standardTableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ current: 1, size: 10, total: 0 }); |
| | | const standardTableData = ref([]) |
| | | const tableLoading = ref(false) |
| | | const page = reactive({ current: 1, size: 10, total: 0 }) |
| | | |
| | | // å·¥åºä¸æï¼ç¨äºåè¡¨åæ¾ï¼ |
| | | const processOptions = ref([]); |
| | | const processOptions = ref([]) |
| | | |
| | | const getProcessList = async () => { |
| | | try { |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }); |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }) |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || []; |
| | | processOptions.value = records.map(item => ({ |
| | | const records = res?.data?.records || [] |
| | | processOptions.value = records.map((item) => ({ |
| | | label: item.processName || item.name || item.label, |
| | | value: item.id || item.processId || item.value, |
| | | })); |
| | | value: item.id || item.processId || item.value |
| | | })) |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åå·¥åºå表失败:", error); |
| | | console.error('è·åå·¥åºå表失败:', error) |
| | | } |
| | | }; |
| | | } |
| | | |
| | | const standardColumns = ref([ |
| | | { label: 'æ åç¼å·', prop: 'standardNo', dataType: 'slot', slot: 'standardNoCell', minWidth: 160, align: 'center', headerSlot: 'standardNoHeader' }, |
| | | { label: 'æ ååç§°', prop: 'standardName', minWidth: 180, align: 'center', headerSlot: 'standardNameHeader' }, |
| | | { |
| | | label: "æ åç¼å·", |
| | | prop: "standardNo", |
| | | dataType: "slot", |
| | | slot: "standardNoCell", |
| | | minWidth: 160, |
| | | align: "center", |
| | | headerSlot: "standardNoHeader", |
| | | label: 'ç±»å«', |
| | | prop: 'inspectType', |
| | | headerSlot: 'inspectTypeHeader', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const map = { 0: 'åæææ£éª', 1: 'è¿ç¨æ£éª', 2: 'åºåæ£éª' } |
| | | return map[val] || val |
| | | } |
| | | }, |
| | | { |
| | | label: "æ ååç§°", |
| | | prop: "standardName", |
| | | minWidth: 180, |
| | | align: "center", |
| | | headerSlot: "standardNameHeader", |
| | | }, |
| | | { |
| | | label: "ç±»å«", |
| | | prop: "inspectType", |
| | | headerSlot: "inspectTypeHeader", |
| | | align: "center", |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | const map = { 0: "åæææ£éª", 1: "è¿ç¨æ£éª", 2: "åºåæ£éª" }; |
| | | return map[val] || val; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å·¥åº", |
| | | prop: "processId", |
| | | align: "center", |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | label: 'å·¥åº', |
| | | prop: 'processId', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const target = processOptions.value.find( |
| | | item => String(item.value) === String(val) |
| | | ); |
| | | return target?.label || val; |
| | | }, |
| | | (item) => String(item.value) === String(val) |
| | | ) |
| | | return target?.label || val |
| | | } |
| | | }, |
| | | { |
| | | label: "夿³¨", |
| | | prop: "remark", |
| | | label: '夿³¨', |
| | | prop: 'remark', |
| | | minWidth: 160, |
| | | align: "center", |
| | | }, |
| | | align: 'center' |
| | | } |
| | | // { |
| | | // label: 'ç¶æ', |
| | | // prop: 'state', |
| | |
| | | // return 'info' |
| | | // } |
| | | // } |
| | | ]); |
| | | ]) |
| | | |
| | | const currentStandard = ref(null); |
| | | const currentStandard = ref(null) |
| | | |
| | | // å³ä¾§ç»å® |
| | | const bindingTableData = ref([]); |
| | | const bindingLoading = ref(false); |
| | | const bindingSelectedRows = ref([]); |
| | | const bindingDialogVisible = ref(false); |
| | | const bindingTableData = ref([]) |
| | | const bindingLoading = ref(false) |
| | | const bindingSelectedRows = ref([]) |
| | | const bindingDialogVisible = ref(false) |
| | | |
| | | // äº§åæ ï¼ç¨äºç»å®éæ©ï¼ |
| | | const productOptions = ref([]); |
| | | const selectedProductIds = ref([]); |
| | | const productTreeData = ref([]) |
| | | const productTreeLoading = ref(false) |
| | | const productTreeRef = ref(null) |
| | | const productTreeKey = ref(0) |
| | | const productSearchKeyword = ref('') |
| | | const selectedProductIds = ref([]) |
| | | |
| | | const getProductOptions = async () => { |
| | | // é¿å
éå¤è¯·æ± |
| | | if (productOptions.value?.length) return; |
| | | const res = await productTreeList(); |
| | | productOptions.value = convertIdToValue(Array.isArray(res) ? res : []); |
| | | }; |
| | | |
| | | function convertIdToValue(data) { |
| | | return (data || []).map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | const markParentNodesDisabled = (nodes) => { |
| | | return (nodes || []).map((node) => { |
| | | const children = node.children?.length ? markParentNodesDisabled(node.children) : [] |
| | | return { |
| | | ...node, |
| | | children, |
| | | disabled: children.length > 0 |
| | | } |
| | | return newItem; |
| | | }); |
| | | }) |
| | | } |
| | | |
| | | const buildProductLabelMap = (nodes, map = {}) => { |
| | | ;(nodes || []).forEach((node) => { |
| | | if (node.id != null) { |
| | | map[node.id] = node.label |
| | | } |
| | | if (node.children?.length) { |
| | | buildProductLabelMap(node.children, map) |
| | | } |
| | | }) |
| | | return map |
| | | } |
| | | |
| | | const productLabelMap = computed(() => buildProductLabelMap(productTreeData.value)) |
| | | |
| | | const selectedProductLabels = computed(() => |
| | | selectedProductIds.value.map((id) => ({ |
| | | id, |
| | | label: productLabelMap.value[id] || id |
| | | })) |
| | | ) |
| | | |
| | | const filterProductNode = (value, data) => { |
| | | if (!value) return true |
| | | return String(data.label || '').includes(value) |
| | | } |
| | | |
| | | watch(productSearchKeyword, (val) => { |
| | | productTreeRef.value?.filter(val) |
| | | }) |
| | | |
| | | const getProductTreeData = async () => { |
| | | if (productTreeData.value?.length) return |
| | | productTreeLoading.value = true |
| | | try { |
| | | const res = await productTreeList() |
| | | productTreeData.value = markParentNodesDisabled(Array.isArray(res) ? res : []) |
| | | } catch (error) { |
| | | console.error('è·åäº§åæ å¤±è´¥:', error) |
| | | } finally { |
| | | productTreeLoading.value = false |
| | | } |
| | | } |
| | | |
| | | const handleProductTreeCheck = () => { |
| | | selectedProductIds.value = productTreeRef.value?.getCheckedKeys(true) || [] |
| | | } |
| | | |
| | | const removeSelectedProduct = (id) => { |
| | | selectedProductIds.value = selectedProductIds.value.filter((item) => item !== id) |
| | | productTreeRef.value?.setChecked(id, false, false) |
| | | } |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getStandardList(); |
| | | }; |
| | | page.current = 1 |
| | | getStandardList() |
| | | } |
| | | |
| | | const handlePagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getStandardList(); |
| | | }; |
| | | const handlePagination = (obj) => { |
| | | page.current = obj.page |
| | | page.size = obj.limit |
| | | getStandardList() |
| | | } |
| | | |
| | | const getStandardList = () => { |
| | | tableLoading.value = true; |
| | | tableLoading.value = true |
| | | qualityTestStandardListPage({ |
| | | ...searchForm.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | state: 1, |
| | | state: 1 |
| | | }) |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | | standardTableData.value = records; |
| | | page.total = res?.data?.total || records.length; |
| | | .then((res) => { |
| | | const records = res?.data?.records || [] |
| | | standardTableData.value = records |
| | | page.total = res?.data?.total || records.length |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | // è¡¨æ ¼è¡ç¹å»ï¼å è½½å³ä¾§ç»å®å表 |
| | | const handleTableRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadBindingList(); |
| | | }; |
| | | const handleTableRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadBindingList() |
| | | } |
| | | |
| | | // 左侧è¡ç¹å»ï¼å è½½å³ä¾§ç»å®å表ï¼ä¿çç¨äºæ åç¼å·åçç¹å»ï¼ |
| | | const handleStandardRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadBindingList(); |
| | | }; |
| | | const handleStandardRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadBindingList() |
| | | } |
| | | |
| | | const loadBindingList = () => { |
| | | if (!currentStandard.value?.id) { |
| | | bindingTableData.value = []; |
| | | return; |
| | | bindingTableData.value = [] |
| | | return |
| | | } |
| | | bindingLoading.value = true; |
| | | bindingLoading.value = true |
| | | qualityTestStandardBindingList({ testStandardId: currentStandard.value.id }) |
| | | .then(res => { |
| | | const base = res?.data || []; |
| | | .then((res) => { |
| | | const base = res?.data || [] |
| | | // å°å½åæ åçå·¥åºå夿³¨å¸¦å°ç»å®å表ä¸å±ç¤º |
| | | bindingTableData.value = base.map(item => ({ |
| | | bindingTableData.value = base.map((item) => ({ |
| | | ...item, |
| | | processId: currentStandard.value?.processId, |
| | | remark: currentStandard.value?.remark, |
| | | })); |
| | | remark: currentStandard.value?.remark |
| | | })) |
| | | }) |
| | | .finally(() => { |
| | | bindingLoading.value = false; |
| | | }); |
| | | }; |
| | | bindingLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | const handleBindingSelectionChange = selection => { |
| | | bindingSelectedRows.value = selection; |
| | | }; |
| | | const handleBindingSelectionChange = (selection) => { |
| | | bindingSelectedRows.value = selection |
| | | } |
| | | |
| | | const openBindingDialog = () => { |
| | | if (!currentStandard.value?.id) return; |
| | | selectedProductIds.value = []; |
| | | getProductOptions(); |
| | | bindingDialogVisible.value = true; |
| | | }; |
| | | if (!currentStandard.value?.id) return |
| | | selectedProductIds.value = [] |
| | | productSearchKeyword.value = '' |
| | | productTreeKey.value += 1 |
| | | bindingDialogVisible.value = true |
| | | nextTick(() => { |
| | | productTreeRef.value?.setCheckedKeys([]) |
| | | productTreeRef.value?.filter('') |
| | | }) |
| | | } |
| | | |
| | | const closeBindingDialog = () => { |
| | | bindingDialogVisible.value = false; |
| | | }; |
| | | bindingDialogVisible.value = false |
| | | selectedProductIds.value = [] |
| | | productSearchKeyword.value = '' |
| | | productTreeRef.value?.setCheckedKeys([]) |
| | | } |
| | | |
| | | const submitBinding = async () => { |
| | | const testStandardId = currentStandard.value?.id; |
| | | if (!testStandardId) return; |
| | | const ids = (selectedProductIds.value || []).filter(Boolean); |
| | | const testStandardId = currentStandard.value?.id |
| | | if (!testStandardId) return |
| | | const ids = (selectedProductIds.value || []).filter(Boolean) |
| | | if (!ids.length) { |
| | | proxy.$message.warning("è¯·éæ©äº§å"); |
| | | return; |
| | | proxy.$message.warning('è¯·éæ©äº§å') |
| | | return |
| | | } |
| | | const payload = ids.map(pid => ({ |
| | | const payload = ids.map((pid) => ({ |
| | | productId: pid, |
| | | testStandardId, |
| | | })); |
| | | await qualityTestStandardBindingAdd(payload); |
| | | proxy.$message.success("æ·»å æå"); |
| | | bindingDialogVisible.value = false; |
| | | loadBindingList(); |
| | | }; |
| | | testStandardId |
| | | })) |
| | | await qualityTestStandardBindingAdd(payload) |
| | | proxy.$message.success('æ·»å æå') |
| | | bindingDialogVisible.value = false |
| | | loadBindingList() |
| | | } |
| | | |
| | | const handleUnbind = async row => { |
| | | const id = row?.id ?? row?.qualityTestStandardBindingId; |
| | | if (id == null || id === "") return; |
| | | const handleUnbind = async (row) => { |
| | | const id = row?.id ?? row?.qualityTestStandardBindingId |
| | | if (id == null || id === '') return |
| | | try { |
| | | await ElMessageBox.confirm("确认å é¤è¯¥ç»å®ï¼", "æç¤º", { type: "warning" }); |
| | | await ElMessageBox.confirm('确认å é¤è¯¥ç»å®ï¼', 'æç¤º', { type: 'warning' }) |
| | | } catch { |
| | | return; |
| | | return |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel([id]); |
| | | proxy.$message.success("å 餿å"); |
| | | loadBindingList(); |
| | | await qualityTestStandardBindingDel([id]) |
| | | proxy.$message.success('å 餿å') |
| | | loadBindingList() |
| | | } catch (err) { |
| | | console.error("å é¤ç»å®å¤±è´¥:", err); |
| | | proxy.$message?.error(err?.message || "å é¤å¤±è´¥"); |
| | | console.error('å é¤ç»å®å¤±è´¥:', err) |
| | | proxy.$message?.error(err?.message || 'å é¤å¤±è´¥') |
| | | } |
| | | }; |
| | | } |
| | | |
| | | const handleBatchUnbind = async () => { |
| | | if (!bindingSelectedRows.value.length) { |
| | | proxy.$message.warning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | proxy.$message.warning('è¯·éæ©æ°æ®') |
| | | return |
| | | } |
| | | const ids = bindingSelectedRows.value |
| | | .map(i => i?.id ?? i?.qualityTestStandardBindingId) |
| | | .filter(id => id != null && id !== ""); |
| | | .map((i) => i?.id ?? i?.qualityTestStandardBindingId) |
| | | .filter((id) => id != null && id !== '') |
| | | if (!ids.length) { |
| | | proxy.$message.warning("é䏿°æ®ç¼ºå°ææ id"); |
| | | return; |
| | | proxy.$message.warning('é䏿°æ®ç¼ºå°ææ id') |
| | | return |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | "éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", |
| | | "å é¤æç¤º", |
| | | { type: "warning" } |
| | | ); |
| | | await ElMessageBox.confirm('éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼', 'å é¤æç¤º', { type: 'warning' }) |
| | | } catch { |
| | | return; |
| | | return |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel(ids); |
| | | proxy.$message.success("å 餿å"); |
| | | loadBindingList(); |
| | | await qualityTestStandardBindingDel(ids) |
| | | proxy.$message.success('å 餿å') |
| | | loadBindingList() |
| | | } catch (err) { |
| | | console.error("æ¹éå é¤ç»å®å¤±è´¥:", err); |
| | | proxy.$message?.error(err?.message || "å é¤å¤±è´¥"); |
| | | console.error('æ¹éå é¤ç»å®å¤±è´¥:', err) |
| | | proxy.$message?.error(err?.message || 'å é¤å¤±è´¥') |
| | | } |
| | | }; |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getStandardList(); |
| | | getProcessList(); |
| | | }); |
| | | getStandardList() |
| | | getProcessList() |
| | | getProductTreeData() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | width: 100%; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .binding-dialog { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .binding-search { |
| | | width: 100%; |
| | | } |
| | | |
| | | .product-binding-tree { |
| | | max-height: 360px; |
| | | overflow-y: auto; |
| | | border: 1px solid #ebeef5; |
| | | border-radius: 4px; |
| | | padding: 8px; |
| | | } |
| | | |
| | | .selected-products { |
| | | border-top: 1px solid #ebeef5; |
| | | padding-top: 12px; |
| | | } |
| | | |
| | | .selected-title { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .selected-tags { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="queryParams" |
| | | ref="queryForm" |
| | | :inline="true" |
| | | v-show="showSearch" |
| | | label-width="68px"> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName"> |
| | | <el-input v-model="queryParams.productName" |
| | | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> |
| | | <el-form-item label="产ååç§°" prop="productName"> |
| | | <el-input |
| | | v-model="queryParams.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°" |
| | | clearable |
| | | @keyup.enter.native="handleQuery" /> |
| | | @keyup.enter.native="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¹æ¬¡å·" |
| | | prop="batchNumber"> |
| | | <el-input v-model="queryParams.batchNumber" |
| | | <el-form-item label="æ¹æ¬¡å·" prop="batchNumber"> |
| | | <el-input |
| | | v-model="queryParams.batchNumber" |
| | | placeholder="请è¾å
¥æ¹æ¬¡å·" |
| | | clearable |
| | | @keyup.enter.native="handleQuery" /> |
| | | @keyup.enter.native="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="é忥æ" |
| | | prop="returnDate"> |
| | | <el-date-picker clearable |
| | | <el-form-item label="é忥æ" prop="returnDate"> |
| | | <el-date-picker |
| | | clearable |
| | | v-model="queryParams.returnDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | |
| | | </el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | icon="Search" |
| | | @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="Refresh" |
| | | @click="resetQuery">éç½®</el-button> |
| | | <el-button type="primary" icon="Search" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="Refresh" @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <el-row :gutter="10" |
| | | class="mb8"> |
| | | |
| | | <el-row :gutter="10" class="mb8"> |
| | | <el-col :span="1.5"> |
| | | <el-button type="primary" |
| | | <el-button |
| | | type="primary" |
| | | plain |
| | | icon="Plus" |
| | | @click="handleAdd">æ°å¢</el-button> |
| | | @click="handleAdd" |
| | | >æ°å¢</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="success" |
| | | <el-button |
| | | type="success" |
| | | plain |
| | | icon="Edit" |
| | | :disabled="single" |
| | | @click="handleUpdate">ä¿®æ¹</el-button> |
| | | @click="handleUpdate" |
| | | >ä¿®æ¹</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="danger" |
| | | <el-button |
| | | type="danger" |
| | | plain |
| | | icon="Delete" |
| | | :disabled="multiple" |
| | | @click="handleDelete">å é¤</el-button> |
| | | @click="handleDelete" |
| | | >å é¤</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="warning" |
| | | <el-button |
| | | type="warning" |
| | | plain |
| | | icon="Download" |
| | | @click="handleExport">导åº</el-button> |
| | | @click="handleExport" |
| | | >导åº</el-button> |
| | | </el-col> |
| | | <right-toolbar v-model:showSearch="showSearch" |
| | | @queryTable="getList"></right-toolbar> |
| | | <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> |
| | | </el-row> |
| | | <el-table v-loading="loading" |
| | | :data="nearExpiryReturnList" |
| | | @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="55" |
| | | align="center" /> |
| | | <el-table-column label="åºå·" |
| | | type="index" |
| | | width="50" |
| | | align="center" /> |
| | | <el-table-column label="产ååç§°" |
| | | prop="productName" /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | | prop="productSpec" /> |
| | | <el-table-column label="æ¹æ¬¡å·" |
| | | prop="batchNumber" /> |
| | | <el-table-column label="çäº§æ¥æ" |
| | | prop="productionDate" |
| | | align="center"> |
| | | |
| | | <el-table v-loading="loading" :data="nearExpiryReturnList" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="åºå·" type="index" width="50" align="center" /> |
| | | <el-table-column label="产ååç§°" prop="productName" /> |
| | | <el-table-column label="产åè§æ ¼" prop="productSpec" /> |
| | | <el-table-column label="æ¹æ¬¡å·" prop="batchNumber" /> |
| | | <el-table-column label="çäº§æ¥æ" prop="productionDate" align="center"> |
| | | <template #default="scope"> |
| | | <span>{{ parseTime(scope.row.productionDate, '{y}-{m}-{d}') }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å°ææ¥æ" |
| | | prop="expiryDate" |
| | | align="center"> |
| | | <el-table-column label="å°ææ¥æ" prop="expiryDate" align="center"> |
| | | <template #default="scope"> |
| | | <span>{{ parseTime(scope.row.expiryDate, '{y}-{m}-{d}') }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éåæ°é" |
| | | prop="returnQuantity" /> |
| | | <el-table-column label="éååå " |
| | | prop="returnReason" /> |
| | | <el-table-column label="é忥æ" |
| | | prop="returnDate" |
| | | align="center"> |
| | | <el-table-column label="éåæ°é" prop="returnQuantity" /> |
| | | <el-table-column label="éååå " prop="returnReason" /> |
| | | <el-table-column label="é忥æ" prop="returnDate" align="center"> |
| | | <template #default="scope"> |
| | | <span>{{ parseTime(scope.row.returnDate, '{y}-{m}-{d}') }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¤çç¶æ" |
| | | prop="status" |
| | | align="center"> |
| | | <el-table-column label="å¤çç¶æ" prop="status" align="center"> |
| | | <template #default="scope"> |
| | | <dict-tag :options="statusOptions" |
| | | :value="scope.row.status" /> |
| | | <dict-tag :options="statusOptions" :value="scope.row.status"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | align="center" |
| | | class-name="small-padding fixed-width"> |
| | | <el-table-column label="æä½" align="center" class-name="small-padding fixed-width"> |
| | | <template #default="scope"> |
| | | <el-button size="mini" |
| | | type="text" |
| | | icon="Edit" |
| | | @click="handleUpdate(scope.row)">ä¿®æ¹</el-button> |
| | | <el-button size="mini" |
| | | type="text" |
| | | icon="Delete" |
| | | @click="handleDelete(scope.row)">å é¤</el-button> |
| | | <el-button size="mini" type="text" icon="Edit" @click="handleUpdate(scope.row)">ä¿®æ¹</el-button> |
| | | <el-button size="mini" type="text" icon="Delete" @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total>0" |
| | | |
| | | <pagination |
| | | v-show="total>0" |
| | | :total="total" |
| | | v-model:page="queryParams.pageNum" |
| | | v-model:limit="queryParams.pageSize" |
| | | @pagination="getList" /> |
| | | @pagination="getList" |
| | | /> |
| | | |
| | | <!-- æ·»å æä¿®æ¹ä¸´æéåå°è´¦å¯¹è¯æ¡ --> |
| | | <el-dialog :title="title" |
| | | v-model="open" |
| | | width="800px" |
| | | append-to-body> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="100px"> |
| | | <el-dialog :title="title" v-model="open" width="800px" append-to-body> |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="100px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName"> |
| | | <el-input v-model="form.productName" |
| | | placeholder="请è¾å
¥äº§ååç§°" /> |
| | | <el-form-item label="产ååç§°" prop="productName"> |
| | | <el-input v-model="form.productName" placeholder="请è¾å
¥äº§ååç§°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | prop="productSpec"> |
| | | <el-input v-model="form.productSpec" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" /> |
| | | <el-form-item label="产åè§æ ¼" prop="productSpec"> |
| | | <el-input v-model="form.productSpec" placeholder="请è¾å
¥äº§åè§æ ¼" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¹æ¬¡å·" |
| | | prop="batchNumber"> |
| | | <el-input v-model="form.batchNumber" |
| | | placeholder="请è¾å
¥æ¹æ¬¡å·" /> |
| | | <el-form-item label="æ¹æ¬¡å·" prop="batchNumber"> |
| | | <el-input v-model="form.batchNumber" placeholder="请è¾å
¥æ¹æ¬¡å·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éåæ°é" |
| | | prop="returnQuantity"> |
| | | <el-input-number v-model="form.returnQuantity" |
| | | controls-position="right" |
| | | :min="1" /> |
| | | <el-form-item label="éåæ°é" prop="returnQuantity"> |
| | | <el-input-number v-model="form.returnQuantity" controls-position="right" :min="1" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="çäº§æ¥æ" |
| | | prop="productionDate"> |
| | | <el-date-picker clearable |
| | | <el-form-item label="çäº§æ¥æ" prop="productionDate"> |
| | | <el-date-picker |
| | | clearable |
| | | v-model="form.productionDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å°ææ¥æ" |
| | | prop="expiryDate"> |
| | | <el-date-picker clearable |
| | | <el-form-item label="å°ææ¥æ" prop="expiryDate"> |
| | | <el-date-picker |
| | | clearable |
| | | v-model="form.expiryDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é忥æ" |
| | | prop="returnDate"> |
| | | <el-date-picker clearable |
| | | <el-form-item label="é忥æ" prop="returnDate"> |
| | | <el-date-picker |
| | | clearable |
| | | v-model="form.returnDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¤çç¶æ" |
| | | prop="status"> |
| | | <el-select v-model="form.status" |
| | | placeholder="è¯·éæ©å¤çç¶æ"> |
| | | <el-option v-for="dict in statusOptions" |
| | | <el-form-item label="å¤çç¶æ" prop="status"> |
| | | <el-select v-model="form.status" placeholder="è¯·éæ©å¤çç¶æ"> |
| | | <el-option |
| | | v-for="dict in statusOptions" |
| | | :key="dict.value" |
| | | :label="dict.label" |
| | | :value="dict.value"></el-option> |
| | | :value="dict.value" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éååå " |
| | | prop="returnReason"> |
| | | <el-input v-model="form.returnReason" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥éååå " /> |
| | | <el-form-item label="éååå " prop="returnReason"> |
| | | <el-input v-model="form.returnReason" type="textarea" placeholder="请è¾å
¥éååå " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥å¤æ³¨" /> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">ç¡® å®</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡® å®</el-button> |
| | | <el-button @click="cancel">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | const statusOptions = ref([ |
| | | { label: "å¾
å¤ç", value: "0" }, |
| | | { label: "å¤çä¸", value: "1" }, |
| | | { label: "已宿", value: "2" }, |
| | | { label: "已宿", value: "2" } |
| | | ]); |
| | | |
| | | const data = reactive({ |
| | |
| | | pageSize: 10, |
| | | productName: null, |
| | | batchNumber: null, |
| | | returnDate: null, |
| | | returnDate: null |
| | | }, |
| | | rules: { |
| | | productName: [ |
| | | { required: true, message: "产ååç§°ä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "产ååç§°ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | productSpec: [ |
| | | { required: true, message: "è§æ ¼åå·ä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "产åè§æ ¼ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | batchNumber: [ |
| | | { required: true, message: "æ¹æ¬¡å·ä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "æ¹æ¬¡å·ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | returnQuantity: [ |
| | | { required: true, message: "éåæ°éä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "éåæ°éä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | productionDate: [ |
| | | { required: true, message: "çäº§æ¥æä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "çäº§æ¥æä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | expiryDate: [ |
| | | { required: true, message: "å°ææ¥æä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "å°ææ¥æä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | returnDate: [ |
| | | { required: true, message: "é忥æä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "é忥æä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | returnReason: [ |
| | | { required: true, message: "éååå ä¸è½ä¸ºç©º", trigger: "blur" }, |
| | | { required: true, message: "éååå ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | status: [ |
| | | { required: true, message: "å¤çç¶æä¸è½ä¸ºç©º", trigger: "change" }, |
| | | ], |
| | | }, |
| | | { required: true, message: "å¤çç¶æä¸è½ä¸ºç©º", trigger: "change" } |
| | | ] |
| | | } |
| | | }); |
| | | |
| | | const { queryParams, form, rules } = toRefs(data); |
| | |
| | | returnReason: null, |
| | | returnDate: null, |
| | | status: null, |
| | | remark: null, |
| | | remark: null |
| | | }; |
| | | proxy.resetForm("formRef"); |
| | | } |
| | |
| | | /** å é¤æé®æä½ */ |
| | | function handleDelete(row) { |
| | | const deleteIds = row.id || ids.value; |
| | | ElMessageBox.confirm( |
| | | 'æ¯å¦ç¡®è®¤å é¤ä¸´æéåå°è´¦ç¼å·ä¸º"' + deleteIds + '"çæ°æ®é¡¹ï¼', |
| | | "è¦å", |
| | | { |
| | | ElMessageBox.confirm('æ¯å¦ç¡®è®¤å é¤ä¸´æéåå°è´¦ç¼å·ä¸º"' + deleteIds + '"çæ°æ®é¡¹ï¼', "è¦å", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(function () { |
| | | type: "warning" |
| | | }).then(function() { |
| | | // ä¸è°ç¨æ¥å£ï¼åªæ¾ç¤ºæåæç¤º |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }) |
| | | .catch(() => {}); |
| | | }).catch(() => {}); |
| | | } |
| | | |
| | | /** å¯¼åºæé®æä½ */ |
| src/views/qualityManagement/nonconformingManagement/components/formDia.vue
src/views/qualityManagement/processInspection/components/formDia.vue
src/views/qualityManagement/processInspection/index.vue
src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
src/views/qualityManagement/rawMaterialInspection/index.vue
src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue
src/views/reportAnalysis/PSIDataAnalysis/components/ProductTypeSwitch.vue
src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
src/views/reportAnalysis/PSIDataAnalysis/index.vue
src/views/reportAnalysis/PSIDataAnalysis/psiNavigation.js
src/views/reportAnalysis/dataDashboard/components/PanelHeader.vue
src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
src/views/reportAnalysis/productionAnalysis/components/PanelHeader.vue
src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
src/views/reportAnalysis/productionAnalysis/components/center-center.vue
src/views/reportAnalysis/productionAnalysis/components/center-top.vue
src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
src/views/reportAnalysis/productionAnalysis/components/left-top.vue
src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
src/views/reportAnalysis/productionAnalysis/components/right-top.vue
src/views/reportAnalysis/productionAnalysis/productionNavigation.js
src/views/reportAnalysis/reportManagement/index.vue
src/views/safeProduction/dangerInvestigation/index.vue
src/views/safeProduction/safetyTrainingAssessment/index.vue
src/views/salesManagement/deliveryLedger/index.vue
src/views/salesManagement/indicatorStats/index.vue
src/views/salesManagement/receiptPaymentLedger/index.vue
src/views/salesManagement/returnOrder/components/detailDia.vue
src/views/salesManagement/returnOrder/components/formDia.vue
src/views/salesManagement/returnOrder/index.vue
src/views/salesManagement/salesLedger/index.vue
src/views/salesManagement/salesQuotation/index.vue
src/views/system/dept/index.vue
src/views/system/dict/data.vue
src/views/system/dict/index.vue
src/views/system/menu/index.vue
src/views/system/post/index.vue
src/views/system/role/index.vue
src/views/systemArchitecture/index.vue
vite.config.js |