huminmin
12 小时以前 87d86e5816558d1e9989b0cea9046478284eff89
Merge branch 'dev_New' of http://114.132.189.42:9002/r/product-inventory-management into dev_New
已添加8个文件
已修改10个文件
4093 ■■■■ 文件已修改
src/api/collaborativeApproval/shipmentReview.js 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/employeeRecord.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/metricMaintenance.js 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/chartCard.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/chartCard2.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/chartCard3.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PIMTable/PIMTable.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/shipmentReview/fileList.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/shipmentReview/index.vue 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/formDia.vue 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/metricBinding/index.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/metricMaintenance/index.vue 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/formDia.vue 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/dataDashboard/index.vue 596 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/reportManagement/index.vue 1997 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/fileList.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 485 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/collaborativeApproval/shipmentReview.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
// å‘货审批
import request from "@/utils/request";
// èŽ·å–å‘è´§å®¡æ‰¹åˆ—è¡¨
export function getShipmentApprovalList(query) {
    return request({
        url: '/shipmentApproval/listPage',
        method: 'get',
        params: query,
    })
}
// å‘货申请批准
// /shipmentApproval/update
export function approveShipment(query) {
    return request({
        url: '/shipmentApproval/update',
        method: 'post',
        data: query,
    })
}
src/api/personnelManagement/employeeRecord.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
import request from '@/utils/request'
// æŸ¥è¯¢åœ¨èŒå‘˜å·¥å°è´¦
export function staffOnJobListPage(query) {
    return request({
        url: '/staff/staffOnJob/listPage',
        method: 'get',
        params: query,
    })
}
// æŸ¥è¯¢å‘˜å·¥å…¥èŒä¿¡æ¯
export function staffOnJobInfo(query) {
    return request({
        url: '/staff/staffOnJob/staffNo',
        method: 'get',
        params: query,
    })
}
// å¯¼å‡ºåˆåŒå‰¯æœ¬
export function staffOnJobExportCopy(data) {
    return request({
        url: '/staff/staffOnJob/exportCopy',
        method: 'post',
        data: data,
    })
}
src/api/qualityManagement/metricMaintenance.js
@@ -37,10 +37,11 @@
}
// åˆ é™¤æŒ‡æ ‡åˆ—表
export function qualityInspectDetailByProductId(productId) {
export function qualityInspectDetailByProductId(params) {
  return request({
    url: "/qualityTestStandard/product/" + productId,
    url: "/qualityTestStandard/getQualityTestStandardByProductId",
    method: "get",
    params: params,
  });
}
@@ -98,3 +99,12 @@
    data: ids,
  });
}
// æ ¹æ®æ ‡å‡†ID获取标准参数
export function getQualityTestStandardParamByTestStandardId(testStandardId) {
  return request({
    url: "/qualityTestStandard/getQualityTestStandardParamByTestStandardId",
    method: "get",
    params: { testStandardId },
  });
}
src/assets/images/chartCard.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="40" height="40" viewBox="0 0 40 40"><defs><mask id="master_svg0_88_35670" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="40" height="40"><ellipse cx="20" cy="20" rx="20" ry="20" fill="#FFFFFF" fill-opacity="1"/></mask><clipPath id="master_svg1_88_35666"><rect x="7" y="7" width="27" height="27" rx="0"/></clipPath><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg2_88_26531"><stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"/><stop offset="98.57142567634583%" stop-color="#F0FBFF" stop-opacity="1"/></linearGradient><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg3_88_26531"><stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"/><stop offset="98.57142567634583%" stop-color="#F0FBFF" stop-opacity="1"/></linearGradient></defs><g mask="url(#master_svg0_88_35670)"><ellipse cx="20" cy="20" rx="20" ry="20" fill="#0092FF" fill-opacity="1"/><g clip-path="url(#master_svg1_88_35666)"><path d="M21.175671875,27.58515925L14.263672875000001,27.58515925C13.750673275,27.58515925,13.426672974999999,27.24765625,13.426672974999999,26.74815725C13.426672974999999,26.23515525,13.764173075,25.911160250000002,14.263672875000001,25.911160250000002L21.351173875,25.911160250000002C21.688676875,24.89865825,22.188173875,23.88615425,22.863174875,23.211156250000002L14.263672875000001,23.211156250000002C13.750673275,23.211156250000002,13.426672974999999,22.87365525,13.426672974999999,22.37415225C13.426672974999999,21.87465325,13.764173075,21.537155249999998,14.263672875000001,21.537155249999998L25.738675875,21.537155249999998C26.251674875,21.37515325,26.751174875,21.37515325,27.088676875,21.37515325C28.438678875,21.37515325,29.626676875,21.88815525,30.625678875,22.549656249999998L30.625678875,13.072656349999999C30.625678875,11.38515625,29.275674875,10.03515625,27.588174875,10.03515625L27.075177875,10.03515625L27.075177875,13.24815675C27.075177875,14.935656550000001,25.725173875,16.285657450000002,24.037676875000002,16.285657450000002L16.113174475,16.285657450000002C14.425674475000001,16.272157149999998,13.075673375000001,14.922158249999999,13.075673375000001,13.23465635L13.075673375000001,10.03515625L12.238672475,10.03515625C10.551171974999999,10.03515625,9.201171875,11.38515625,9.201171875,13.072656349999999L9.201171875,29.94765825C9.201171875,31.63515625,10.551171974999999,32.985161250000004,12.238672475,32.985161250000004L25.576673875,32.985161250000004C23.200674875,32.485662250000004,21.337675875000002,30.28515825,21.175671875,27.58515925Z" fill="url(#master_svg2_88_26531)" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M16.1124145625,14.764538762499999L24.0504169625,14.764538762499999C24.8874170625,14.764538762499999,25.5624140625,14.0895385625,25.5624140625,13.252537762500001L25.5624140625,10.0395388625L22.5249171625,10.0395388625C22.3629159625,8.8650390625,21.3369150625,7.8525390625,19.986915562500002,7.8525390625C18.7989153625,7.8525390625,17.7864150625,8.8650390625,17.6244149625,10.0395388625L14.5869140625,10.0395388625L14.5869140625,13.252537762500001C14.5869140625,14.0895385625,15.2619143725,14.764538762499999,16.1124145625,14.764538762499999ZM30.7869150625,24.3900370625C29.9499160625,23.3775360625,28.5999220625,22.7025380625,27.2499170625,22.7025380625L26.412916062500003,22.7025380625C25.8999180625,22.7025380625,25.5759160625,22.8780390625,25.0629190625,23.2155400625C24.0504169625,23.7285370625,23.1999158625,24.7275330625,22.700415562499998,25.9155390625C22.5384173625,26.4285390625,22.5384173625,26.9280380625,22.5384173625,27.4275380625L22.5384173625,27.5895390625C22.700415562499998,30.1275410625,24.7254170625,31.9770390625,27.1014200625,31.9770390625C28.4514180625,31.9770390625,29.8014230625,31.3020400625,30.6384180625,30.2895320625C31.3134210625,29.4525340625,31.6509170625,28.4265380625,31.6509170625,27.2520330625C31.8129160625,26.2395310625,31.2999250625,25.2270370625,30.7869150625,24.3900370625ZM29.7879200625,26.5770380625L27.0879160625,29.2770390625C26.7504160625,29.6145400625,26.412916062500003,29.6145400625,26.0754160625,29.2770390625L24.387915562499998,27.5895390625C24.0504169625,27.2520370625,24.0504169625,26.9145390625,24.387915562499998,26.5770380625C24.725415062499998,26.2395380625,25.0629150625,26.2395400625,25.4004160625,26.5770380625L26.2374170625,27.4140400625L26.5749150625,27.7515370625L28.7619150625,25.5645350625C29.0994140625,25.2270370625,29.4369190625,25.2270370625,29.774416062500002,25.5645350625C30.1119160625,25.9020370625,30.1119160625,26.2395380625,29.7879200625,26.5770380625Z" fill="url(#master_svg3_88_26531)" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
src/assets/images/chartCard2.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="40" height="40" viewBox="0 0 40 40"><defs><mask id="master_svg0_88_35670" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="40" height="40"><ellipse cx="20" cy="20" rx="20" ry="20" fill="#FFFFFF" fill-opacity="1"/></mask><clipPath id="master_svg1_88_35666"><rect x="7" y="7" width="27" height="27" rx="0"/></clipPath><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg2_88_26531"><stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"/><stop offset="98.57142567634583%" stop-color="#F0FBFF" stop-opacity="1"/></linearGradient><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg3_88_26531"><stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"/><stop offset="98.57142567634583%" stop-color="#F0FBFF" stop-opacity="1"/></linearGradient></defs><g mask="url(#master_svg0_88_35670)"><ellipse cx="20" cy="20" rx="20" ry="20" fill="#5EB334" fill-opacity="1"/><g clip-path="url(#master_svg1_88_35666)"><path d="M21.175671875,27.58515925L14.263672875000001,27.58515925C13.750673275,27.58515925,13.426672974999999,27.24765625,13.426672974999999,26.74815725C13.426672974999999,26.23515525,13.764173075,25.911160250000002,14.263672875000001,25.911160250000002L21.351173875,25.911160250000002C21.688676875,24.89865825,22.188173875,23.88615425,22.863174875,23.211156250000002L14.263672875000001,23.211156250000002C13.750673275,23.211156250000002,13.426672974999999,22.87365525,13.426672974999999,22.37415225C13.426672974999999,21.87465325,13.764173075,21.537155249999998,14.263672875000001,21.537155249999998L25.738675875,21.537155249999998C26.251674875,21.37515325,26.751174875,21.37515325,27.088676875,21.37515325C28.438678875,21.37515325,29.626676875,21.88815525,30.625678875,22.549656249999998L30.625678875,13.072656349999999C30.625678875,11.38515625,29.275674875,10.03515625,27.588174875,10.03515625L27.075177875,10.03515625L27.075177875,13.24815675C27.075177875,14.935656550000001,25.725173875,16.285657450000002,24.037676875000002,16.285657450000002L16.113174475,16.285657450000002C14.425674475000001,16.272157149999998,13.075673375000001,14.922158249999999,13.075673375000001,13.23465635L13.075673375000001,10.03515625L12.238672475,10.03515625C10.551171974999999,10.03515625,9.201171875,11.38515625,9.201171875,13.072656349999999L9.201171875,29.94765825C9.201171875,31.63515625,10.551171974999999,32.985161250000004,12.238672475,32.985161250000004L25.576673875,32.985161250000004C23.200674875,32.485662250000004,21.337675875000002,30.28515825,21.175671875,27.58515925Z" fill="url(#master_svg2_88_26531)" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M16.1124145625,14.764538762499999L24.0504169625,14.764538762499999C24.8874170625,14.764538762499999,25.5624140625,14.0895385625,25.5624140625,13.252537762500001L25.5624140625,10.0395388625L22.5249171625,10.0395388625C22.3629159625,8.8650390625,21.3369150625,7.8525390625,19.986915562500002,7.8525390625C18.7989153625,7.8525390625,17.7864150625,8.8650390625,17.6244149625,10.0395388625L14.5869140625,10.0395388625L14.5869140625,13.252537762500001C14.5869140625,14.0895385625,15.2619143725,14.764538762499999,16.1124145625,14.764538762499999ZM30.7869150625,24.3900370625C29.9499160625,23.3775360625,28.5999220625,22.7025380625,27.2499170625,22.7025380625L26.412916062500003,22.7025380625C25.8999180625,22.7025380625,25.5759160625,22.8780390625,25.0629190625,23.2155400625C24.0504169625,23.7285370625,23.1999158625,24.7275330625,22.700415562499998,25.9155390625C22.5384173625,26.4285390625,22.5384173625,26.9280380625,22.5384173625,27.4275380625L22.5384173625,27.5895390625C22.700415562499998,30.1275410625,24.7254170625,31.9770390625,27.1014200625,31.9770390625C28.4514180625,31.9770390625,29.8014230625,31.3020400625,30.6384180625,30.2895320625C31.3134210625,29.4525340625,31.6509170625,28.4265380625,31.6509170625,27.2520330625C31.8129160625,26.2395310625,31.2999250625,25.2270370625,30.7869150625,24.3900370625ZM29.7879200625,26.5770380625L27.0879160625,29.2770390625C26.7504160625,29.6145400625,26.412916062500003,29.6145400625,26.0754160625,29.2770390625L24.387915562499998,27.5895390625C24.0504169625,27.2520370625,24.0504169625,26.9145390625,24.387915562499998,26.5770380625C24.725415062499998,26.2395380625,25.0629150625,26.2395400625,25.4004160625,26.5770380625L26.2374170625,27.4140400625L26.5749150625,27.7515370625L28.7619150625,25.5645350625C29.0994140625,25.2270370625,29.4369190625,25.2270370625,29.774416062500002,25.5645350625C30.1119160625,25.9020370625,30.1119160625,26.2395380625,29.7879200625,26.5770380625Z" fill="url(#master_svg3_88_26531)" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
src/assets/images/chartCard3.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="40" height="40" viewBox="0 0 40 40"><defs><mask id="master_svg0_88_35670" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="40" height="40"><ellipse cx="20" cy="20" rx="20" ry="20" fill="#FFFFFF" fill-opacity="1"/></mask><clipPath id="master_svg1_88_35666"><rect x="7" y="7" width="27" height="27" rx="0"/></clipPath><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg2_88_26531"><stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"/><stop offset="98.57142567634583%" stop-color="#F0FBFF" stop-opacity="1"/></linearGradient><linearGradient x1="0.5" y1="0" x2="0.5" y2="1" id="master_svg3_88_26531"><stop offset="0%" stop-color="#FFFFFF" stop-opacity="1"/><stop offset="98.57142567634583%" stop-color="#F0FBFF" stop-opacity="1"/></linearGradient></defs><g mask="url(#master_svg0_88_35670)"><ellipse cx="20" cy="20" rx="20" ry="20" fill="#8000FF" fill-opacity="1"/><g clip-path="url(#master_svg1_88_35666)"><path d="M21.175671875,27.58515925L14.263672875000001,27.58515925C13.750673275,27.58515925,13.426672974999999,27.24765625,13.426672974999999,26.74815725C13.426672974999999,26.23515525,13.764173075,25.911160250000002,14.263672875000001,25.911160250000002L21.351173875,25.911160250000002C21.688676875,24.89865825,22.188173875,23.88615425,22.863174875,23.211156250000002L14.263672875000001,23.211156250000002C13.750673275,23.211156250000002,13.426672974999999,22.87365525,13.426672974999999,22.37415225C13.426672974999999,21.87465325,13.764173075,21.537155249999998,14.263672875000001,21.537155249999998L25.738675875,21.537155249999998C26.251674875,21.37515325,26.751174875,21.37515325,27.088676875,21.37515325C28.438678875,21.37515325,29.626676875,21.88815525,30.625678875,22.549656249999998L30.625678875,13.072656349999999C30.625678875,11.38515625,29.275674875,10.03515625,27.588174875,10.03515625L27.075177875,10.03515625L27.075177875,13.24815675C27.075177875,14.935656550000001,25.725173875,16.285657450000002,24.037676875000002,16.285657450000002L16.113174475,16.285657450000002C14.425674475000001,16.272157149999998,13.075673375000001,14.922158249999999,13.075673375000001,13.23465635L13.075673375000001,10.03515625L12.238672475,10.03515625C10.551171974999999,10.03515625,9.201171875,11.38515625,9.201171875,13.072656349999999L9.201171875,29.94765825C9.201171875,31.63515625,10.551171974999999,32.985161250000004,12.238672475,32.985161250000004L25.576673875,32.985161250000004C23.200674875,32.485662250000004,21.337675875000002,30.28515825,21.175671875,27.58515925Z" fill="url(#master_svg2_88_26531)" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M16.1124145625,14.764538762499999L24.0504169625,14.764538762499999C24.8874170625,14.764538762499999,25.5624140625,14.0895385625,25.5624140625,13.252537762500001L25.5624140625,10.0395388625L22.5249171625,10.0395388625C22.3629159625,8.8650390625,21.3369150625,7.8525390625,19.986915562500002,7.8525390625C18.7989153625,7.8525390625,17.7864150625,8.8650390625,17.6244149625,10.0395388625L14.5869140625,10.0395388625L14.5869140625,13.252537762500001C14.5869140625,14.0895385625,15.2619143725,14.764538762499999,16.1124145625,14.764538762499999ZM30.7869150625,24.3900370625C29.9499160625,23.3775360625,28.5999220625,22.7025380625,27.2499170625,22.7025380625L26.412916062500003,22.7025380625C25.8999180625,22.7025380625,25.5759160625,22.8780390625,25.0629190625,23.2155400625C24.0504169625,23.7285370625,23.1999158625,24.7275330625,22.700415562499998,25.9155390625C22.5384173625,26.4285390625,22.5384173625,26.9280380625,22.5384173625,27.4275380625L22.5384173625,27.5895390625C22.700415562499998,30.1275410625,24.7254170625,31.9770390625,27.1014200625,31.9770390625C28.4514180625,31.9770390625,29.8014230625,31.3020400625,30.6384180625,30.2895320625C31.3134210625,29.4525340625,31.6509170625,28.4265380625,31.6509170625,27.2520330625C31.8129160625,26.2395310625,31.2999250625,25.2270370625,30.7869150625,24.3900370625ZM29.7879200625,26.5770380625L27.0879160625,29.2770390625C26.7504160625,29.6145400625,26.412916062500003,29.6145400625,26.0754160625,29.2770390625L24.387915562499998,27.5895390625C24.0504169625,27.2520370625,24.0504169625,26.9145390625,24.387915562499998,26.5770380625C24.725415062499998,26.2395380625,25.0629150625,26.2395400625,25.4004160625,26.5770380625L26.2374170625,27.4140400625L26.5749150625,27.7515370625L28.7619150625,25.5645350625C29.0994140625,25.2270370625,29.4369190625,25.2270370625,29.774416062500002,25.5645350625C30.1119160625,25.9020370625,30.1119160625,26.2395380625,29.7879200625,26.5770380625Z" fill="url(#master_svg3_88_26531)" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
src/components/PIMTable/PIMTable.vue
@@ -130,7 +130,7 @@
        </div>
        <!-- æŒ‰é’® -->
        <div v-else-if="item.dataType == 'action'">
        <div v-else-if="item.dataType == 'action'" @click.stop>
          <template v-for="(o, key) in item.operation" :key="key">
            <el-button
              v-show="o.type != 'upload'"
@@ -145,7 +145,7 @@
                    : o.color,
              }"
              link
              @click="o.clickFun(scope.row)"
              @click.stop="o.clickFun(scope.row)"
              :key="key"
            >
              {{ o.name }}
src/views/collaborativeApproval/shipmentReview/fileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
<template>
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose">
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="100" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
  <filePreview ref="filePreviewRef" />
</template>
<script setup>
import { ref } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
const dialogVisible = ref(false)
const tableData = ref([])
const { proxy } = getCurrentInstance();
const filePreviewRef = ref()
const handleClose = () => {
  dialogVisible.value = false
}
const open = (list) => {
  dialogVisible.value = true
  tableData.value = list
}
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
defineExpose({
  open
})
</script>
<style></style>
src/views/collaborativeApproval/shipmentReview/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,340 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">销售合同号:</span>
        <el-input
            v-model="searchForm.salesContractNo"
            style="width: 240px"
            placeholder="请输入销售合同号搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <span class="search_title ml10">审批状态:</span>
        <el-select v-model="searchForm.approveStatus" clearable @change="handleQuery" style="width: 240px">
          <el-option label="待审核" :value="2" />
          <el-option label="审核成功" :value="3" />
          <el-option label="审核失败" :value="4" />
        </el-select>
        <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>-->
      </div>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
    </div>
    <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="approveType"></info-form-dia>
    <approval-dia ref="approvalDia" @close="handleQuery"></approval-dia>
    <FileList ref="fileListRef" />
  </div>
</template>
<script setup>
import FileList from "./fileList.vue";
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import {ElMessageBox} from "element-plus";
import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
import {getShipmentApprovalList, approveShipment} from "@/api/collaborativeApproval/shipmentReview.js";
// import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
// å®šä¹‰ç»„件接收的props
const props = defineProps({
  approveType: {
    type: [Number, String],
    default: 6
  }
});
const userList = ref([]);
const userStore = useUserStore();
const data = reactive({
  searchForm: {
    approveId: "",
    approveStatus: "",
  },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
  {
    label: "审批状态",
    prop: "approveStatus",
    dataType: "tag",
    width: 100,
    formatData: (params) => {
      if (params === 2) {
        return "待审核";
      } else if (params === 3) {
        return "审核完成";
      } else if (params === 4) {
        return "审核驳回";
      } else {
        return '未知状态';
      }
    },
    formatType: (params) => {
      if (params === 0) {
        return "warning";
      } else if (params === 2) {
        return "info";
      } else if (params === 3) {
        return "success";
      } else if (params === 4) {
        return "danger";
      } else {
        return 'danger';
      }
    },
  },
  {
    label: "销售合同号",
    prop: "salesContractNo",
    width: 170
  },
  {
    label: "客户名称",
    prop: "customerName",
    width: 200
  },
  {
    label: "产品大类",
    prop: "productCategory",
    width: 200
  },
  {
    label: "规格型号",
    prop: "specificationModel",
    width: 220
  },
  {
    label: "申请人",
    prop: "approveUserId",
    width: 120,
    align: "center",
    formatData:(params)=>{
      const user = userList.value.find(item => item.userId === params)
      return user ? user.nickName : '--'
    }
  },
  {
    label: "车牌号",
    prop: "shippingCarNumber",
    width: 120,
  },
  {
    label: "申请人",
    prop: "approveUserId",
    width: 120,
  },
  {
    label: "申请日期",
    prop: "executionDate",
    width: 200
  },
  {
    label: "当前审批人",
    prop: "salesman",
    width: 120
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 120,
    operation: [
      {
        name: "通过",
        type: "text",
        clickFun: (row) => {
          handleApproval("通过", row);
        },
        disabled: (row) => row.approveStatus !== 2
      },
      {
        name: "驳回",
        type: "text",
        clickFun: (row) => {
          handleApproval("驳回", row);
        },
        disabled: (row) => row.approveStatus !== 2
      },
      // {
      //   name: "编辑",
      //   type: "text",
      //   clickFun: (row) => {
      //     openForm("edit", row);
      //   },
      //   disabled: (row) => row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
      // },
      // {
      //   name: "审核",
      //   type: "text",
      //   clickFun: (row) => {
      //     openApprovalDia("approval", row);
      //   },
      //   disabled: (row) => row.approveUserCurrentId == null || row.approveStatus == 2 || row.approveStatus == 3 || row.approveStatus == 4 || row.approveUserCurrentId !== userStore.id
      // },
      // {
      //   name: "详情",
      //   type: "text",
      //   clickFun: (row) => {
      //     openApprovalDia('view', row);
      //   },
      // },
      // {
      //   name: "附件",
      //   type: "text",
      //   clickFun: (row) => {
      //     downLoadFile(row);
      //   },
      // },
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
  total: 0
});
const infoFormDia = ref()
const approvalDia = ref()
const { proxy } = getCurrentInstance()
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const fileListRef = ref(null)
const downLoadFile = (row) => {
  fileListRef.value.open(row.commonFileList)
}
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList =async () => {
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  tableLoading.value = true;
  getShipmentApprovalList({...page, ...searchForm.value,approveType:props.approveType}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
// å¯¼å‡º
const handleOut = () => {
  const type = Number(props.approveType || 6)
  const urlMap = {
    0: "/shipmentApproval/export",
  }
  const url = urlMap[type] || urlMap[0]
  const nameMap = {
    0: "发货审核表",
  }
  const fileName = nameMap[type] || nameMap[0]
  proxy.download(url, {}, `${fileName}.xlsx`)
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æ‰“开新增、编辑弹框
const openForm = (type, row) => {
  nextTick(() => {
    infoFormDia.value?.openDialog(type, row)
  })
};
// æ‰“开新增检验弹框
const openApprovalDia = (type, row) => {
  nextTick(() => {
    approvalDia.value?.openDialog(type, row)
  })
};
// å®¡æ ¸é€šè¿‡/驳回
const handleApproval = (name = "审核",row) => {
  ElMessageBox.confirm(`选中的内容将被${name},是否确认${name}?`, "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(async()=>{
    let res = await approveShipment({
      id: row.id,
      approveStatus: name === "通过" ? 3 : 4
    });
    if(res.code === 200){
      proxy.$modal.msgSuccess(`${name}成功`);
    }else{
      proxy.$modal.msgError(`${name}失败`);
    }
    await getList()
  }).catch(err=>{
    proxy.$modal.msgError(`未知错误,请联系管理员`);
  })
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.approveId);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        approveProcessDelete(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
onMounted(() => {
  getList();
});
</script>
<style scoped></style>
src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -27,6 +27,24 @@
              <el-input v-model="form.model" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="指标选择:" prop="testStandardId">
              <el-select
                v-model="form.testStandardId"
                placeholder="请选择指标"
                clearable
                @change="handleTestStandardChange"
                style="width: 100%"
              >
                <el-option
                  v-for="item in testStandardOptions"
                  :key="item.id"
                  :label="item.standardName || item.standardNo"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
@@ -101,12 +119,12 @@
</template>
<script setup>
import {ref} from "vue";
import {ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import {getOptions} from "@/api/procurementManagement/procurementLedger.js";
import {productTreeList} from "@/api/basicData/product.js";
import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js";
import {userListNoPage} from "@/api/system/user.js";
import {qualityInspectDetailByProductId} 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'])
@@ -121,6 +139,7 @@
    productName: "",
    productId: "",
    model: "",
    testStandardId: "",
    unit: "",
    quantity: "",
    checkCompany: "",
@@ -132,6 +151,7 @@
    checkName: [{ required: false, message: "请输入", trigger: "blur" }],
    productId: [{ required: true, message: "请输入", trigger: "blur" }],
    model: [{ required: false, message: "请输入", trigger: "blur" }],
    testStandardId: [{required: true, message: "请选择指标", trigger: "change"}],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
@@ -169,6 +189,7 @@
const tableLoading = ref(false);
const userList = ref([]);
const currentProductId = ref(0);
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
// æ‰“开弹框
const openDialog = async (type, row) => {
@@ -180,11 +201,54 @@
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    form.value = {}
  testStandardOptions.value = [];
  tableData.value = [];
  getProductOptions();
  if (operationType.value === 'edit') {
    form.value = {...row}
    // å…ˆä¿å­˜ testStandardId,避免被清空
    const savedTestStandardId = row.testStandardId;
    // å…ˆè®¾ç½®è¡¨å•数据,但暂时清空 testStandardId,等选项加载完成后再设置
    form.value = {...row, testStandardId: ''}
        currentProductId.value = row.productId || 0
        getQualityInspectParamList(row.id)
        // ç¼–辑模式下,先加载指标选项,然后加载参数列表
        if (currentProductId.value) {
            // å…ˆåŠ è½½æŒ‡æ ‡é€‰é¡¹
            let params = {
                productId: currentProductId.value,
                inspectType: 2
            }
            qualityInspectDetailByProductId(params).then(res => {
                testStandardOptions.value = res.data || [];
                // ä½¿ç”¨ nextTick å’Œ setTimeout ç¡®ä¿é€‰é¡¹å·²ç»æ¸²æŸ“到 DOM
                nextTick(() => {
                    setTimeout(() => {
                        // å¦‚果编辑数据中有 testStandardId,则设置并加载对应的参数
                        if (savedTestStandardId) {
                            // ç¡®ä¿ç±»åž‹åŒ¹é…ï¼ˆitem.id å¯èƒ½æ˜¯æ•°å­—或字符串)
                            const matchedOption = testStandardOptions.value.find(item =>
                                item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
                            );
                            if (matchedOption) {
                                // ç¡®ä¿ä½¿ç”¨åŒ¹é…é¡¹çš„ id(保持类型一致)
                                form.value.testStandardId = matchedOption.id;
                console.log(22222,form.value.testStandardId);
                                handleTestStandardChange(matchedOption.id);
                            } else {
                                // å¦‚果找不到匹配项,尝试直接使用原值
                                console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value);
                                form.value.testStandardId = savedTestStandardId;
                                handleTestStandardChange(savedTestStandardId);
                            }
                        } else {
                            // å¦åˆ™ä½¿ç”¨æ—§çš„逻辑
                            getQualityInspectParamList(row.id);
                        }
                    }, 100);
                });
            });
        } else {
            getQualityInspectParamList(row.id);
        }
  }
}
const getProductOptions = () => {
@@ -195,7 +259,7 @@
const getModels = (value) => {
    currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
    if (currentProductId) {
    if (currentProductId.value) {
        getList();
    }
};
@@ -253,9 +317,40 @@
  })
}
const getList = () => {
    qualityInspectDetailByProductId(currentProductId.value).then(res => {
        tableData.value = res.data;
  if (!currentProductId.value) {
    testStandardOptions.value = [];
    tableData.value = [];
    return;
  }
  let params = {
    productId: currentProductId.value,
    inspectType: 2
  }
    qualityInspectDetailByProductId(params).then(res => {
        // ä¿å­˜ä¸‹æ‹‰æ¡†é€‰é¡¹æ•°æ®
        testStandardOptions.value = res.data || [];
        // æ¸…空表格数据,等待用户选择指标
        tableData.value = [];
        // æ¸…空指标选择
        form.value.testStandardId = '';
    })
}
// æŒ‡æ ‡é€‰æ‹©å˜åŒ–处理
const handleTestStandardChange = (testStandardId) => {
  if (!testStandardId) {
    tableData.value = [];
    return;
  }
  tableLoading.value = true;
  getQualityTestStandardParamByTestStandardId(testStandardId).then(res => {
    tableData.value = res.data || [];
  }).catch(error => {
    console.error('获取标准参数失败:', error);
    tableData.value = [];
  }).finally(() => {
    tableLoading.value = false;
  })
}
const getQualityInspectParamList = (id) => {
    qualityInspectParamInfo(id).then(res => {
@@ -265,6 +360,9 @@
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  tableData.value = [];
  testStandardOptions.value = [];
  form.value.testStandardId = '';
  dialogFormVisible.value = false;
  emit('close')
};
src/views/qualityManagement/metricBinding/index.vue
@@ -10,6 +10,7 @@
        :isSelection="false"
        :rowClassName="rowClassNameCenter"
        :tableLoading="tableLoading"
        :rowClick="handleTableRowClick"
        @pagination="handlePagination"
        :total="page.total"
      >
@@ -197,12 +198,13 @@
}
const standardColumns = ref([
  { label: '标准编号', prop: 'standardNo', dataType: 'slot', slot: 'standardNoCell', minWidth: 160, headerSlot: 'standardNoHeader' },
  { label: '标准名称', prop: 'standardName', minWidth: 180, headerSlot: 'standardNameHeader' },
  { label: '标准编号', prop: 'standardNo', dataType: 'slot', slot: 'standardNoCell', minWidth: 160, align: 'center', headerSlot: 'standardNoHeader' },
  { 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: '出厂检验' }
@@ -212,6 +214,7 @@
  {
    label: '工序',
    prop: 'processId',
    align: 'center',
    dataType: 'tag',
    formatData: (val) => {
      const target = processOptions.value.find(
@@ -223,7 +226,8 @@
  {
    label: '备注',
    prop: 'remark',
    minWidth: 160
    minWidth: 160,
    align: 'center'
  }
  // {
  //   label: '状态',
@@ -304,6 +308,13 @@
    })
}
// è¡¨æ ¼è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§ç»‘å®šåˆ—è¡¨
const handleTableRowClick = (row) => {
  currentStandard.value = row
  loadBindingList()
}
// å·¦ä¾§è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§ç»‘å®šåˆ—è¡¨ï¼ˆä¿ç•™ç”¨äºŽæ ‡å‡†ç¼–å·åˆ—çš„ç‚¹å‡»ï¼‰
const handleStandardRowClick = (row) => {
  currentStandard.value = row
  loadBindingList()
@@ -471,4 +482,23 @@
:deep(.center-table .el-table__body-wrapper td .cell) {
  text-align: center !important;
}
/* PIMTable è¡¨å¤´å±…中 */
:deep(.lims-table .pim-table-header-cell) {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
:deep(.lims-table .pim-table-header-title) {
  text-align: center;
  width: 100%;
}
:deep(.lims-table .pim-table-header-extra) {
  width: 100%;
  margin-top: 4px;
}
</style>
src/views/qualityManagement/metricMaintenance/index.vue
@@ -19,6 +19,7 @@
        :isSelection="true"
        :tableLoading="tableLoading"
        :rowClassName="rowClassNameCenter"
        :rowClick="handleTableRowClick"
        @selection-change="handleSelectionChange"
        @pagination="handlePagination"
        :total="page.total"
@@ -212,7 +213,7 @@
    standardNo: [{ required: true, message: '请输入标准编号', trigger: 'blur' }],
    standardName: [{ required: true, message: '请输入标准名称', trigger: 'blur' }],
    inspectType: [{ required: true, message: '请选择检测类型', trigger: 'change' }],
    processId: [{ required: true, message: '请选择工序', trigger: 'change' }]
    processId: [{ required: false, message: '请选择工序', trigger: 'change' }]
  }
})
@@ -277,18 +278,21 @@
    dataType: 'slot',
    slot: 'standardNoCell',
    minWidth: 160,
    align: 'center',
    headerSlot: 'standardNoHeader'
  },
  {
    label: '标准名称',
    prop: 'standardName',
    minWidth: 180,
    align: 'center',
    headerSlot: 'standardNameHeader'
  },
  {
    label: '类别',
    prop: 'inspectType',
    headerSlot: 'inspectTypeHeader',
    align: 'center',
    dataType: 'tag',
    formatData: (val) => {
      const map = {
@@ -302,6 +306,7 @@
  {
    label: '工序',
    prop: 'processId',
    align: 'center',
    dataType: 'tag',
    formatData: (val) => {
      const target = processOptions.value.find(
@@ -314,6 +319,7 @@
    label: '状态',
    prop: 'state',
    headerSlot: 'stateHeader',
    align: 'center',
    dataType: 'tag',
    formatData: (val) => {
      const map = {
@@ -332,7 +338,8 @@
  {
    label: '备注',
    prop: 'remark',
    minWidth: 160
    minWidth: 160,
    align: 'center'
  },
  {
    dataType: 'action',
@@ -447,7 +454,13 @@
  getStandardList()
}
// å·¦ä¾§è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§å‚æ•°
// è¡¨æ ¼è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§å‚æ•°
const handleTableRowClick = (row) => {
  currentStandard.value = row
  loadDetail(row.id)
}
// å·¦ä¾§è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§å‚æ•°ï¼ˆä¿ç•™ç”¨äºŽæ ‡å‡†ç¼–å·åˆ—çš„ç‚¹å‡»ï¼‰
const handleStandardRowClick = (row) => {
  currentStandard.value = row
  loadDetail(row.id)
@@ -567,14 +580,23 @@
      processId: ''
    })
  } else if (type === 'edit' && row) {
    Object.assign(standardForm.value, row)
    Object.assign(standardForm.value, {
      ...row,
      // ç¡®ä¿ inspectType å’Œ state è½¬æ¢ä¸ºå­—符串,以匹配 el-select çš„ value ç±»åž‹
      inspectType: row.inspectType !== null && row.inspectType !== undefined ? String(row.inspectType) : '',
      state: row.state !== null && row.state !== undefined ? String(row.state) : '0',
      // ç¡®ä¿ processId è½¬æ¢ä¸ºå­—符串或数字(根据实际需要)
      processId: row.processId !== null && row.processId !== undefined ? row.processId : ''
    })
  } else if (type === 'copy' && row) {
    const { id, ...rest } = row
    Object.assign(standardForm.value, {
      ...rest,
      id: undefined,
      standardNo: '',
      state: '0'
      state: '0',
      // ç¡®ä¿ inspectType è½¬æ¢ä¸ºå­—符串
      inspectType: rest.inspectType !== null && rest.inspectType !== undefined ? String(rest.inspectType) : ''
    })
  }
  standardDialogVisible.value = true
@@ -673,14 +695,41 @@
.metric-maintenance {
  display: flex;
  gap: 16px;
  min-width: 0; /* å…è®¸ flex å­å…ƒç´ æ”¶ç¼© */
}
.left-panel,
.right-panel {
  flex: 1;
  min-width: 0; /* å…è®¸ flex å­å…ƒç´ æ”¶ç¼© */
  background: #ffffff;
  padding: 16px;
  box-sizing: border-box;
  overflow: hidden; /* é˜²æ­¢å†…容溢出 */
}
/* ä½Žåˆ†è¾¨çŽ‡é€‚é… */
@media (max-width: 1400px) {
  .metric-maintenance {
    flex-direction: column;
  }
  .left-panel,
  .right-panel {
    width: 100%;
    min-width: 0;
  }
}
@media (max-width: 768px) {
  .metric-maintenance {
    gap: 12px;
  }
  .left-panel,
  .right-panel {
    padding: 12px;
  }
}
.toolbar {
@@ -688,6 +737,8 @@
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  flex-wrap: wrap;
  gap: 8px;
}
.toolbar-left {
@@ -699,6 +750,9 @@
.toolbar-right {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.search-label {
@@ -758,4 +812,23 @@
:deep(.center-table .el-table__body-wrapper td .cell) {
  text-align: center !important;
}
/* PIMTable è¡¨å¤´å±…中 */
:deep(.lims-table .pim-table-header-cell) {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
:deep(.lims-table .pim-table-header-title) {
  text-align: center;
  width: 100%;
}
:deep(.lims-table .pim-table-header-extra) {
  width: 100%;
  margin-top: 4px;
}
</style>
src/views/qualityManagement/processInspection/components/formDia.vue
@@ -9,8 +9,21 @@
      <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="process">
              <el-input v-model="form.process" placeholder="请输入" clearable/>
            <el-form-item label="工序:" prop="processId">
              <el-select
                v-model="form.processId"
                placeholder="请选择工序"
                clearable
                @change="handleProcessChange"
                style="width: 100%"
              >
                <el-option
                  v-for="item in processOptions"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -32,6 +45,24 @@
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="model">
              <el-input v-model="form.model" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="指标选择:" prop="testStandardId">
              <el-select
                v-model="form.testStandardId"
                placeholder="请选择指标"
                clearable
                @change="handleTestStandardChange"
                style="width: 100%"
              >
                <el-option
                  v-for="item in testStandardOptions"
                  :key="item.id"
                  :label="item.standardName || item.standardNo"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -108,11 +139,12 @@
</template>
<script setup>
import {ref} from "vue";
import {ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import {getOptions} from "@/api/procurementManagement/procurementLedger.js";
import {productTreeList} from "@/api/basicData/product.js";
import {productProcessListPage} from "@/api/basicData/productProcess.js";
import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js";
import {qualityInspectDetailByProductId} from "@/api/qualityManagement/metricMaintenance.js";
import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js";
import {userListNoPage} from "@/api/system/user.js";
import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
const { proxy } = getCurrentInstance()
@@ -123,11 +155,12 @@
const data = reactive({
  form: {
    checkTime: "",
    process: "",
    processId: "",
    checkName: "",
    productName: "",
    productId: "",
    model: "",
    testStandardId: "",
    unit: "",
    quantity: "",
    checkCompany: "",
@@ -135,10 +168,11 @@
  },
  rules: {
    checkTime: [{ required: true, message: "请输入", trigger: "blur" },],
    process: [{ required: true, message: "请输入", trigger: "blur" }],
    processId: [{ required: true, message: "请选择工序", trigger: "change" }],
    checkName: [{ required: false, message: "请输入", trigger: "blur" }],
    productId: [{ required: true, message: "请输入", trigger: "blur" }],
    model: [{ required: false, message: "请输入", trigger: "blur" }],
    testStandardId: [{required: true, message: "请选择指标", trigger: "change"}],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
@@ -176,6 +210,24 @@
const tableData = ref([]);
const tableLoading = ref(false);
const currentProductId = ref(0);
const processOptions = ref([]); // å·¥åºä¸‹æ‹‰æ¡†æ•°æ®
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
// èŽ·å–å·¥åºåˆ—è¡¨
const getProcessList = async () => {
    try {
        const res = await productProcessListPage({ current: 1, size: 1000 })
        if (res?.code === 200) {
            const records = res?.data?.records || []
            processOptions.value = records.map(item => ({
                label: item.processName || item.name || item.label,
                value: item.id || item.processId || item.value
            }))
        }
    } catch (error) {
        console.error('获取工序列表失败:', error)
    }
}
// æ‰“开弹框
const openDialog = async (type, row) => {
@@ -187,11 +239,64 @@
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    form.value = {}
    testStandardOptions.value = [];
    tableData.value = [];
    getProductOptions();
    // å…ˆåŠ è½½å·¥åºåˆ—è¡¨
    await getProcessList();
    if (operationType.value === 'edit') {
        form.value = {...row}
        // å…ˆä¿å­˜ testStandardId,避免被清空
        const savedTestStandardId = row.testStandardId;
        // å…ˆè®¾ç½®è¡¨å•数据,但暂时清空 testStandardId,等选项加载完成后再设置
        form.value = {...row, testStandardId: ''}
        // å…¼å®¹æ—§æ•°æ®ï¼šå¦‚æžœ row ä¸­æœ‰ process å­—段,转换为 processId
        if (row.process && !row.processId) {
            // å°è¯•从 processOptions ä¸­æŸ¥æ‰¾åŒ¹é…çš„工序
            const processOption = processOptions.value.find(p => p.label === row.process);
            if (processOption) {
                form.value.processId = processOption.value;
            }
        }
        currentProductId.value = row.productId || 0
        getQualityInspectParamList(row.id)
        // ç¼–辑模式下,先加载指标选项,然后加载参数列表
        if (currentProductId.value) {
            // å…ˆåŠ è½½æŒ‡æ ‡é€‰é¡¹
            let params = {
                productId: currentProductId.value,
                inspectType: 1,
                process: form.value.processId ? processOptions.value.find(p => p.value === form.value.processId)?.label : ''
            }
            qualityInspectDetailByProductId(params).then(res => {
                testStandardOptions.value = res.data || [];
                // ä½¿ç”¨ nextTick å’Œ setTimeout ç¡®ä¿é€‰é¡¹å·²ç»æ¸²æŸ“到 DOM
                nextTick(() => {
                    setTimeout(() => {
                        // å¦‚果编辑数据中有 testStandardId,则设置并加载对应的参数
                        if (savedTestStandardId) {
                            // ç¡®ä¿ç±»åž‹åŒ¹é…ï¼ˆitem.id å¯èƒ½æ˜¯æ•°å­—或字符串)
                            const matchedOption = testStandardOptions.value.find(item =>
                                item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
                            );
                            if (matchedOption) {
                                // ç¡®ä¿ä½¿ç”¨åŒ¹é…é¡¹çš„ id(保持类型一致)
                                form.value.testStandardId = matchedOption.id;
                                handleTestStandardChange(matchedOption.id);
                            } else {
                                // å¦‚果找不到匹配项,尝试直接使用原值
                                console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value);
                                form.value.testStandardId = savedTestStandardId;
                                handleTestStandardChange(savedTestStandardId);
                            }
                        } else {
                            // å¦åˆ™ä½¿ç”¨æ—§çš„逻辑
                            getQualityInspectParamList(row.id);
                        }
                    }, 100);
                });
            });
        } else {
            getQualityInspectParamList(row.id);
        }
    }
}
const getProductOptions = () => {
@@ -202,7 +307,7 @@
const getModels = (value) => {
    currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
    if (currentProductId) {
    if (currentProductId.value) {
        getList();
    }
};
@@ -234,17 +339,31 @@
    return newItem;
  });
}
// å·¥åºå˜åŒ–处理
const handleProcessChange = () => {
    // å·¥åºå˜åŒ–时,如果已选择产品,重新加载指标列表
    if (currentProductId.value) {
        getList();
    }
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.inspectType = 1
            // å°† processId è½¬æ¢ä¸º process åç§°ï¼ˆå¦‚果后端需要 process å­—段)
            const processName = form.value.processId ? processOptions.value.find(p => p.value === form.value.processId)?.label : '';
            if (operationType.value === "add") {
                tableData.value.forEach((item) => {
                    delete item.id
                })
            }
            const data = {...form.value, qualityInspectParams: tableData.value}
            const data = {
                ...form.value,
                process: processName, // ä¿ç•™ process å­—段以兼容后端
                qualityInspectParams: tableData.value
            }
      if (operationType.value === "add") {
        qualityInspectAdd(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
@@ -260,8 +379,42 @@
  })
}
const getList = () => {
    qualityInspectDetailByProductId(currentProductId.value).then(res => {
        tableData.value = res.data;
    if (!currentProductId.value) {
        testStandardOptions.value = [];
        tableData.value = [];
        return;
    }
    // èŽ·å–å·¥åºåç§°
    const processName = form.value.processId ? processOptions.value.find(p => p.value === form.value.processId)?.label : '';
    let params = {
        productId: currentProductId.value,
        inspectType: 1,
        process: processName
    }
    qualityInspectDetailByProductId(params).then(res => {
        // ä¿å­˜ä¸‹æ‹‰æ¡†é€‰é¡¹æ•°æ®
        testStandardOptions.value = res.data || [];
        // æ¸…空表格数据,等待用户选择指标
        tableData.value = [];
        // æ¸…空指标选择
        form.value.testStandardId = '';
    })
}
// æŒ‡æ ‡é€‰æ‹©å˜åŒ–处理
const handleTestStandardChange = (testStandardId) => {
    if (!testStandardId) {
        tableData.value = [];
        return;
    }
    tableLoading.value = true;
    getQualityTestStandardParamByTestStandardId(testStandardId).then(res => {
        tableData.value = res.data || [];
    }).catch(error => {
        console.error('获取标准参数失败:', error);
        tableData.value = [];
    }).finally(() => {
        tableLoading.value = false;
    })
}
const getQualityInspectParamList = (id) => {
@@ -272,6 +425,9 @@
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  tableData.value = [];
  testStandardOptions.value = [];
  form.value.testStandardId = '';
  dialogFormVisible.value = false;
  emit('close')
};
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -45,6 +45,24 @@
              <el-input v-model="form.model" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="指标选择:" prop="testStandardId">
              <el-select
                v-model="form.testStandardId"
                placeholder="请选择指标"
                clearable
                @change="handleTestStandardChange"
                style="width: 100%"
              >
                <el-option
                  v-for="item in testStandardOptions"
                  :key="item.id"
                  :label="item.standardName || item.standardNo"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
@@ -121,13 +139,13 @@
</template>
<script setup>
import {ref} from "vue";
import {ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import {getOptions} from "@/api/procurementManagement/procurementLedger.js";
import {productTreeList} from "@/api/basicData/product.js";
import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js";
import {ElMessageBox} from "element-plus";
import {qualityInspectParamDel, qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
import {qualityInspectDetailByProductId} from "@/api/qualityManagement/metricMaintenance.js";
import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js";
const {proxy} = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -142,6 +160,7 @@
    productName: "",
    productId: "",
    model: "",
    testStandardId: "",
    unit: "",
    quantity: "",
    checkCompany: "",
@@ -153,6 +172,7 @@
    checkName: [{required: false, message: "请输入", trigger: "blur"}],
    productId: [{required: true, message: "请输入", trigger: "blur"}],
    model: [{required: false, message: "请输入", trigger: "blur"}],
    testStandardId: [{required: true, message: "请选择指标", trigger: "change"}],
    unit: [{required: false, message: "请输入", trigger: "blur"}],
    quantity: [{required: true, message: "请输入", trigger: "blur"}],
    checkCompany: [{required: false, message: "请输入", trigger: "blur"}],
@@ -190,6 +210,7 @@
const supplierList = ref([]);
const productOptions = ref([]);
const currentProductId = ref(0);
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
// æ‰“开弹框
const openDialog = (type, row) => {
@@ -199,11 +220,52 @@
    supplierList.value = res.data;
  });
    form.value = {}
  testStandardOptions.value = [];
  tableData.value = [];
  getProductOptions();
  if (operationType.value === 'edit') {
    // å…ˆä¿å­˜ testStandardId,避免被清空
    const savedTestStandardId = row.testStandardId;
    form.value = {...row}
    currentProductId.value = row.productId || 0
    getQualityInspectParamList(row.id)
    // ç¼–辑模式下,先加载指标选项,然后加载参数列表
    if (currentProductId.value) {
      // å…ˆåŠ è½½æŒ‡æ ‡é€‰é¡¹
      let params = {
        productId: currentProductId.value,
        inspectType: 0
      }
      qualityInspectDetailByProductId(params).then(res => {
        testStandardOptions.value = res.data || [];
        // ä½¿ç”¨ nextTick å’Œ setTimeout ç¡®ä¿é€‰é¡¹å·²ç»æ¸²æŸ“到 DOM
        nextTick(() => {
          setTimeout(() => {
            // å¦‚果编辑数据中有 testStandardId,则设置并加载对应的参数
            if (savedTestStandardId) {
              // ç¡®ä¿ç±»åž‹åŒ¹é…ï¼ˆitem.id å¯èƒ½æ˜¯æ•°å­—或字符串)
              const matchedOption = testStandardOptions.value.find(item =>
                item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
              );
              if (matchedOption) {
                // ç¡®ä¿ä½¿ç”¨åŒ¹é…é¡¹çš„ id(保持类型一致)
                form.value.testStandardId = matchedOption.id;
                handleTestStandardChange(matchedOption.id);
              } else {
                // å¦‚果找不到匹配项,尝试直接使用原值
                console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value);
                form.value.testStandardId = savedTestStandardId;
                handleTestStandardChange(savedTestStandardId);
              }
            } else {
              // å¦åˆ™ä½¿ç”¨æ—§çš„逻辑
              getQualityInspectParamList(row.id);
            }
          }, 100);
        });
      });
    } else {
      getQualityInspectParamList(row.id);
    }
  }
}
const getProductOptions = () => {
@@ -214,7 +276,7 @@
const getModels = (value) => {
  currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
  if (currentProductId) {
  if (currentProductId.value) {
    getList();
  }
};
@@ -275,8 +337,39 @@
}
const getList = () => {
  qualityInspectDetailByProductId(currentProductId.value).then(res => {
    tableData.value = res.data;
  if (!currentProductId.value) {
    testStandardOptions.value = [];
    tableData.value = [];
    return;
  }
  let params = {
    productId: currentProductId.value,
    inspectType: 0
  }
  qualityInspectDetailByProductId(params).then(res => {
    // ä¿å­˜ä¸‹æ‹‰æ¡†é€‰é¡¹æ•°æ®
    testStandardOptions.value = res.data || [];
    // æ¸…空表格数据,等待用户选择指标
    tableData.value = [];
    // æ¸…空指标选择
    form.value.testStandardId = '';
  })
}
// æŒ‡æ ‡é€‰æ‹©å˜åŒ–处理
const handleTestStandardChange = (testStandardId) => {
  if (!testStandardId) {
    tableData.value = [];
    return;
  }
  tableLoading.value = true;
  getQualityTestStandardParamByTestStandardId(testStandardId).then(res => {
    tableData.value = res.data || [];
  }).catch(error => {
    console.error('获取标准参数失败:', error);
    tableData.value = [];
  }).finally(() => {
    tableLoading.value = false;
  })
}
@@ -288,7 +381,9 @@
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  tableData.value = []
  tableData.value = [];
  testStandardOptions.value = [];
  form.value.testStandardId = '';
  dialogFormVisible.value = false;
  emit('close')
};
src/views/reportAnalysis/dataDashboard/index.vue
@@ -22,34 +22,44 @@
      <div class="left-panel">
        <!-- å®¢æˆ·ä¿¡æ¯ç»Ÿè®¡åˆ†æž -->
                <div class="panel-header">
                    <span class="panel-title">客户信息统计分析</span>
                    <span class="panel-title">在制品统计分析</span>
                </div>
        <div class="panel-item-customers">
                    <div class="panel-title-second">
                        <div class="panel-title-icon"></div>
                        <div class="total-customers">
                            <span class="label">总合同金额(元)</span>
                            <span class="value">{{sum}}</span>
                    <div class="quality-cards">
                        <div class="quality-cardSec">
                            <div class="quality-card one"></div>
                            <div class="quality-cardTitle">
                                <div>总在制数量</div>
                                <div>{{workInProcessStatistics.totalQuantity}}ä»¶</div>
                            </div>
                        </div>
<!--                        <div class="jiantou"></div>-->
                        <div class="quality-cardSec">
                            <div class="quality-card two"></div>
                            <div class="quality-cardTitle">
                                <div>平均周转天数</div>
                                <div>{{workInProcessStatistics.avgTurnoverDays}}天</div>
                            </div>
                        </div>
                        <div class="quality-cardSec">
                            <div class="quality-card three"></div>
                            <div class="quality-cardTitle">
                                <div>周转效率</div>
                                <div>{{workInProcessStatistics.turnoverEfficiency}}%</div>
                            </div>
                        </div>
                    </div>
                    <!-- é¥¼å›¾åŒºåŸŸ -->
                    <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 82%;margin-top: 20px">
                        <div style="width: 240px; height: 240px; background-image: url('/src/assets/BI/zonghetongbingtubiankuang@2x.png'); background-size: contain; background-position: center; background-repeat: no-repeat; display: flex; align-items: center; justify-content: center;">
                            <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie"
                                             :series="materialPieSeries"
                                             :tooltip="pieTooltip"
                                             :options="{backgroundColor: 'transparent'}"
                                             style="margin-left: 5px;"></Echarts>
                        </div>
                        <ul class="contract-list" style="margin: 0; padding: 0; display: flex; flex-direction: column;justify-content: space-around; height: 100%; overflow-y: auto; scroll-behavior: smooth;" ref="refContractList">
                            <li v-for="item in materialPieSeries[0].data" :key="item.name" style="list-style: none; margin-bottom: 12px;">
                                <div style="display: flex;align-items: center;justify-content: space-between;width: 100%">
                                    <div class="line" :style="{color: item.itemStyle.color}">■ {{item.name}}</div>
                                    <div style="font-weight: 700;font-size: 16px;color: #85B1E4;">ï¿¥{{item.value}}</div>
                                </div>
                            </li>
                        </ul>
                    <!-- å·¥åºåœ¨åˆ¶å“æ•°é‡æŸ±çж图 -->
                    <div style="height: 82%;margin-top: 20px">
                        <Echarts ref="chart"
                                         :chartStyle="chartStyle"
                                         :grid="grid"
                                         :legend="workInProcessBarLegend"
                                         :series="workInProcessBarSeries"
                                         :tooltip="tooltip"
                                         :xAxis="workInProcessXAxis"
                                         :yAxis="workInProcessYAxis"
                                         :options="{backgroundColor: 'transparent', textStyle: {color: '#B8C8E0'}}"
                                         style="height: 100%"></Echarts>
                    </div>
        </div>
@@ -63,21 +73,21 @@
                            <div class="quality-cardSec">
                                <div class="quality-card one"></div>
                                <div class="quality-cardTitle">
                                    <div>原材料已检测数</div>
                                    <div>原材料检数</div>
                                    <div>{{qualityStatisticsObject.supplierNum}}ä»¶</div>
                                </div>
                            </div>
                            <div class="quality-cardSec">
                                <div class="quality-card two"></div>
                                <div class="quality-cardTitle">
                                    <div>过程检验数量</div>
                                    <div>过程检数</div>
                                    <div>{{qualityStatisticsObject.processNum}}ä»¶</div>
                                </div>
                            </div>
                            <div class="quality-cardSec">
                                <div class="quality-card three"></div>
                                <div class="quality-cardTitle">
                                    <div>出厂已检数量</div>
                                    <div>出厂检数</div>
                                    <div>{{qualityStatisticsObject.factoryNum}}ä»¶</div>
                                </div>
                            </div>
@@ -179,20 +189,72 @@
                </div>
                <div class="main-panel">
                    <div class="panel-item-customers">
                        <div class="event-header">
                            <img src="@/assets/BI/shijianmingxiicon@2x.png" alt="图标" class="event-icon" />
                            <span class="event-title">经营分析</span>
                        <div class="order-statistics-cards" style="margin-bottom: 0px;">
                            <div class="quality-cardSec">
                                <div class="quality-card four"></div>
                                <div class="quality-cardTitle">
                                    <div>总订单数</div>
                                    <div>{{orderStatisticsObject.totalOrderCount}}ä»¶</div>
                                </div>
                            </div>
                            <div class="quality-cardSec">
                                <div class="quality-card five"></div>
                                <div class="quality-cardTitle">
                                    <div>未完成订单数</div>
                                    <div>{{orderStatisticsObject.uncompletedOrderCount}}ä»¶</div>
                                </div>
                            </div>
                            <div class="quality-cardSec">
                                <div class="quality-card six"></div>
                                <div class="quality-cardTitle">
                                    <div>部分完成订单数</div>
                                    <div>{{orderStatisticsObject.partialCompletedOrderCount}}ä»¶</div>
                                </div>
                            </div>
                            <div class="quality-cardSec">
                                <div class="quality-card seven"></div>
                                <div class="quality-cardTitle">
                                    <div>已完成订单数</div>
                                    <div>{{orderStatisticsObject.completedOrderCount}}ä»¶</div>
                                </div>
                            </div>
                        </div>
                        <Echarts ref="chart"
                                         :chartStyle="chartStyle"
                                         :grid="grid"
                                         :legend="barLegend1"
                                         :series="barSeries11"
                                         :tooltip="tooltip"
                                         :xAxis="xAxis3"
                                         :yAxis="yAxis3"
                                         :options="{backgroundColor: 'transparent', textStyle: {color: '#B8C8E0'}}"
                                         style="height: 170px"></Echarts>
                        <div class="progress-table-container" ref="progressTableRef" style="margin-top: 0px;" @scroll="handleTableScroll">
                            <table class="progress-table">
                                <thead>
                                    <tr>
                                        <th>生产订单号</th>
                                        <th>产品名称</th>
                                        <th>规格</th>
                                        <th>需求数量</th>
                                        <th>完成数量</th>
                                        <th>完成进度</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr
                                        v-for="(item, index) in progressTableData"
                                        :key="index"
                                        :ref="el => setRowRef(el, index)"
                                        :class="{ 'row-under-header': isRowUnderHeader(index) }"
                                    >
                                        <td>{{ item.npsNo || '-' }}</td>
                                        <td>{{ item.productCategory || '-' }}</td>
                                        <td>{{ item.specificationModel || '-' }}</td>
                                        <td>{{ item.quantity || 0 }}</td>
                                        <td>{{ item.completeQuantity || 0 }}</td>
                                        <td>
                                            <el-progress
                                                :percentage="calculateProgress(item)"
                                                :color="progressColor(calculateProgress(item))"
                                                :status="calculateProgress(item) >= 100 ? 'success' : ''"
                                                :stroke-width="8"
                                            />
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
      </div>
@@ -253,7 +315,7 @@
    getProgressStatistics,
      getWorkInProcessTurnover
} from "@/api/viewIndex.js";
import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js";
import {staffOnJobListPage} from "@/api/personnelManagement/employeeRecord.js";
import {listCustomer} from "@/api/basicData/customerFile.js";
import {listSupplier} from "@/api/basicData/supplierManageFile.js";
import {getLedgerPage} from "@/api/equipmentManagement/ledger.js";
@@ -261,6 +323,7 @@
import {getUpkeepPage} from "@/api/equipmentManagement/upkeep.js";
import {measuringInstrumentListPage} from "@/api/equipmentManagement/measurementEquipment.js";
import {listPageAnalysis} from "@/api/financialManagement/expenseManagement.js";
import {productOrderListPage} from "@/api/productionManagement/productionOrder.js";
// å…¨å±ç›¸å…³çŠ¶æ€
const isFullscreen = ref(false);
@@ -288,11 +351,17 @@
const realtimeLineChartRef = ref(null)
const refContractList = ref(null)
const refTodoList = ref(null)
const progressTableRef = ref(null)
const timerScroll = ref(null)
const progressTableScrollTimer = ref(null)
const isTableScrolling = ref(false)
const tableScrollTimeout = ref(null)
const tableRowRefs = ref([])
const rowsUnderHeader = ref(new Set())
const chartStylePie = {
    width: '140%',
    height: '140%' // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
    width: '100%',
    height: '100%' // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
}
const materialPieSeries = ref([
    {
@@ -336,6 +405,21 @@
    supplierNum: 0,
    processNum: 0,
    factoryNum: 0,
})
// è®¢å•统计对象
const orderStatisticsObject = ref({
    totalOrderCount: 0,
    uncompletedOrderCount: 0,
    partialCompletedOrderCount: 0,
    completedOrderCount: 0,
})
// åœ¨åˆ¶å“å‘¨è½¬ç»Ÿè®¡å¯¹è±¡
const workInProcessStatistics = ref({
    totalQuantity: 0,
    avgTurnoverDays: 0,
    turnoverEfficiency: 0,
})
const chartStyle = {
    width: '100%',
@@ -579,8 +663,83 @@
    axisLabel: { color: '#B8C8E0' }
}]
// åœ¨åˆ¶å“å·¥åºæŸ±çŠ¶å›¾é…ç½®
const workInProcessXAxis = ref([{
    type: 'category',
    axisTick: { show: false },
    axisLabel: { color: '#B8C8E0' },
    data: []
}])
const workInProcessYAxis = [{
    type: 'value',
    axisLabel: { color: '#B8C8E0' },
    name: ''
}]
const workInProcessBarLegend = {
    show: false,
    textStyle: { color: '#B8C8E0' },
    data: []
}
const workInProcessBarSeries = ref([
    {
        name: '在制品数量',
        type: 'bar',
        barWidth: 25, // å›ºå®šæŸ±çŠ¶å›¾å®½åº¦ä¸º40px
        barGap: 0,
        emphasis: {
            focus: 'series'
        },
        itemStyle: {
            color: {
                type: 'linear',
                x: 0,
                y: 0,
                x2: 0,
                y2: 1,
                colorStops: [
                    { offset: 0, color: '#4EE4FF' },
                    { offset: 1, color: '#00A4ED' }
                ]
            }
        },
        label: {
            show: true,
            position: 'top',
            color: '#B8C8E0'
        },
        data: []
    }
])
// å¾…办事项
const todoList = ref([])
// ç”Ÿäº§è®¢å•完成进度表格数据
const progressTableData = ref([])
// è®¡ç®—完成进度百分比
const calculateProgress = (item) => {
    if (!item) return 0
    // ä¼˜å…ˆä½¿ç”¨completionStatus字段
    if (item.completionStatus !== undefined && item.completionStatus !== null) {
        const percentage = Number(item.completionStatus)
        if (isNaN(percentage)) return 0
        return Math.min(Math.max(Math.round(percentage), 0), 100)
    }
    // å¦‚果没有completionStatus,则根据完成数量和需求数量计算
    if (!item.quantity || item.quantity === 0) return 0
    const percentage = (item.completeQuantity || 0) / item.quantity * 100
    return Math.min(Math.max(Math.round(percentage), 0), 100)
}
// æ ¹æ®è¿›åº¦ç™¾åˆ†æ¯”返回颜色
const progressColor = (percentage) => {
    const p = percentage || 0
    if (p < 30) return "#f56c6c"
    if (p < 50) return "#e6a23c"
    if (p < 80) return "#409eff"
    return "#67c23a"
}
// è®¡ç®—缩放比例
const calculateScale = () => {
@@ -635,6 +794,43 @@
        }))
    })
}
// åœ¨åˆ¶å“å‘¨è½¬ç»Ÿè®¡
const workInProcessTurnoverInfo = () => {
    getWorkInProcessTurnover().then((res) => {
        console.log("在制品周转统计数据:", res)
        if (!res || !res.data) {
            console.warn('在制品周转统计数据为空')
            return
        }
        // ä»ŽæŽ¥å£èŽ·å–ç»Ÿè®¡æ•°æ®
        workInProcessStatistics.value = {
            totalQuantity: res.data.totalOrderCount || 0,
            avgTurnoverDays: res.data.averageTurnoverDays || 0,
            turnoverEfficiency: res.data.turnoverEfficiency || 0,
        }
        // è®¾ç½®å·¥åºæŸ±çŠ¶å›¾æ•°æ®
        // X轴:processDetails (工序详情数组)
        // Y轴:processQuantityDetails (工序数量详情数组)
        if (res.data.processDetails && Array.isArray(res.data.processDetails)) {
            // è®¾ç½®X轴数据(工序名称)
            workInProcessXAxis.value[0].data = res.data.processDetails
        } else {
            workInProcessXAxis.value[0].data = []
        }
        if (res.data.processQuantityDetails && Array.isArray(res.data.processQuantityDetails)) {
            // è®¾ç½®Y轴数据(在制品数量)
            workInProcessBarSeries.value[0].data = res.data.processQuantityDetails
        } else {
            workInProcessBarSeries.value[0].data = []
        }
    }).catch((error) => {
        console.error('获取在制品周转统计失败:', error)
    })
}
// è´¨æ£€ç»Ÿè®¡
const qualityStatisticsInfo = () => {
    qualityStatistics().then((res) => {
@@ -651,6 +847,7 @@
}
// å„生产订单的完成进度统计
const progressStatisticsInfo = () => {
    // ä»Žç»Ÿè®¡æŽ¥å£èŽ·å–ç»Ÿè®¡æ•°æ®
    getProgressStatistics().then((res) => {
        console.log("生产订单完成进度统计数据:", res)
        
@@ -659,24 +856,22 @@
            return
        }
        
        // è®¾ç½®X轴数据 - ä½¿ç”¨åˆ†ç±»åç§°
        xAxis3.value[0].data = ['已完成进度数', '总订单数', '未完成订单数', '已完成订单数']
        // è®¾ç½®å•个系列的数据 - æ¯ä¸ªX轴分类对应一个值
        if (barSeries11.value && barSeries11.value.length > 0) {
            barSeries11.value[0].data = [
                res.data.completedProgressCount || 0,
                res.data.totalOrderCount || 0,
                res.data.uncompletedOrderCount || 0,
                res.data.completedOrderCount || 0
            ]
        // ä»ŽæŽ¥å£èŽ·å–ç»Ÿè®¡æ•°æ®
        orderStatisticsObject.value = {
            totalOrderCount: res.data.totalOrderCount || 0,
            uncompletedOrderCount: res.data.uncompletedOrderCount || 0,
            partialCompletedOrderCount: res.data.partialCompletedOrderCount || 0,
            completedOrderCount: res.data.completedOrderCount || 0
        }
        progressTableData.value = res.data.completedOrderDetails || []
        // é‡ç½®è¡Œå¼•用
        tableRowRefs.value = []
        rowsUnderHeader.value.clear()
        
        console.log('图表数据设置完成:', {
            xAxis: xAxis3.value[0].data,
            series: barSeries11.value[0]?.data
        // åœ¨èŽ·å–åˆ°æ•°æ®åŽï¼Œåˆå§‹åŒ–æ»šåŠ¨åŠŸèƒ½
        nextTick(() => {
            initProgressTableScroll()
        })
    }).catch((error) => {
        console.error('获取生产订单完成进度统计失败:', error)
    })
@@ -822,6 +1017,163 @@
// è‡ªåŠ¨è½®æ¢å‘¨ã€æœˆã€å­£åº¦çš„å®šæ—¶å™¨
const autoSwitchTimer = ref(null)
// è®¾ç½®è¡Œå¼•用
const setRowRef = (el, index) => {
    if (el) {
        tableRowRefs.value[index] = el
    }
}
// åˆ¤æ–­è¡Œæ˜¯å¦åœ¨è¡¨å¤´ä¸‹æ–¹
const isRowUnderHeader = (index) => {
    return rowsUnderHeader.value.has(index)
}
// å¤„理表格滚动事件
const handleTableScroll = () => {
    const tableContainer = progressTableRef.value
    if (!tableContainer) return
    const thead = tableContainer.querySelector('thead')
    if (!thead) return
    const theadHeight = thead.offsetHeight
    const containerRect = tableContainer.getBoundingClientRect()
    const containerTop = containerRect.top
    const theadBottom = containerTop + theadHeight
    // æ¸…空之前的记录
    rowsUnderHeader.value.clear()
    // æ£€æŸ¥æ¯ä¸€è¡Œæ˜¯å¦åœ¨è¡¨å¤´ä¸‹æ–¹ï¼ˆè¢«è¡¨å¤´é®æŒ¡ï¼‰
    tableRowRefs.value.forEach((row, index) => {
        if (row) {
            const rowRect = row.getBoundingClientRect()
            const rowTop = rowRect.top
            const rowBottom = rowRect.bottom
            // å¦‚果行与表头有重叠(行在表头下方被遮挡)
            // è¡Œçš„顶部在表头底部下方,但行的底部在表头底部上方,说明被遮挡
            if (rowTop < theadBottom && rowBottom > containerTop) {
                rowsUnderHeader.value.add(index)
            }
        }
    })
    // æ¸…除之前的定时器
    if (tableScrollTimeout.value) {
        clearTimeout(tableScrollTimeout.value)
    }
    // æ»šåŠ¨åœæ­¢åŽæ¸…ç©ºæ·¡åŒ–æ ‡è®°
    tableScrollTimeout.value = setTimeout(() => {
        rowsUnderHeader.value.clear()
    }, 150)
}
// åˆå§‹åŒ–生产订单进度表格滚动功能
const initProgressTableScroll = () => {
    const tableContainer = progressTableRef.value
    if (!tableContainer) return
    // æ¸…理之前的滚动动画和定时器
    if (progressTableScrollTimer.value) {
        cancelAnimationFrame(progressTableScrollTimer.value)
        progressTableScrollTimer.value = null
    }
    if (tableContainer._pauseTimer) {
        clearInterval(tableContainer._pauseTimer)
        tableContainer._pauseTimer = null
    }
    const tbody = tableContainer.querySelector('tbody')
    if (!tbody) return
    // æ¸…理之前可能存在的克隆行(保留原始数据行)
    // åŽŸå§‹æ•°æ®è¡Œçš„æ•°é‡åº”è¯¥ç­‰äºŽ progressTableData.value.length
    const originalCount = progressTableData.value.length
    const allRows = Array.from(tbody.querySelectorAll('tr'))
    if (allRows.length > originalCount) {
        // ç§»é™¤æ‰€æœ‰è¶…过原始数量的行(这些是克隆的行)
        for (let i = originalCount; i < allRows.length; i++) {
            allRows[i].remove()
        }
    }
    const scrollItems = Array.from(tbody.querySelectorAll('tr'))
    if (scrollItems.length === 0) return
    // èŽ·å–åŽŸå§‹æ•°æ®é¡¹æ•°é‡
    const originalItemCount = scrollItems.length
    // è®¡ç®—容器高度和表头高度
    const thead = tableContainer.querySelector('thead')
    const theadHeight = thead ? thead.offsetHeight : 40
    const containerHeight = tableContainer.clientHeight
    const visibleHeight = containerHeight - theadHeight
    // è®¡ç®—原始数据的总高度
    const itemHeight = scrollItems[0]?.offsetHeight || 40
    const totalContentHeight = itemHeight * originalItemCount
    // å¦‚果数据量不够,容器可以完全显示所有数据,就不需要滚动和克隆
    if (totalContentHeight <= visibleHeight) {
        // æ•°æ®é‡å°‘,不需要滚动,直接返回
        return
    }
    // æ•°æ®é‡è¶³å¤Ÿï¼Œéœ€è¦æ»šåŠ¨ï¼Œè¿›è¡Œå…‹éš†ä»¥å®žçŽ°æ— ç¼æ»šåŠ¨
    const cloneCount = Math.ceil(visibleHeight / itemHeight) + 2
    // å…‹éš†å‰å‡ ä¸ªé¡¹ç›®å¹¶æ·»åŠ åˆ°åˆ—è¡¨æœ«å°¾ï¼Œå®žçŽ°æ— ç¼æ»šåŠ¨
    for (let i = 0; i < cloneCount; i++) {
        const clone = scrollItems[i % originalItemCount].cloneNode(true)
        tbody.appendChild(clone)
    }
    let scrollPosition = 0
    const scrollSpeed = 1.5
    const pauseTime = 3000
    let isPaused = false
    let lastTimestamp = 0
    // è¿žç»­æ»šåŠ¨åŠ¨ç”»å‡½æ•°
    function scrollAnimation(timestamp) {
        if (!lastTimestamp) lastTimestamp = timestamp
        const deltaTime = timestamp - lastTimestamp
        lastTimestamp = timestamp
        if (!isPaused) {
            scrollPosition += scrollSpeed * (deltaTime / 16)
            // è®¡ç®—最大滚动位置(原始内容的高度)
            const maxScroll = itemHeight * originalItemCount
            // å½“滚动超过原始内容长度时,重置位置实现无缝滚动
            if (scrollPosition >= maxScroll) {
                scrollPosition = 0
                tableContainer.scrollTop = 0
            } else {
                tableContainer.scrollTop = scrollPosition
            }
        }
        progressTableScrollTimer.value = requestAnimationFrame(scrollAnimation)
    }
    // å¯åŠ¨æ»šåŠ¨åŠ¨ç”»
    progressTableScrollTimer.value = requestAnimationFrame(scrollAnimation)
    // è®¾ç½®æ»šåЍ-暂停-滚动的循环效果
    const pauseTimer = setInterval(() => {
        isPaused = !isPaused
    }, pauseTime)
    // æ¸…理定时器
    tableContainer._pauseTimer = pauseTimer
}
// åˆå§‹åŒ–待办事项列表滚动功能
const initTodoListScroll = () => {
    const todoList = refTodoList.value
@@ -1031,6 +1383,7 @@
  window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
  window.addEventListener('MSFullscreenChange', handleFullscreenChange)
  analysisCustomer()
  workInProcessTurnoverInfo()
  qualityStatisticsInfo()
    // accountStatisticsInfo()
    progressStatisticsInfo()
@@ -1072,6 +1425,25 @@
      clearInterval(todoList._pauseTimer)
      todoList._pauseTimer = null
    }
  }
  // æ¸…理生产订单进度表格的动画和定时器
  const progressTable = progressTableRef.value
  if (progressTable) {
    if (progressTableScrollTimer.value) {
      cancelAnimationFrame(progressTableScrollTimer.value)
      progressTableScrollTimer.value = null
    }
    if (progressTable._pauseTimer) {
      clearInterval(progressTable._pauseTimer)
      progressTable._pauseTimer = null
    }
  }
  // æ¸…理表格滚动定时器
  if (tableScrollTimeout.value) {
    clearTimeout(tableScrollTimeout.value)
    tableScrollTimeout.value = null
  }
  
  // æ¸…理自动轮换周、月、季度的定时器
@@ -1272,7 +1644,33 @@
}
.quality-card.three {
    background-image: url("@/assets/BI/chuchangyijianicon@2x.png");
}
/* è®¢å•统计卡片样式 */
.order-statistics-cards {
    display: flex;
    gap: 12px;
    width: 100%;
    height: 94px;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}
.quality-card.four {
    background-image: url("@/assets/BI/yuancailiaoyijianicon@2x.png");
}
.quality-card.five {
    background-image: url("@/assets/BI/guochengyijianicon@2x.png");
}
.quality-card.six {
    background-image: url("@/assets/BI/chuchangyijianicon@2x.png");
}
.quality-card.seven {
    background-image: url("@/assets/BI/yuancailiaoyijianicon@2x.png");
}
.panel-title-icon {
    width: 60px;
@@ -1555,4 +1953,84 @@
  border-color: rgba(255, 255, 255, 0.5);
  box-shadow: -1px 0 0 0 rgba(255, 255, 255, 0.5);
}
/* ç”Ÿäº§è®¢å•进度表格样式 */
.progress-table-container {
  height: 250px;
  overflow-y: auto;
  overflow-x: hidden;
  margin-top: 10px;
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE和Edge */
}
.progress-table-container::-webkit-scrollbar {
  display: none; /* Chrome、Safari和Opera */
}
.progress-table {
  width: 100%;
  border-collapse: collapse;
  color: #B8C8E0;
  font-size: 12px;
  table-layout: fixed;
}
.progress-table thead {
  position: sticky;
  top: 0;
  background-color: rgba(26, 88, 176, 0.9);
  z-index: 10;
}
.progress-table th {
  padding: 8px 6px;
  text-align: left;
  font-weight: 500;
  border-bottom: 1px solid rgba(184, 200, 224, 0.3);
  color: #B8C8E0;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.progress-table th:nth-child(1) { width: 15%; } /* ç”Ÿäº§è®¢å•号 */
.progress-table th:nth-child(2) { width: 15%; } /* äº§å“åç§° */
.progress-table th:nth-child(3) { width: 15%; } /* è§„æ ¼ */
.progress-table th:nth-child(4) { width: 12%; } /* éœ€æ±‚数量 */
.progress-table th:nth-child(5) { width: 12%; } /* å®Œæˆæ•°é‡ */
.progress-table th:nth-child(6) { width: 31%; } /* å®Œæˆè¿›åº¦ */
.progress-table td {
  padding: 8px 6px;
  border-bottom: 1px solid rgba(184, 200, 224, 0.1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  transition: opacity 0.3s ease;
}
.progress-table tbody tr:hover {
  background-color: rgba(184, 200, 224, 0.1);
}
.progress-table tbody tr.row-under-header {
  opacity: 0.5;
}
/* el-progress ç»„件样式调整 */
.progress-table :deep(.el-progress) {
  width: 100%;
}
.progress-table :deep(.el-progress-bar__outer) {
  background-color: rgba(184, 200, 224, 0.2);
}
.progress-table :deep(.el-progress__text) {
  color: #B8C8E0;
  font-size: 11px;
}
</style>
src/views/reportAnalysis/reportManagement/index.vue
@@ -1,715 +1,1402 @@
<template>
    <div class="report-management">
        <!-- ç­›é€‰æ¡ä»¶ -->
        <el-card class="filter-card" shadow="never">
            <el-form :model="filterForm" inline>
                <el-form-item label="时间范围">
                    <el-date-picker
                        style="width: 300px"
                        v-model="filterForm.dateRange"
                        type="daterange"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        format="YYYY-MM-DD"
                        value-format="YYYY-MM-DD"
                        @change="handleFilterChange"
                    />
                </el-form-item>
                <el-form-item label="报表类型">
                    <el-select v-model="filterForm.reportType" placeholder="请选择报表类型" @change="handleFilterChange" style="width: 300px">
                        <el-option label="样品进度报表" value="sample" />
                        <el-option label="设备使用报表" value="equipment" />
                        <el-option label="检测项目报表" value="inspection" />
                        <el-option label="领用记录报表" value="usage" />
                    </el-select>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="handleFilterChange">查询</el-button>
                    <el-button @click="resetFilter">重置</el-button>
                    <el-button type="success" @click="exportReport">导出报表</el-button>
                </el-form-item>
            </el-form>
        </el-card>
        <!-- ç»Ÿè®¡å¡ç‰‡ -->
        <div class="statistics-cards">
            <el-row :gutter="20">
                <el-col :span="6">
                    <el-card class="stat-card" shadow="hover">
                        <div class="stat-content">
                            <div class="stat-icon">
                                <el-icon><Box /></el-icon>
                            </div>
                            <div class="stat-info">
                                <div class="stat-number">{{ statistics.totalSamples }}</div>
                                <div class="stat-label">总样品数</div>
                            </div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card class="stat-card" shadow="hover">
                        <div class="stat-content">
                            <div class="stat-icon">
                                <el-icon><Tools /></el-icon>
                            </div>
                            <div class="stat-info">
                                <div class="stat-number">{{ statistics.activeEquipment }}</div>
                                <div class="stat-label">在用设备</div>
                            </div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card class="stat-card" shadow="hover">
                        <div class="stat-content">
                            <div class="stat-icon">
                                <el-icon><Document /></el-icon>
                            </div>
                            <div class="stat-info">
                                <div class="stat-number">{{ statistics.completedInspections }}</div>
                                <div class="stat-label">已完成检测</div>
                            </div>
                        </div>
                    </el-card>
                </el-col>
                <el-col :span="6">
                    <el-card class="stat-card" shadow="hover">
                        <div class="stat-content">
                            <div class="stat-icon">
                                <el-icon><ShoppingCart /></el-icon>
                            </div>
                            <div class="stat-info">
                                <div class="stat-number">{{ statistics.totalUsage }}</div>
                                <div class="stat-label">总领用次数</div>
                            </div>
                        </div>
                    </el-card>
                </el-col>
            </el-row>
        </div>
        <!-- å›¾è¡¨åŒºåŸŸ -->
        <div class="charts-container">
            <el-row :gutter="20">
                <!-- æ ·å“è¿›åº¦å›¾è¡¨ -->
                <el-col :span="12">
                    <el-card class="chart-card" shadow="hover">
                        <template #header>
                            <div class="card-header">
                                <span>样品进度统计</span>
                                <el-button link @click="refreshSampleChart">刷新</el-button>
                            </div>
                        </template>
                        <div ref="sampleChartRef" class="chart-container"></div>
                    </el-card>
                </el-col>
                <!-- è®¾å¤‡ä½¿ç”¨å›¾è¡¨ -->
                <el-col :span="12">
                    <el-card class="chart-card" shadow="hover">
                        <template #header>
                            <div class="card-header">
                                <span>设备使用率统计</span>
                                <el-button link @click="refreshEquipmentChart">刷新</el-button>
                            </div>
                        </template>
                        <div ref="equipmentChartRef" class="chart-container"></div>
                    </el-card>
                </el-col>
            </el-row>
            <el-row :gutter="20" style="margin-top: 20px;">
                <!-- æ£€æµ‹é¡¹ç›®ç»Ÿè®¡ -->
                <el-col :span="12">
                    <el-card class="chart-card" shadow="hover">
                        <template #header>
                            <div class="card-header">
                                <span>检测项目分布</span>
                                <el-button link @click="refreshInspectionChart">刷新</el-button>
                            </div>
                        </template>
                        <div ref="inspectionChartRef" class="chart-container"></div>
                    </el-card>
                </el-col>
                <!-- é¢†ç”¨è®°å½•趋势 -->
                <el-col :span="12">
                    <el-card class="chart-card" shadow="hover">
                        <template #header>
                            <div class="card-header">
                                <span>领用记录趋势</span>
                                <el-button link @click="refreshUsageChart">刷新</el-button>
                            </div>
                        </template>
                        <div ref="usageChartRef" class="chart-container"></div>
                    </el-card>
                </el-col>
            </el-row>
        </div>
        <!-- è¯¦ç»†æ•°æ®è¡¨æ ¼ -->
        <el-card class="table-card" shadow="hover">
            <template #header>
                <div class="card-header">
                    <span>详细数据</span>
                    <div>
                        <el-button type="primary" size="small" @click="refreshTable">刷新</el-button>
                        <el-button type="success" size="small" @click="exportTable">导出</el-button>
                    </div>
                </div>
            </template>
            <el-table
                :data="tableData"
                style="width: 100%"
                v-loading="tableLoading"
                stripe
                border
            >
                <el-table-column prop="id" label="编号" width="80" />
                <el-table-column prop="name" label="名称" />
                <el-table-column prop="status" label="状态">
                    <template #default="scope">
                        <el-tag :type="getStatusType(scope.row.status)">
                            {{ scope.row.status }}
                        </el-tag>
                    </template>
                </el-table-column>
                <el-table-column prop="progress" label="进度">
                    <template #default="scope">
                        <el-progress :percentage="scope.row.progress" :status="getProgressStatus(scope.row.progress)" />
                    </template>
                </el-table-column>
                <el-table-column prop="createTime" label="创建时间" width="180" />
                <el-table-column prop="updateTime" label="更新时间" width="180" />
                <el-table-column label="操作" width="150" fixed="right">
                    <template #default="scope">
                        <el-button link size="small" @click="viewDetail(scope.row)">查看</el-button>
                        <el-button link size="small" @click="editItem(scope.row)">编辑</el-button>
                    </template>
                </el-table-column>
            </el-table>
            <div class="pagination-container">
                <el-pagination
                    v-model:current-page="pagination.currentPage"
                    v-model:page-size="pagination.pageSize"
                    :page-sizes="[10, 20, 50, 100]"
                    :total="pagination.total"
                    layout="total, sizes, prev, pager, next, jumper"
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                />
            </div>
        </el-card>
    </div>
  <div class="report-management">
    <!-- å›¾è¡¨åŒºåŸŸ -->
    <div class="charts-container">
      <el-row :gutter="20">
        <!-- å„类型完成数量 -->
        <el-col :span="9">
          <el-card class="chart-card"
                   shadow="hover">
            <template #header>
              <div class="card-header">
                <div class="chart-title-line"></div>
                <span>各类型完成数量</span>
              </div>
            </template>
            <div class="top-container">
              <div class="typeNum">
                <div class="typeNum-left">
                  <img src="~@/assets/images/chartCard.svg"
                       alt="图表"
                       style="width: 40px; height: 40px; object-fit: contain;">
                  <div class="typeNum-left-text">原材料</div>
                </div>
                <div class="typeNum-center">
                  <div class="typeNum-leftLine">-</div>
                  <div class="typeNum-rightLine"></div>
                </div>
                <div class="typeNum-right">
                  <div class="typeNum-right-top">
                    <div class="typeNum-right-top-name">总数量</div>
                    <div class="typeNum-right-top-text">2099 <span class="unit">个</span></div>
                  </div>
                  <div class="typeNum-right-bottom">
                    <div class="typeNum-right-top-name">已完成数</div>
                    <div class="typeNum-right-top-text">2099 <span class="unit">个</span></div>
                  </div>
                </div>
              </div>
              <div class="typeNum">
                <div class="typeNum-left">
                  <img src="~@/assets/images/chartCard2.svg"
                       alt="图表"
                       style="width: 40px; height: 40px; object-fit: contain;">
                  <div class="typeNum-left-text"
                       style="color: #5EB334;">半成品</div>
                </div>
                <div class="typeNum-center">
                  <div class="typeNum-leftLine2">-</div>
                  <div class="typeNum-rightLine2"></div>
                </div>
                <div class="typeNum-right">
                  <div class="typeNum-right-top">
                    <div class="typeNum-right-top-name">总数量</div>
                    <div class="typeNum-right-top-text">2099 <span class="unit">个</span></div>
                  </div>
                  <div class="typeNum-right-bottom">
                    <div class="typeNum-right-top-name">已完成数</div>
                    <div class="typeNum-right-top-text">2099 <span class="unit">个</span></div>
                  </div>
                </div>
              </div>
              <div class="typeNum">
                <div class="typeNum-left">
                  <img src="~@/assets/images/chartCard3.svg"
                       alt="图表"
                       style="width: 40px; height: 40px; object-fit: contain;">
                  <div class="typeNum-left-text"
                       style="color: #8000FF;">成品</div>
                </div>
                <div class="typeNum-center">
                  <div class="typeNum-leftLine3">-</div>
                  <div class="typeNum-rightLine3"></div>
                </div>
                <div class="typeNum-right">
                  <div class="typeNum-right-top">
                    <div class="typeNum-right-top-name">总数量</div>
                    <div class="typeNum-right-top-text">2099 <span class="unit">个</span></div>
                  </div>
                  <div class="typeNum-right-bottom">
                    <div class="typeNum-right-top-name">已完成数</div>
                    <div class="typeNum-right-top-text">2099 <span class="unit">个</span></div>
                  </div>
                </div>
              </div>
            </div>
          </el-card>
        </el-col>
        <!-- è´¨æ£€åˆæ ¼çއ -->
        <el-col :span="15">
          <el-card class="chart-card"
                   shadow="hover">
            <template #header>
              <div class="card-header">
                <div class="chart-title-line"></div>
                <span>质检合格率</span>
              </div>
            </template>
            <div class="top-container flex-center">
              <div class="quality-card blue-card">
                <div class="quality-card-title">
                  <img src="~@/assets/images/chartCard.svg"
                       alt="原材料"
                       style="width: 24px; height: 24px; margin-right: 8px;">
                  åŽŸææ–™åˆæ ¼çŽ‡
                </div>
                <div class="quality-card-content">
                  <div class="quality-item">
                    <div>
                      <div class="quality-item-label blue-label">完成率</div>
                      <div class="quality-item-tip">占比</div>
                      <div class="quality-item-value">80%</div>
                    </div>
                    <div class="quality-item-chart"
                         ref="materialCompletionChart"></div>
                  </div>
                  <div class="quality-item">
                    <div>
                      <div class="quality-item-label green-label">合格率</div>
                      <div class="quality-item-tip">占比</div>
                      <div class="quality-item-value">80%</div>
                    </div>
                    <div class="quality-item-chart"
                         ref="materialQualityChart"></div>
                  </div>
                </div>
              </div>
              <div class="quality-card green-card">
                <div class="quality-card-title">
                  <img src="~@/assets/images/chartCard2.svg"
                       alt="半成品"
                       style="width: 24px; height: 24px; margin-right: 8px;">
                  åŠæˆå“åˆæ ¼çއ
                </div>
                <div class="quality-card-content">
                  <div class="quality-item">
                    <div>
                      <div class="quality-item-label blue-label">完成率</div>
                      <div class="quality-item-tip">占比</div>
                      <div class="quality-item-value">80%</div>
                    </div>
                    <div class="quality-item-chart"
                         ref="semiCompletionChart"></div>
                  </div>
                  <div class="quality-item">
                    <div>
                      <div class="quality-item-label green-label">合格率</div>
                      <div class="quality-item-tip">占比</div>
                      <div class="quality-item-value">80%</div>
                    </div>
                    <div class="quality-item-chart"
                         ref="semiQualityChart"></div>
                  </div>
                </div>
              </div>
              <div class="quality-card purple-card">
                <div class="quality-card-title">
                  <img src="~@/assets/images/chartCard3.svg"
                       alt="成品"
                       style="width: 24px; height: 24px; margin-right: 8px;">
                  æˆå“åˆæ ¼çއ
                </div>
                <div class="quality-card-content">
                  <div class="quality-item">
                    <div>
                      <div class="quality-item-label blue-label">完成率</div>
                      <div class="quality-item-tip">占比</div>
                      <div class="quality-item-value">80%</div>
                    </div>
                    <div class="quality-item-chart"
                         ref="finalCompletionChart"></div>
                  </div>
                  <div class="quality-item">
                    <div>
                      <div class="quality-item-label green-label">合格率</div>
                      <div class="quality-item-tip">占比</div>
                      <div class="quality-item-value">80%</div>
                    </div>
                    <div class="quality-item-chart"
                         ref="finalQualityChart"></div>
                  </div>
                </div>
              </div>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>
    <div class="charts-container">
      <el-row :gutter="20">
        <!-- è´¨æ£€åˆæ ¼çއ -->
        <el-col :span="24">
          <el-card class="chart-card"
                   shadow="hover">
            <template #header>
              <div class="card-header">
                <div class="chart-title-line"></div>
                <span>质检合格率</span>
              </div>
            </template>
            <div class="chart-container-line">
              <div class="container-line-left">
                <div style="height: 100%; width: 100%;"
                     ref="usageChartRef">
                </div>
              </div>
              <div class="container-line-right">
                <div style="height: 80%; width: 100%;"
                     ref="inspectionChartRef">
                </div>
                <div class="container-line-right-bottom">
                  <div class="inspection-chart-box">
                    <div class="chart-box-title">原材料抽检数</div>
                    <div class="chart-box-num">600</div>
                  </div>
                  <div class="inspection-chart-box">
                    <div class="chart-box-title">半成品抽检数</div>
                    <div class="chart-box-num">200</div>
                  </div>
                  <div class="inspection-chart-box">
                    <div class="chart-box-title">成品抽检数</div>
                    <div class="chart-box-num">200</div>
                  </div>
                </div>
              </div>
            </div>
            <!-- </div> -->
            <!-- <div ref="sampleChartRef"
                 class="chart-container"></div> -->
            <div class="yearchange">
              <div style="margin-right: 8px;font-size: 14px;">年份:</div>
              <el-date-picker v-model="value3"
                              size="mini"
                              :clearable="false"
                              style="width: 60px;"
                              type="year"
                              :disabled-date="disabledDate"
                              placeholder="">
              </el-date-picker>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>
    <div class="charts-container">
      <el-row :gutter="20">
        <!-- æ ·å“è¿›åº¦å›¾è¡¨ -->
        <el-col :span="12">
          <el-card class="chart-card"
                   shadow="hover">
            <template #header>
              <div class="card-header">
                <div class="chart-title-line"></div>
                <span>质量完成明细</span>
              </div>
            </template>
            <div ref="equipmentChartRef"
                 class="chart-container"></div>
          </el-card>
        </el-col>
        <!-- è®¾å¤‡ä½¿ç”¨å›¾è¡¨ -->
        <el-col :span="12">
          <el-card class="chart-card"
                   shadow="hover">
            <template #header>
              <div class="card-header">
                <div class="chart-title-line"></div>
                <span>检测项目分类</span>
              </div>
            </template>
            <div class="chart-container-line">
              <div class="container-line2-left">
                <div class="info-box">
                  <div class="info-box-header">项目分布</div>
                  <div class="info-line">
                    <div class="info-icon"
                         style="background-color: #165DFF"></div>
                    <div class="info-line-title">物理性能</div>
                    <div class="info-line-value1">30%</div>
                    <div class="info-line-value2">300</div>
                  </div>
                  <div class="info-line">
                    <div class="info-icon"
                         style="background-color: #14C9C9;"></div>
                    <div class="info-line-title">物理性能</div>
                    <div class="info-line-value1">30%</div>
                    <div class="info-line-value2">300</div>
                  </div>
                  <div class="info-line">
                    <div class="info-icon"
                         style="background-color: #F7BA1E;"></div>
                    <div class="info-line-title">物理性能</div>
                    <div class="info-line-value1">30%</div>
                    <div class="info-line-value2">300</div>
                  </div>
                  <div class="info-line">
                    <div class="info-icon"
                         style="background-color: #722ED1;"></div>
                    <div class="info-line-title">物理性能</div>
                    <div class="info-line-value1">30%</div>
                    <div class="info-line-value2">300</div>
                  </div>
                  <div class="info-line">
                    <div class="info-icon"
                         style="background-color: #3491FA;"></div>
                    <div class="info-line-title">物理性能</div>
                    <div class="info-line-value1">30%</div>
                    <div class="info-line-value2">300</div>
                  </div>
                </div>
              </div>
              <div ref="sampleChartRef"
                   style="height: 100%; width: 50%;"
                   class="chart-container"></div>
            </div>
            <!-- Tab é€‰æ‹©å™¨ -->
            <div class="tab-selector">
              <div class="tab-item"
                   :class="{ active: activeTab === 'raw' }"
                   @click="activeTab = 'raw'">原材料</div>
              <div class="tab-item"
                   :class="{ active: activeTab === 'semi' }"
                   @click="activeTab = 'semi'">半成品</div>
              <div class="tab-item"
                   :class="{ active: activeTab === 'final' }"
                   @click="activeTab = 'final'">成品</div>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import * as echarts from 'echarts'
import { Box, Tools, Document, ShoppingCart } from '@element-plus/icons-vue'
  import { ref, reactive, onMounted, nextTick } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import * as echarts from "echarts";
  import { Box, Tools, Document, ShoppingCart } from "@element-plus/icons-vue";
// å“åº”式数据
const filterForm = reactive({
    dateRange: [],
    reportType: 'sample'
})
  // å“åº”式数据
  const filterForm = reactive({
    dateRange: [],
    reportType: "sample",
  });
const statistics = reactive({
    totalSamples: 1250,
    activeEquipment: 45,
    completedInspections: 890,
    totalUsage: 2340
})
  const statistics = reactive({
    totalSamples: 1250,
    activeEquipment: 45,
    completedInspections: 890,
    totalUsage: 2340,
  });
const tableData = ref([])
const tableLoading = ref(false)
const pagination = reactive({
    currentPage: 1,
    pageSize: 20,
    total: 0
})
  const tableData = ref([]);
  const tableLoading = ref(false);
  const pagination = reactive({
    currentPage: 1,
    pageSize: 20,
    total: 0,
  });
// å›¾è¡¨å¼•用
const sampleChartRef = ref(null)
const equipmentChartRef = ref(null)
const inspectionChartRef = ref(null)
const usageChartRef = ref(null)
  // åˆå§‹åŒ–年份为当前年份(使用Date对象)
  const currentYear = new Date().getFullYear();
  const value3 = ref(new Date(currentYear, 0, 1));
// å›¾è¡¨å®žä¾‹
let sampleChart = null
let equipmentChart = null
let inspectionChart = null
let usageChart = null
  // é™åˆ¶æ—¥æœŸé€‰æ‹©ï¼Œä¸å…è®¸é€‰æ‹©ä»Šå¹´ä¹‹åŽçš„年份
  const disabledDate = time => {
    const currentYear = new Date().getFullYear();
    return time.getFullYear() > currentYear;
  };
// åˆå§‹åŒ–数据
const initData = () => {
    // æ¨¡æ‹Ÿè¡¨æ ¼æ•°æ®
    tableData.value = [
        {
            id: 'SP001',
            name: '样品A-001',
            type: '金属材料',
            status: '检测中',
            progress: 75,
            createTime: '2025-01-15 09:30:00',
            updateTime: '2025-01-20 14:20:00'
        },
        {
            id: 'SP002',
            name: '样品B-002',
            type: '塑料制品',
            status: '已完成',
            progress: 100,
            createTime: '2025-01-10 10:15:00',
            updateTime: '2025-01-18 16:45:00'
        },
        {
            id: 'SP003',
            name: '样品C-003',
            type: '电子元件',
            status: '待检测',
            progress: 0,
            createTime: '2025-01-22 08:45:00',
            updateTime: '2025-01-22 08:45:00'
        },
        {
            id: 'EQ001',
            name: '检测设备A',
            type: '光谱仪',
            status: '使用中',
            progress: 60,
            createTime: '2025-01-05 14:20:00',
            updateTime: '2025-01-20 11:30:00'
        },
        {
            id: 'EQ002',
            name: '检测设备B',
            type: '显微镜',
            status: '空闲',
            progress: 0,
            createTime: '2025-01-08 16:10:00',
            updateTime: '2025-01-19 09:15:00'
        }
    ]
    pagination.total = tableData.value.length
}
  // Tab é€‰æ‹©å™¨å½“前激活项
  const activeTab = ref("raw");
// åˆå§‹åŒ–样品进度图表
const initSampleChart = () => {
    if (sampleChartRef.value) {
        sampleChart = echarts.init(sampleChartRef.value)
        const option = {
            title: {
                show: false
            },
            tooltip: {
                trigger: 'item',
                formatter: '{a} <br/>{b}: {c} ({d}%)'
            },
            legend: {
                orient: 'vertical',
                left: 'left'
            },
            series: [
                {
                    name: '样品状态',
                    type: 'pie',
                    radius: ['40%', '70%'],
                    avoidLabelOverlap: false,
                    label: {
                        show: false,
                        position: 'center'
                    },
                    emphasis: {
                        label: {
                            show: true,
                            fontSize: '18',
                            fontWeight: 'bold'
                        }
                    },
                    labelLine: {
                        show: false
                    },
                    data: [
                        { value: 450, name: '已完成' },
                        { value: 320, name: '检测中' },
                        { value: 280, name: '待检测' },
                        { value: 200, name: '已暂停' }
                    ]
                }
            ]
        }
        sampleChart.setOption(option)
    }
}
  // å›¾è¡¨å¼•用
  const sampleChartRef = ref(null);
  const equipmentChartRef = ref(null);
  const inspectionChartRef = ref(null);
  const usageChartRef = ref(null);
// åˆå§‹åŒ–设备使用图表
const initEquipmentChart = () => {
    if (equipmentChartRef.value) {
        equipmentChart = echarts.init(equipmentChartRef.value)
        const option = {
            title: {
                show: false
            },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    type: 'shadow'
                }
            },
            xAxis: {
                type: 'category',
                data: ['光谱仪', '显微镜', '硬度计', '拉力机', '冲击机', '金相仪']
            },
            yAxis: {
                type: 'value',
                name: '使用率(%)'
            },
            series: [
                {
                    name: '使用率',
                    type: 'bar',
                    data: [85, 60, 75, 90, 45, 70],
                    itemStyle: {
                        color: function(params) {
                            const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#9C27B0']
                            return colors[params.dataIndex]
                        }
                    }
                }
            ]
        }
        equipmentChart.setOption(option)
    }
}
  // è´¨æ£€åˆæ ¼çŽ‡å›¾è¡¨å¼•ç”¨
  const materialCompletionChart = ref(null);
  const materialQualityChart = ref(null);
  const semiCompletionChart = ref(null);
  const semiQualityChart = ref(null);
  const finalCompletionChart = ref(null);
  const finalQualityChart = ref(null);
// åˆå§‹åŒ–检测项目图表
const initInspectionChart = () => {
    if (inspectionChartRef.value) {
        inspectionChart = echarts.init(inspectionChartRef.value)
        const option = {
            title: {
                show: false
            },
            tooltip: {
                trigger: 'item'
            },
            legend: {
                orient: 'vertical',
                left: 'left'
            },
            series: [
                {
                    name: '检测项目',
                    type: 'pie',
                    radius: '50%',
                    data: [
                        { value: 335, name: '物理性能' },
                        { value: 310, name: '化学分析' },
                        { value: 234, name: '尺寸测量' },
                        { value: 135, name: '外观检查' },
                        { value: 148, name: '其他检测' }
                    ],
                    emphasis: {
                        itemStyle: {
                            shadowBlur: 10,
                            shadowOffsetX: 0,
                            shadowColor: 'rgba(0, 0, 0, 0.5)'
                        }
                    }
                }
            ]
        }
        inspectionChart.setOption(option)
    }
}
  // å›¾è¡¨å®žä¾‹
  let sampleChart = null;
  let equipmentChart = null;
  let inspectionChart = null;
  let usageChart = null;
// åˆå§‹åŒ–领用记录图表
const initUsageChart = () => {
    if (usageChartRef.value) {
        usageChart = echarts.init(usageChartRef.value)
        const option = {
            title: {
                show: false
            },
            tooltip: {
                trigger: 'axis'
            },
            legend: {
                data: ['领用次数', '归还次数']
            },
            xAxis: {
                type: 'category',
                boundaryGap: false,
                data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
            },
            yAxis: {
                type: 'value'
            },
            series: [
                {
                    name: '领用次数',
                    type: 'line',
                    stack: 'Total',
                    data: [120, 132, 101, 134, 90, 230, 210, 182, 191, 234, 290, 330]
                },
                {
                    name: '归还次数',
                    type: 'line',
                    stack: 'Total',
                    data: [110, 125, 95, 128, 85, 220, 200, 175, 185, 225, 280, 320]
                }
            ]
        }
        usageChart.setOption(option)
    }
}
  // è´¨æ£€åˆæ ¼çŽ‡å›¾è¡¨å®žä¾‹
  let materialCompletionChartInstance = null;
  let materialQualityChartInstance = null;
  let semiCompletionChartInstance = null;
  let semiQualityChartInstance = null;
  let finalCompletionChartInstance = null;
  let finalQualityChartInstance = null;
// äº‹ä»¶å¤„理函数
const handleFilterChange = () => {
    ElMessage.success('筛选条件已更新')
    // è¿™é‡Œå¯ä»¥æ ¹æ®ç­›é€‰æ¡ä»¶é‡æ–°åŠ è½½æ•°æ®
}
  // åˆå§‹åŒ–数据
  const initData = () => {
    // æ¨¡æ‹Ÿè¡¨æ ¼æ•°æ®
    tableData.value = [
      {
        id: "SP001",
        name: "样品A-001",
        type: "金属材料",
        status: "检测中",
        progress: 75,
        createTime: "2025-01-15 09:30:00",
        updateTime: "2025-01-20 14:20:00",
      },
      {
        id: "SP002",
        name: "样品B-002",
        type: "塑料制品",
        status: "已完成",
        progress: 100,
        createTime: "2025-01-10 10:15:00",
        updateTime: "2025-01-18 16:45:00",
      },
      {
        id: "SP003",
        name: "样品C-003",
        type: "电子元件",
        status: "待检测",
        progress: 0,
        createTime: "2025-01-22 08:45:00",
        updateTime: "2025-01-22 08:45:00",
      },
      {
        id: "EQ001",
        name: "检测设备A",
        type: "原材料",
        status: "使用中",
        progress: 60,
        createTime: "2025-01-05 14:20:00",
        updateTime: "2025-01-20 11:30:00",
      },
      {
        id: "EQ002",
        name: "检测设备B",
        type: "半成品",
        status: "空闲",
        progress: 0,
        createTime: "2025-01-08 16:10:00",
        updateTime: "2025-01-19 09:15:00",
      },
    ];
const resetFilter = () => {
    filterForm.dateRange = []
    filterForm.reportType = 'sample'
    ElMessage.info('筛选条件已重置')
}
    pagination.total = tableData.value.length;
  };
const exportReport = () => {
    ElMessage.success('报表导出功能开发中...')
}
  // åˆå§‹åŒ–样品进度图表
  const initSampleChart = () => {
    if (sampleChartRef.value) {
      sampleChart = echarts.init(sampleChartRef.value);
      const option = {
        title: {
          show: false,
        },
        tooltip: {
          trigger: "item",
          formatter: "{a} <br/>{b}: {c} ({d}%)",
        },
        // legend: {
        //   orient: "vertical",
        //   left: "left",
        // },
        series: [
          {
            name: "样品状态",
            type: "pie",
            radius: ["40%", "80%"],
            avoidLabelOverlap: false,
            label: {
              show: false,
              position: "center",
            },
            emphasis: {
              label: {
                show: true,
                fontSize: "18",
                fontWeight: "bold",
              },
            },
            labelLine: {
              show: false,
            },
            data: [
              { value: 450, name: "已完成" },
              { value: 320, name: "检测中" },
              { value: 280, name: "待检测" },
              { value: 200, name: "已暂停" },
            ],
          },
        ],
      };
      sampleChart.setOption(option);
    }
  };
const refreshSampleChart = () => {
    initSampleChart()
    ElMessage.success('样品进度图表已刷新')
}
  // åˆå§‹åŒ–设备使用图表
  const initEquipmentChart = () => {
    if (equipmentChartRef.value) {
      equipmentChart = echarts.init(equipmentChartRef.value);
      const option = {
        title: {
          show: false,
        },
        tooltip: {
          trigger: "axis",
          axisPointer: {
            type: "shadow",
          },
        },
        grid: {
          left: "1%",
          right: "1%",
          bottom: "1%",
          containLabel: true,
        },
        legend: {
          data: ["原材料", "半成品", "成品"], // å›¾ä¾‹æ•°æ®
          icon: ["circle", "circle", "circle"],
          itemWidth: 10, // è®¾ç½®å›¾æ ‡å®½åº¦
          itemHeight: 10,
          itemGap: 30,
          right: 10,
        },
        xAxis: {
          type: "category",
          data: [
            value3.value.getFullYear() + "-1",
            value3.value.getFullYear() + "-2",
            value3.value.getFullYear() + "-3",
            value3.value.getFullYear() + "-4",
            value3.value.getFullYear() + "-5",
            value3.value.getFullYear() + "-6",
            value3.value.getFullYear() + "-7",
            value3.value.getFullYear() + "-8",
            value3.value.getFullYear() + "-9",
            value3.value.getFullYear() + "-10",
            value3.value.getFullYear() + "-11",
            value3.value.getFullYear() + "-12",
          ], // æ”¹ä¸ºåäºŒä¸ªæœˆ
        },
        yAxis: {
          type: "value",
          name: "数(个)",
        },
        series: [
          {
            name: "原材料",
            type: "bar",
            barWidth: "15%",
            data: [85, 75, 80, 85, 90, 88, 92, 87, 89, 91, 93, 95],
            itemStyle: {
              color: "#409EFF",
            },
          },
          {
            name: "半成品",
            type: "bar",
            barWidth: "15%",
const refreshEquipmentChart = () => {
    initEquipmentChart()
    ElMessage.success('设备使用图表已刷新')
}
            data: [60, 65, 70, 68, 72, 75, 78, 80, 79, 82, 84, 85],
            itemStyle: {
              color: "#67C23A",
            },
          },
          {
            name: "成品",
            type: "bar",
            barWidth: "15%",
const refreshInspectionChart = () => {
    initInspectionChart()
    ElMessage.success('检测项目图表已刷新')
}
            data: [75, 78, 80, 82, 85, 83, 86, 88, 87, 89, 90, 92],
            itemStyle: {
              color: "#E6A23C",
            },
          },
        ],
      };
      equipmentChart.setOption(option);
    }
  };
const refreshUsageChart = () => {
    initUsageChart()
    ElMessage.success('领用记录图表已刷新')
}
  // åˆå§‹åŒ–检测项目图表
  const initInspectionChart = () => {
    if (inspectionChartRef.value) {
      inspectionChart = echarts.init(inspectionChartRef.value);
      const option = {
        title: {
          show: false,
        },
        tooltip: {
          trigger: "item",
        },
        series: [
          {
            type: "pie",
            radius: "80%",
            data: [
              { value: 335, name: "原材料", itemStyle: { color: "#1890FF" } },
              { value: 310, name: "半成品", itemStyle: { color: "#F7BA1E" } },
              { value: 234, name: "成品", itemStyle: { color: "#14C9C9" } },
            ],
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: "rgba(0, 0, 0, 0.5)",
              },
            },
          },
        ],
      };
      inspectionChart.setOption(option);
    }
  };
const refreshTable = () => {
    tableLoading.value = true
    setTimeout(() => {
        tableLoading.value = false
        ElMessage.success('表格数据已刷新')
    }, 1000)
}
  // åˆå§‹åŒ–领用记录图表
  const initUsageChart = () => {
    // æ£€æŸ¥å›¾è¡¨å®¹å™¨æ˜¯å¦å­˜åœ¨
    if (usageChartRef.value) {
      // åˆå§‹åŒ– ECharts å®žä¾‹
      usageChart = echarts.init(usageChartRef.value);
      // é…ç½®å›¾è¡¨é€‰é¡¹
      const option = {
        // æ ‡é¢˜é…ç½®ï¼ˆéšè—ï¼‰
        title: {
          show: false,
        },
const exportTable = () => {
    ElMessage.success('表格导出功能开发中...')
}
        // ç½‘格配置(调整边距)
        grid: {
          left: "1%",
          right: "4%",
          bottom: "3%",
          top: "14%",
          containLabel: true,
        },
        // æç¤ºæ¡†é…ç½®
        tooltip: {
          trigger: "axis", // è§¦å‘类型为坐标轴触发
        },
        // å›¾ä¾‹é…ç½®
        legend: {
          data: ["原材料", "半成品", "成品"], // å›¾ä¾‹æ•°æ®
          icon: ["circle", "circle", "circle"],
          itemWidth: 10, // è®¾ç½®å›¾æ ‡å®½åº¦
          itemHeight: 10,
          itemGap: 30,
        },
        // X轴配置
        xAxis: {
          type: "category", // ç±»åˆ«è½´
          boundaryGap: false, // åæ ‡è½´ä¸¤è¾¹ç•™ç™½ç­–ç•¥
          data: [
            value3.value.getFullYear() + "-1",
            value3.value.getFullYear() + "-2",
            value3.value.getFullYear() + "-3",
            value3.value.getFullYear() + "-4",
            value3.value.getFullYear() + "-5",
            value3.value.getFullYear() + "-6",
            value3.value.getFullYear() + "-7",
            value3.value.getFullYear() + "-8",
            value3.value.getFullYear() + "-9",
            value3.value.getFullYear() + "-10",
            value3.value.getFullYear() + "-11",
            value3.value.getFullYear() + "-12",
          ], // X轴数据
        },
        // Y轴配置
        yAxis: {
          type: "value", // æ•°å€¼è½´
          name: "单位:%",
        },
        // ç³»åˆ—数据
        series: [
          {
            name: "原材料", // ç³»åˆ—名称
            type: "line", // å›¾è¡¨ç±»åž‹ä¸ºæŠ˜çº¿å›¾
            stack: "Total", // å †å åç§°
            symbol: "none",
            itemStyle: {
              color: "#1890FF", // è®¾ç½®è¿™æ¡çº¿çš„颜色
            },
            data: [120, 132, 101, 134, 90, 230, 210, 182, 191, 234, 290, 330], // é¢†ç”¨æ¬¡æ•°æ•°æ®
          },
          {
            name: "半成品", // ç³»åˆ—名称
            type: "line", // å›¾è¡¨ç±»åž‹ä¸ºæŠ˜çº¿å›¾
            stack: "Total", // å †å åç§°
            symbol: "none",
            itemStyle: {
              color: "#F7BA1E", // è®¾ç½®è¿™æ¡çº¿çš„颜色
            },
            data: [110, 125, 95, 128, 85, 220, 200, 175, 185, 225, 280, 320], // å½’还次数数据
          },
          {
            name: "成品", // ç³»åˆ—名称
            type: "line", // å›¾è¡¨ç±»åž‹ä¸ºæŠ˜çº¿å›¾
            stack: "Total", // å †å åç§°
            symbol: "none",
            itemStyle: {
              color: "#14C9C9", // è®¾ç½®è¿™æ¡çº¿çš„颜色
            },
            data: [110, 125, 95, 128, 85, 220, 200, 175, 185, 225, 280, 320], // å½’还次数数据
          },
        ],
      };
      // å°†é…ç½®åº”用到图表
      usageChart.setOption(option);
    }
  };
const handleSizeChange = (val) => {
    pagination.pageSize = val
    // é‡æ–°åŠ è½½æ•°æ®
}
  // åˆå§‹åŒ–质检合格率图表
  const initQualityChart = (chartRef, color) => {
    if (chartRef.value) {
      const chart = echarts.init(chartRef.value);
      const option = {
        series: [
          {
            type: "pie",
            radius: ["45%", "90%"],
            itemStyle: {
              borderColor: "#f5f5f5",
              // borderWidth: 2,
            },
            labelLine: {
              show: false,
            },
            data: [
              { value: 0.8, itemStyle: { color: color } },
              { value: 0.2, itemStyle: { color: "#f5f5f5" } },
            ],
          },
        ],
      };
      chart.setOption(option);
      return chart;
    }
    return null;
  };
const handleCurrentChange = (val) => {
    pagination.currentPage = val
    // é‡æ–°åŠ è½½æ•°æ®
}
  // åˆå§‹åŒ–所有质检合格率图表
  const initAllQualityCharts = () => {
    materialCompletionChartInstance = initQualityChart(
      materialCompletionChart,
      "#1890ff"
    );
    materialQualityChartInstance = initQualityChart(
      materialQualityChart,
      "#52c41a"
    );
    semiCompletionChartInstance = initQualityChart(
      semiCompletionChart,
      "#1890ff"
    );
    semiQualityChartInstance = initQualityChart(semiQualityChart, "#52c41a");
    finalCompletionChartInstance = initQualityChart(
      finalCompletionChart,
      "#1890ff"
    );
    finalQualityChartInstance = initQualityChart(finalQualityChart, "#722ed1");
  };
const getStatusType = (status) => {
    const statusMap = {
        '已完成': 'success',
        '检测中': 'warning',
        '待检测': 'info',
        '已暂停': 'danger',
        '使用中': 'primary',
        '空闲': 'info'
    }
    return statusMap[status] || 'info'
}
  // äº‹ä»¶å¤„理函数
  const handleFilterChange = () => {
    ElMessage.success("筛选条件已更新");
    // è¿™é‡Œå¯ä»¥æ ¹æ®ç­›é€‰æ¡ä»¶é‡æ–°åŠ è½½æ•°æ®
  };
const getProgressStatus = (progress) => {
    if (progress === 100) return 'success'
    if (progress >= 80) return 'warning'
    if (progress >= 50) return ''
    return 'exception'
}
  const resetFilter = () => {
    filterForm.dateRange = [];
    filterForm.reportType = "sample";
    ElMessage.info("筛选条件已重置");
  };
const viewDetail = (row) => {
    ElMessage.info(`查看详情: ${row.name}`)
}
  const exportReport = () => {
    ElMessage.success("报表导出功能开发中...");
  };
const editItem = (row) => {
    ElMessage.info(`编辑项目: ${row.name}`)
}
  const refreshSampleChart = () => {
    initSampleChart();
    ElMessage.success("样品进度图表已刷新");
  };
// ç”Ÿå‘½å‘¨æœŸ
onMounted(() => {
    initData()
    nextTick(() => {
        initSampleChart()
        initEquipmentChart()
        initInspectionChart()
        initUsageChart()
    })
    // ç›‘听窗口大小变化,重新调整图表大小
    window.addEventListener('resize', () => {
        sampleChart?.resize()
        equipmentChart?.resize()
        inspectionChart?.resize()
        usageChart?.resize()
    })
})
  const refreshEquipmentChart = () => {
    initEquipmentChart();
    ElMessage.success("设备使用图表已刷新");
  };
  const refreshInspectionChart = () => {
    initInspectionChart();
    ElMessage.success("检测项目图表已刷新");
  };
  const refreshUsageChart = () => {
    initUsageChart();
    ElMessage.success("领用记录图表已刷新");
  };
  const refreshTable = () => {
    tableLoading.value = true;
    setTimeout(() => {
      tableLoading.value = false;
      ElMessage.success("表格数据已刷新");
    }, 1000);
  };
  const exportTable = () => {
    ElMessage.success("表格导出功能开发中...");
  };
  const handleSizeChange = val => {
    pagination.pageSize = val;
    // é‡æ–°åŠ è½½æ•°æ®
  };
  const handleCurrentChange = val => {
    pagination.currentPage = val;
    // é‡æ–°åŠ è½½æ•°æ®
  };
  const getStatusType = status => {
    const statusMap = {
      å·²å®Œæˆ: "success",
      æ£€æµ‹ä¸­: "warning",
      å¾…检测: "info",
      å·²æš‚停: "danger",
      ä½¿ç”¨ä¸­: "primary",
      ç©ºé—²: "info",
    };
    return statusMap[status] || "info";
  };
  const getProgressStatus = progress => {
    if (progress === 100) return "success";
    if (progress >= 80) return "warning";
    if (progress >= 50) return "";
    return "exception";
  };
  const viewDetail = row => {
    ElMessage.info(`查看详情: ${row.name}`);
  };
  const editItem = row => {
    ElMessage.info(`编辑项目: ${row.name}`);
  };
  // ç”Ÿå‘½å‘¨æœŸ
  onMounted(() => {
    initData();
    nextTick(() => {
      initSampleChart();
      initEquipmentChart();
      initInspectionChart();
      initUsageChart();
      initAllQualityCharts();
    });
    // ç›‘听窗口大小变化,重新调整图表大小
    window.addEventListener("resize", () => {
      sampleChart?.resize();
      equipmentChart?.resize();
      inspectionChart?.resize();
      usageChart?.resize();
      // è°ƒæ•´è´¨æ£€åˆæ ¼çŽ‡å›¾è¡¨å¤§å°
      materialCompletionChartInstance?.resize();
      materialQualityChartInstance?.resize();
      semiCompletionChartInstance?.resize();
      semiQualityChartInstance?.resize();
      finalCompletionChartInstance?.resize();
      finalQualityChartInstance?.resize();
    });
  });
</script>
<style scoped>
.report-management {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 100vh;
}
  .report-management {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 100vh;
    /* height: 87vh;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              overflow: hidden; */
  }
.page-header {
    margin-bottom: 20px;
    text-align: center;
}
  .page-header {
    margin-bottom: 20px;
    text-align: center;
  }
.page-header h2 {
    color: #303133;
    margin-bottom: 8px;
    font-size: 24px;
    font-weight: 600;
}
  .page-header h2 {
    color: #303133;
    margin-bottom: 8px;
    font-size: 24px;
    font-weight: 600;
  }
.page-header p {
    color: #909399;
    font-size: 14px;
    margin: 0;
}
  .page-header p {
    color: #909399;
    font-size: 14px;
    margin: 0;
  }
.filter-card {
    margin-bottom: 20px;
}
  .filter-card {
    margin-bottom: 20px;
  }
.statistics-cards {
    margin-bottom: 20px;
}
  .statistics-cards {
    margin-bottom: 20px;
  }
.stat-card {
    height: 120px;
}
  .stat-card {
    height: 120px;
  }
.stat-content {
    display: flex;
    align-items: center;
    height: 100%;
}
  .stat-content {
    display: flex;
    align-items: center;
    height: 100%;
  }
.stat-icon {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 20px;
    font-size: 24px;
    color: white;
}
  .stat-icon {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 20px;
    font-size: 24px;
    color: white;
  }
.stat-card:nth-child(1) .stat-icon {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
  .stat-card:nth-child(1) .stat-icon {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  }
.stat-card:nth-child(2) .stat-icon {
    background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
  .stat-card:nth-child(2) .stat-icon {
    background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  }
.stat-card:nth-child(3) .stat-icon {
    background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
  .stat-card:nth-child(3) .stat-icon {
    background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
  }
.stat-card:nth-child(4) .stat-icon {
    background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
  .stat-card:nth-child(4) .stat-icon {
    background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
  }
.stat-info {
    flex: 1;
}
  .stat-info {
    flex: 1;
  }
.stat-number {
    font-size: 28px;
    font-weight: bold;
    color: #303133;
    margin-bottom: 8px;
}
  .stat-number {
    font-size: 28px;
    font-weight: bold;
    color: #303133;
    margin-bottom: 8px;
  }
.stat-label {
    font-size: 14px;
    color: #909399;
}
  .stat-label {
    font-size: 14px;
    color: #909399;
  }
.charts-container {
    margin-bottom: 20px;
}
  .charts-container {
    /* margin-bottom: 20px; */
    position: relative;
  }
.chart-card {
    margin-bottom: 20px;
}
  .chart-card {
    margin-bottom: 20px;
  }
  .container-line-right-bottom {
    height: 20%;
    width: 100%;
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    /* background-color: #5b3f3f; */
  }
  .card-header {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    font-family: Source Han Sans, Source Han Sans;
    font-weight: 700;
    font-size: 18px;
    color: #000000;
    /* line-height: 27px; */
    text-align: left;
    font-style: normal;
    text-transform: none;
  }
  .chart-title-line {
    width: 6px;
    height: 22px;
    background-color: #161a9a;
    margin-right: 16px;
    border-radius: 3px;
  }
.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
  .chart-container {
    height: 250px;
    width: 100%;
  }
  .chart-container-line {
    height: 250px;
    width: 100%;
    display: flex;
    position: relative;
  }
.chart-container {
    height: 300px;
    width: 100%;
}
  /* Tab é€‰æ‹©å™¨æ ·å¼ */
  .tab-selector {
    position: absolute;
    top: 20px;
    right: 40px;
    display: flex;
    border: 1px solid #dcdfe6;
    border-radius: 4px;
    overflow: hidden;
  }
.table-card {
    margin-bottom: 20px;
}
  .tab-item {
    padding: 4px 12px;
    cursor: pointer;
    font-size: 14px;
    color: #606266;
    background-color: #fff;
    border-right: 1px solid #dcdfe6;
    transition: all 0.3s;
  }
.pagination-container {
    margin-top: 20px;
    text-align: right;
}
  .tab-item:last-child {
    border-right: none;
  }
:deep(.el-card__header) {
    padding: 15px 20px;
    border-bottom: 1px solid #ebeef5;
    background-color: #fafafa;
}
  .tab-item:hover {
    color: #409eff;
  }
:deep(.el-card__body) {
    padding: 20px;
}
  .tab-item.active {
    color: #fff;
    background-color: #409eff;
  }
  .container-line-left {
    height: 100%;
    width: 66%;
  }
  .container-line-right {
    height: 100%;
    width: 34%;
  }
  .container-line2-left {
    height: 100%;
    width: 50%;
  }
  .info-box {
    width: 92%;
    margin-left: 4%;
    height: 100%;
    background-color: #f7f8fa;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-around;
  }
  .info-box-header {
    width: 100%;
    margin-left: 20px;
    color: #1d2129;
    font-size: 16px;
    font-weight: 500;
  }
:deep(.el-table) {
    margin-bottom: 20px;
}
  .info-line {
    width: 100%;
    display: flex;
    padding-left: 20px;
    align-items: center;
  }
  .info-icon {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    margin-right: 8px;
  }
  .info-line-title {
    font-size: 12px;
    color: #4e5969;
    flex: 1;
  }
  .info-line-value1 {
    font-size: 12px;
    color: #3d3d3d;
    color: #1d2129;
    font-weight: 500;
    margin-right: 15%;
  }
  .info-line-value2 {
    font-size: 12px;
    color: #3d3d3d;
    color: #1d2129;
    font-weight: 500;
    margin-right: 10%;
  }
  .top-container {
    height: 130px;
    width: 100%;
    display: flex;
  }
  .typeNum {
    height: 100%;
    width: 33.33%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .typeNum-left {
    font-size: 12px;
    color: #909399;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .typeNum-left-text {
    font-size: 12px;
    color: #3491fa;
    font-weight: 500;
    margin-top: 5px;
  }
  .table-card {
    margin-bottom: 20px;
  }
  .typeNum-center {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: 10px;
  }
  .typeNum-leftLine {
    color: #3491fa;
    font-size: 12px;
  }
  .typeNum-rightLine {
    border-top: 1px solid #3491fa;
    border-left: 1px solid #3491fa;
    border-bottom: 1px solid #3491fa;
    height: 80px;
    width: 8px;
  }
  .typeNum-leftLine2 {
    color: #5eb334;
    font-size: 12px;
  }
  .typeNum-rightLine2 {
    border-top: 1px solid #3491fa;
    border-left: 1px solid #5eb334;
    border-bottom: 1px solid #5eb334;
    height: 80px;
    width: 8px;
  }
  .typeNum-leftLine3 {
    color: #8000ff;
    font-size: 12px;
  }
  .typeNum-rightLine3 {
    border-top: 1px solid #8000ff;
    border-left: 1px solid #8000ff;
    border-bottom: 1px solid #8000ff;
    height: 80px;
    width: 8px;
  }
  .typeNum-right {
    margin-left: 10px;
    display: flex;
    flex-direction: column;
    height: 90%;
    justify-content: space-between;
  }
  .typeNum-right-top-name {
    font-weight: 400;
    font-size: 12px;
    color: #3d3d3d;
  }
  .typeNum-right-top-text {
    font-weight: 400;
    font-size: 16px;
    color: rgba(0, 0, 0, 0.85);
    margin-top: 5px;
  }
  .unit {
    font-size: 12px;
    color: #3d3d3d;
  }
  .inspection-chart-box {
    height: 50px;
    width: 30%;
    background-color: #f7f8fa;
    border-radius: 8px;
    padding-left: 15px;
  }
  .chart-box-title {
    font-size: 12px;
    color: #4e5969;
    margin-top: 5px;
  }
  .unit {
    font-size: 12px;
    color: #3d3d3d;
  }
  .chart-box-num {
    font-size: 18px;
    color: #000;
    margin-top: 5px;
    font-weight: 500;
  }
  /* è´¨æ£€åˆæ ¼çŽ‡å¡ç‰‡æ ·å¼ */
  .top-container合格率 {
    height: 130px;
    width: 100%;
    display: flex;
    gap: 15px;
    align-items: center;
    justify-content: space-between;
  }
  .flex-center {
    justify-content: space-evenly;
  }
  .quality-card {
    /* flex: 1; */
    width: 32%;
    /* height: 100px; */
    border-radius: 8px;
    padding: 12px;
    display: flex;
    flex-direction: column;
  }
:deep(.el-progress) {
    margin: 0;
}
  .blue-card {
    background-color: #e6f7ff;
  }
:deep(.el-tag) {
    margin: 0;
}
  .green-card {
    background-color: #f6ffed;
    color: #000000;
  }
  .purple-card {
    background-color: #f9f0ff;
  }
  .quality-card-title {
    font-size: 14px;
    font-weight: 500;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
  }
  .quality-item-tip {
    font-size: 12px;
    color: #666666;
    margin-bottom: 3px;
  }
  .blue-label {
    color: #1890ff;
  }
  .green-label {
    color: #52c41a;
  }
  .quality-card-title {
    color: #000;
    font-weight: bold;
  }
  .quality-card-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex: 1;
  }
  .quality-item {
    display: flex;
    /* flex-direction: column; */
    align-items: center;
    justify-content: center;
    margin-top: 5px;
    flex: 1;
  }
  .quality-item-label {
    font-size: 12px;
    /* color: #666; */
    margin-bottom: 4px;
  }
  .quality-item-value {
    font-size: 20px;
    font-weight: 500;
    margin-bottom: 4px;
  }
  .quality-item-chart {
    width: 60px;
    height: 60px;
    margin-left: 10px;
  }
  /* .flex-center {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            justify-content: space-evenly;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          } */
  .blue-chart {
    /* background-color: rgba(24, 144, 255, 0.1); */
  }
  .green-chart {
    /* background-color: rgba(82, 196, 26, 0.1); */
  }
  .purple-chart {
    /* background-color: rgba(114, 46, 209, 0.1); */
  }
  .chart-ring {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    border: 15px solid transparent;
    position: relative;
  }
  .blue-chart .chart-ring {
    border-top-color: #1890ff;
    border-right-color: #1890ff;
    border-bottom-color: #1890ff;
    transform: rotate(45deg);
  }
  .green-chart .chart-ring {
    border-top-color: #52c41a;
    border-right-color: #52c41a;
    border-bottom-color: #52c41a;
    transform: rotate(45deg);
  }
  .purple-chart .chart-ring {
    border-top-color: #722ed1;
    border-right-color: #722ed1;
    border-bottom-color: #722ed1;
    transform: rotate(45deg);
  }
  .pagination-container {
    margin-top: 20px;
    text-align: right;
  }
  .yearchange {
    position: absolute;
    right: 40px;
    top: 20px;
    display: flex;
    align-items: center;
    /* width: 60px; */
  }
  :deep(.el-card__header) {
    padding: 15px 20px;
    border-bottom: 1px solid #ffffff;
    background-color: #ffffff;
  }
  :deep(.el-card__body) {
    padding: 20px;
  }
  :deep(.el-table) {
    margin-bottom: 20px;
  }
  :deep(.el-progress) {
    margin: 0;
  }
  :deep(.el-tag) {
    margin: 0;
  }
  :deep(.el-input__prefix) {
    display: none !important;
  }
</style>
src/views/salesManagement/salesLedger/fileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
<template>
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose">
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="100" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
  <filePreview ref="filePreviewRef" />
</template>
<script setup>
import { ref } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
const dialogVisible = ref(false)
const tableData = ref([])
const { proxy } = getCurrentInstance();
const filePreviewRef = ref()
const handleClose = () => {
  dialogVisible.value = false
}
const open = (list) => {
  dialogVisible.value = true
  tableData.value = list
}
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
defineExpose({
  open
})
</script>
<style></style>
src/views/salesManagement/salesLedger/index.vue
@@ -6,8 +6,16 @@
          <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="客户合同号:">
          <el-input v-model="searchForm.customerContractNo" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item 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"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="录入日期:">
@@ -26,7 +34,6 @@
          <el-button type="primary" @click="openForm('add')">
            æ–°å¢žå°è´¦
          </el-button>
          <el-button @click="handleImport">导入</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>
@@ -34,7 +41,7 @@
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 21em)">
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column type="expand">
          <template #default="props">
@@ -43,67 +50,72 @@
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="产品状态" width="100px" align="center">
                <template #default="scope">
                  <el-tag v-if="scope.row.approveStatus === 0" type="info">未出库</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 1" type="success">已出库</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 2" type="warning">审核中</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 3" type="success">审核成功</el-tag>
                  <el-tag v-if="scope.row.approveStatus === 4" type="danger">审核失败</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="发货车牌" minWidth="100px" align="center">
                <template #default="scope">
                  <div>
                    <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
                    <el-tag v-else type="info">未发货</el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="发货日期" minWidth="100px" align="center">
                <template #default="scope">
                  <div>
                    <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
                    <el-tag v-else type="info">未发货</el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <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 Width="60px" label="操作" align="center">
                <template #default="scope">
                  <el-button :disabled="scope.row.approveStatus!==2 || scope.row.approveStatus!==5" link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户合同号" prop="customerContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
        <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
                <el-table-column label="生产状态" prop="productionStatus" width="100" show-overflow-tooltip >
                    <template #default="scope">
                        <div>
                            <el-tag v-if="scope.row.productionStatus === '已完成'" type="success">已完成</el-tag>
                            <el-tag v-if="scope.row.productionStatus === '生产中'" type="warning">生产中</el-tag>
                            <el-tag v-if="scope.row.productionStatus === '未开始'" type="danger">未开始</el-tag>
                        </div>
                    </template>
                </el-table-column>
        <el-table-column label="发货车牌" prop="shippingCarNumber" width="120" show-overflow-tooltip>
          <template #default="scope">
            <div>
              <div v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</div>
              <el-tag v-else type="warning">未发货</el-tag>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="发货日期" prop="shippingDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="200" align="center">
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
<!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>-->
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button v-if="!scope.row.shippingCarNumber" link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>
<!--            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>-->
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <FormDialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'"
      :width="'70%'"
      :operation-type="operationType"
      @close="closeDia"
      @confirm="submitForm"
      @cancel="closeDia">
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" width="70%"
      @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row v-if="operationType !== 'view'">
          <el-col :span="24" style="display:flex; justify-content:flex-end; gap:10px; margin-bottom: 6px;">
            <el-button type="primary" plain @click="openQuotationDialog">从审批通过的报价单导入</el-button>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="销售合同号:" prop="salesContractNo">
@@ -112,9 +124,7 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman"
                                                 filterable
                         :reserve-keyword="false" placeholder="请选择" clearable :disabled="operationType === 'view'">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                  :value="item.nickName" />
              </el-select>
@@ -123,14 +133,26 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户合同号:" prop="customerContractNo">
              <el-input v-model="form.customerContractNo" placeholder="请输入" clearable :disabled="operationType === 'view'"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                  }}
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
@@ -143,10 +165,7 @@
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人:" prop="entryPerson">
                            <el-select v-model="form.entryPerson"
                                                 filterable
                         default-first-option
                         :reserve-keyword="false" placeholder="请选择" clearable @change="changs">
                            <el-select v-model="form.entryPerson" placeholder="请选择" clearable @change="changs" disabled>
                                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
                            </el-select>
                        </el-form-item>
@@ -158,7 +177,13 @@
            </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 :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
@@ -208,71 +233,14 @@
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
    <!-- ä»ŽæŠ¥ä»·å•导入(仅审批通过) -->
    <el-dialog
      v-model="quotationDialogVisible"
      title="选择审批通过的销售报价单"
      width="80%"
      :close-on-click-modal="false"
    >
      <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
        <el-input
          v-model="quotationSearchForm.quotationNo"
          placeholder="请输入报价单号"
          clearable
          style="max-width: 260px;"
          @change="fetchQuotationList"
        />
        <el-input
          v-model="quotationSearchForm.customer"
          placeholder="请输入客户名称"
          clearable
          style="max-width: 260px;"
          @change="fetchQuotationList"
        />
        <el-button type="primary" @click="fetchQuotationList">搜索</el-button>
        <el-button @click="resetQuotationSearch">重置</el-button>
      </div>
      <el-table
        :data="quotationList"
        border
        stripe
        v-loading="quotationLoading"
        height="420px"
      >
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column prop="quotationNo" label="报价单号" width="180" show-overflow-tooltip />
        <el-table-column prop="customer" label="客户名称" min-width="220" show-overflow-tooltip />
        <el-table-column prop="salesperson" label="业务员" width="120" show-overflow-tooltip />
        <el-table-column prop="quotationDate" label="报价日期" width="140" />
        <el-table-column prop="status" label="审批状态" width="120" align="center" />
        <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right">
          <template #default="scope">
            {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
          </template>
        </el-table-column>
        <el-table-column fixed="right" label="操作" width="120" align="center">
          <template #default="scope">
            <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button>
          </template>
        </el-table-column>
      </el-table>
      <template #footer>
        <el-button @click="quotationDialogVisible = false">关闭</el-button>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <FormDialog
      v-model="productFormVisible"
      :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
      :width="'40%'"
      :operation-type="productOperationType"
      @close="closeProductDia"
      @confirm="submitProduct"
      @cancel="closeProductDia">
    <el-dialog v-model="productFormVisible" :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" width="40%" @close="closeProductDia">
      <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
@@ -288,7 +256,7 @@
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel" filterable>
              <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
              </el-select>
            </el-form-item>
@@ -349,7 +317,13 @@
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
        <!-- æ‰“印预览弹窗 -->
        <el-dialog
            v-model="printPreviewVisible"
@@ -384,15 +358,12 @@
                                        <span class="value">{{ formatDate(item.createTime) }}</span>
                                    </div>
                                    <div>
                                        <span class="label">发货车牌号:</span>
                                        <span class="value">{{ item.shippingCarNumber }}</span>
                                    </div>
                                </div>
                                <div class="info-row">
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName || '张爱有' }}</span>
                                    </div>
                                </div>
                                <div class="info-row">
                                    <span class="label">单号:</span>
                                    <span class="value">{{ item.salesContractNo }}</span>
                                </div>
@@ -501,6 +472,15 @@
                        </el-form-item>
                    </el-col>
                </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="审批人:" prop="approverId">
              <el-select v-model="deliveryForm.approverId" placeholder="请选择审批人" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
@@ -559,24 +539,21 @@
import { UploadFilled } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
import FormDialog from '@/components/Dialog/FormDialog.vue';
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import FileList from "./fileList.vue";
import {
    ledgerListPage,
    productList,
    customerList,
    addOrUpdateSalesLedger,
    getSalesLedgerWithProducts,
    delLedger,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile, getProductInventory,
  ledgerListPage,
  productList,
  customerList,
  addOrUpdateSalesLedger,
  getSalesLedgerWithProducts,
  delLedger,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile, getProductInventory,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -602,7 +579,9 @@
const data = reactive({
  searchForm: {
    customerName: "", // å®¢æˆ·åç§°
    customerContractNo: "", // å®¢æˆ·åˆåŒç¼–号
    salesContractNo: "", // é”€å”®åˆåŒç¼–号
    projectName: "", // é¡¹ç›®åç§°
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
@@ -610,16 +589,23 @@
  form: {
    salesContractNo: "",
    salesman: "",
    customerContractNo: "",
    customerId: "",
    projectName: "",
    entryPerson: "",
    entryDate: "",
    maintenanceTime: "",
    productData: [],
    executionDate: "",
    paymentMethod: "",
  },
  rules: {
    salesman: [{ required: true, message: "请选择", trigger: "change" }],
    customerContractNo: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    customerId: [{ required: true, message: "请选择", trigger: "change" }],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
    entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    executionDate: [{ required: true, message: "请选择", trigger: "change" }],
@@ -677,16 +663,6 @@
const printPreviewVisible = ref(false);
const printData = ref([]);
// æŠ¥ä»·å•导入相关
const quotationDialogVisible = ref(false);
const quotationLoading = ref(false);
const quotationList = ref([]);
const quotationSearchForm = reactive({
  quotationNo: "",
  customer: "",
});
const selectedQuotation = ref(null);
// å‘货相关
const deliveryFormVisible = ref(false);
const currentDeliveryRow = ref(null);
@@ -702,6 +678,11 @@
    shippingCarNumber: [
      { required: true, message: "请输入发货车牌号", trigger: "blur" }
    ],
    approverId:[
      {
        required: true,message: "",
      }
    ]
  },
});
const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
@@ -768,11 +749,7 @@
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  // åªæœ‰åœ¨ç‚¹å‡»æœç´¢æŒ‰é’®æ—¶æ‰é‡ç½®é¡µç åˆ°ç¬¬ä¸€é¡µ
  // é¿å…è¡¨å•字段change事件干扰分页
  if (arguments.length === 0) {
    page.current = 1;
  }
  page.current = 1;
    expandedRowKeys.value = [];
  getList();
};
@@ -781,14 +758,12 @@
  page.size = obj.limit;
  getList();
};
const getList = () => {
const getList =async () => {
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  // å°†èŒƒå›´æ—¥æœŸå­—段传递给后端
  const params = { ...rest, ...page };
  // ç§»é™¤å½•入日期的默认值设置,只保留范围日期字段
  delete params.entryDate;
  ledgerListPage(params)
  ledgerListPage({ ...rest, ...page })
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.records;
@@ -803,12 +778,8 @@
};
// èŽ·å–äº§å“å¤§ç±»tree数据
const getProductOptions = () => {
  // è¿”回 Promise,便于在编辑产品时等待加载完成
  return productTreeList().then((res) => {
    // å…¼å®¹æŽ¥å£è¿”回 { data: [] } æˆ–直接返回数组
    const list = Array.isArray(res) ? res : (res?.data ?? []);
    productOptions.value = convertIdToValue(list);
    return productOptions.value;
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
const formattedNumber = (row, column, cellValue) => {
@@ -860,19 +831,6 @@
    return newItem;
  });
}
// æ ¹æ®åç§°åæŸ¥äº§å“å¤§ç±» id,便于仅存名称时的反显
function findNodeIdByLabel(nodes, label) {
  if (!label) return null;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    if (node.label === label) return node.value;
    if (node.children && node.children.length > 0) {
      const found = findNodeIdByLabel(node.children, label);
      if (found !== null && found !== undefined) return found;
    }
  }
  return null;
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
@@ -883,23 +841,31 @@
  productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
// å±•开行
const expandChange = (row, expandedRows) => {
  if (expandedRows.length > 0) {
// å±•开行(始终只展开一行)
const expandChange = (row) => {
  const rowKey = row.id;
  const isExpanded = expandedRowKeys.value.includes(rowKey);
  if (isExpanded) {
    // å½“前行已展开 -> æ”¶èµ·
    expandedRowKeys.value = [];
    try {
      productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
        const index = tableData.value.findIndex((item) => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
        expandedRowKeys.value.push(row.id);
      });
    } catch (error) {
      console.log(error);
    }
  } else {
    expandedRowKeys.value = [];
    return;
  }
  // å±•开当前行前,先收起其它行
  expandedRowKeys.value = [];
  try {
    productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
      const index = tableData.value.findIndex((item) => item.id === row.id);
      if (index > -1) {
        tableData.value[index].children = res.data;
      }
      // åªä¿ç•™å½“前这一行处于展开状态
      expandedRowKeys.value = [rowKey];
    });
  } catch (error) {
    console.log(error);
  }
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
@@ -923,19 +889,11 @@
  operationType.value = type;
  form.value = {};
  productData.value = [];
  selectedQuotation.value = null;
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  customerList().then((res) => {
    customerOption.value = res;
  });
  form.value.entryPerson = userStore.id;
  if (type === "add") {
    // æ–°å¢žæ—¶è®¾ç½®å½•入日期为当天
    form.value.entryDate = getCurrentDate();
    // ç­¾è®¢æ—¥æœŸé»˜è®¤ä¸ºå½“天
    form.value.executionDate = getCurrentDate();
  } else {
  if (type !== "add") {
    currentId.value = row.id;
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
      form.value = { ...res };
@@ -952,87 +910,6 @@
  // });
  form.value.entryDate = getCurrentDate(); // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  dialogFormVisible.value = true;
};
// æ‰“开报价单选择弹窗(仅审批通过)
const openQuotationDialog = async () => {
  if (operationType.value === "view") return;
  quotationDialogVisible.value = true;
  // å…ˆç¡®ä¿å®¢æˆ·åˆ—表已加载,便于后续回填 customerId
  if (!customerOption.value || customerOption.value.length === 0) {
    try {
      const res = await customerList();
      customerOption.value = res;
    } catch (e) {
      // ignore,允许用户后续手动选择客户
    }
  }
  await fetchQuotationList();
};
const fetchQuotationList = async () => {
  quotationLoading.value = true;
  try {
    const params = {
      // å…¼å®¹åŽç«¯åˆ†é¡µå­—段:这里沿用报价页面已有可用的字段命名
      currentPage: 1,
      pageSize: 100,
      ...quotationSearchForm,
      status: "通过",
    };
    const res = await getQuotationList(params);
    quotationList.value = res?.data?.records || [];
  } finally {
    quotationLoading.value = false;
  }
};
const resetQuotationSearch = async () => {
  quotationSearchForm.quotationNo = "";
  quotationSearchForm.customer = "";
  await fetchQuotationList();
};
// é€‰ä¸­æŠ¥ä»·å•后回填到台账表单
const applyQuotation = (row) => {
  if (!row) return;
  selectedQuotation.value = row;
  // ä¸šåŠ¡å‘˜
  form.value.salesman = row.salesperson || "";
  // å®¢æˆ·åç§° -> customerId
  const customer = (customerOption.value || []).find((c) => c.customerName === row.customer);
  if (customer?.id) {
    form.value.customerId = customer.id;
  } else {
    // å¦‚果找不到,保留原值(允许用户手动选择/不打断已有输入)
    form.value.customerId = form.value.customerId || "";
  }
  // äº§å“ä¿¡æ¯æ˜ å°„:报价 products -> å°è´¦ productData
  const products = Array.isArray(row.products) ? row.products : [];
  productData.value = products.map((p) => {
    const quantity = Number(p.quantity ?? 0) || 0;
    const unitPrice = Number(p.unitPrice ?? 0) || 0;
    const taxRate = "13"; // é»˜è®¤ 13%,便于直接提交(如需可在产品中自行修改)
    const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
    return {
      // å°è´¦å­—段
      productCategory: p.product || p.productName || "",
      specificationModel: p.specification || "",
      unit: p.unit || "",
      quantity: quantity,
      taxRate: taxRate,
      taxInclusiveUnitPrice: unitPrice.toFixed(2),
      taxInclusiveTotalPrice: taxInclusiveTotalPrice,
      taxExclusiveTotalPrice: taxExclusiveTotalPrice,
      invoiceType: "增普票",
    };
  });
  quotationDialogVisible.value = false;
};
function changs(val) {
  console.log(val);
@@ -1106,7 +983,7 @@
const productIndex = ref(0);
// æ‰“开产品弹框
const openProductForm = async (type, row, index) => {
const openProductForm =async (type, row,index) => {
  productOperationType.value = type;
  productForm.value = {};
  proxy.resetForm("productFormRef");
@@ -1119,29 +996,9 @@
  if (type === "edit") {
    productForm.value = { ...row };
    productIndex.value = index;
    // ç¼–辑时根据产品大类名称反查 tree èŠ‚ç‚¹ id,并加载规格型号列表
    try {
      const options = productOptions.value && productOptions.value.length > 0
        ? productOptions.value
        : await getProductOptions();
      const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
      if (categoryId) {
        const models = await modelList({ id: categoryId });
        modelOptions.value = models || [];
        // æ ¹æ®å½“前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
        const currentModel = (modelOptions.value || []).find(
          (m) => m.model === productForm.value.specificationModel
        );
        if (currentModel) {
          productForm.value.productModelId = currentModel.id;
        }
      }
    } catch (e) {
      // åŠ è½½å¤±è´¥æ—¶ä¿æŒå¯ç¼–è¾‘ï¼Œä¸ä¸­æ–­å¼¹çª—
      console.error("加载产品规格型号失败", e);
    }
  }
  productFormVisible.value = true;
  getProductOptions();
};
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
@@ -1597,6 +1454,15 @@
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// è®¡ç®—产品总数量
const getTotalQuantity = (products) => {
  if (!products || products.length === 0) return '0';
@@ -1809,28 +1675,20 @@
 * @param row ä¸‹è½½æ–‡ä»¶çš„相关信息对象
 */
const fileListRef = ref(null)
const fileListDialogVisible = ref(false)
const downLoadFile = (row) => {
  getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
    if (fileListRef.value) {
      fileListRef.value.open(res.salesLedgerFiles)
      fileListDialogVisible.value = true
    }
    fileListRef.value.open(res.salesLedgerFiles)
  });
}
// æ‰“开发货弹框
const openDeliveryForm = (row) => {
  getProductInventory({ salesLedgerId: row.id, type:1 }).then((res) => {
    currentDeliveryRow.value = row;
    deliveryForm.value = {
      shippingDate: getCurrentDate(),
      shippingCarNumber: "",
    };
    deliveryFormVisible.value = true;
  }).catch(err => {
    ElMessage.error(err.msg);
  });
  currentDeliveryRow.value = row;
  deliveryForm.value = {
    shippingDate: "", // ç§»é™¤é»˜è®¤å€¼è®¾ç½®
    shippingCarNumber: "",
  };
  deliveryFormVisible.value = true;
};
// æäº¤å‘货表单
@@ -1838,7 +1696,9 @@
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    if (valid) {
      addShippingInfo({
        salesLedgerId: currentDeliveryRow.value.id,
        approverId:deliveryForm.value.approverId,
        salesLedgerId: currentDeliveryRow.value.salesLedgerId,
        salesLedgerProductId: currentDeliveryRow.value.id,
        shippingDate: deliveryForm.value.shippingDate,
        shippingCarNumber: deliveryForm.value.shippingCarNumber,
      })
@@ -1846,6 +1706,7 @@
          proxy.$modal.msgSuccess("发货成功");
          closeDeliveryDia();
          getList();
          expandedRowKeys.value = [];
        })
        .catch(() => {
          proxy.$modal.msgError("发货失败,请重试");