| src/api/productionManagement/productionCosting.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionCosting/components/CostConfigDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionCosting/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/productionManagement/productionCosting.js
@@ -1,49 +1,154 @@ // çäº§æ ¸ç®é¡µé¢æ¥å£ import request from "@/utils/request"; // å页æ¥è¯¢ export function productionAccountingListPage(query) { /* ===================== æ ¸ç®æ¥è¯¢ ===================== */ // æåå·¥æ±æ»ï¼äº§éãææ¶é´å计ãæååæ°ï¼ export function costingSummary(query) { return request({ url: "/salesLedger/productionAccounting/listPage", url: "/productionCosting/summary", method: "get", params: query, }); } // å·¦è¾¹è¡¨æ ¼çæ¥å£ // salesLedger/productionAccounting/page export function salesLedgerProductionAccountingList(query) { // æç»ï¼ç½çï¼å·¥åº+åå·ï¼æåï¼å·¥åºï¼ export function costingDetail(query) { return request({ url: "/productionAccount/listPage", url: "/productionCosting/detail", method: "get", params: query, }); } // å³è¾¹è¡¨æ ¼çæ¥å£ // export function salesLedgerProductionAccountingListProductionDetails(query) { // æä»½å·¥ä½æ¥ä¿¡æ¯ export function costingWorkday(yearMonth) { return request({ url: "/productionAccount/listProductionDetails", url: "/productionCosting/workday", method: "get", params: query, params: { yearMonth }, }); } // æ¹éæ ¸ç® export function productionAccountingBatchAccounting(data) { // å¯¼åºæç» export function costingExport(query) { return request({ url: "/productionAccount/batchAccounting", url: "/productionCosting/export", method: "post", data, params: query, responseType: "blob", }); } // åä¸ªä¿®æ£ export function productionAccountingSingleAdjust(data) { /* ===================== é¨é¨ç±»åé ç½® ===================== */ export function costingDeptList() { return request({ url: "/productionCosting/dept/list", method: "get" }); } export function costingDeptAdd(data) { return request({ url: "/productionCosting/dept", method: "post", data }); } export function costingDeptUpdate(data) { return request({ url: "/productionCosting/dept", method: "put", data }); } export function costingDeptDel(id) { return request({ url: "/productionCosting/dept/" + id, method: "delete" }); } /* ===================== æ åæ°éé ç½® ===================== */ export function costingStandardList(query) { return request({ url: "/productionAccount/singleAdjust", method: "post", data, url: "/productionCosting/standard/list", method: "get", params: query, }); } export function costingStandardAdd(data) { return request({ url: "/productionCosting/standard", method: "post", data }); } export function costingStandardUpdate(data) { return request({ url: "/productionCosting/standard", method: "put", data }); } export function costingStandardDel(id) { return request({ url: "/productionCosting/standard/" + id, method: "delete" }); } /* ===================== æç®çé ç½® ===================== */ export function costingFactorList(query) { return request({ url: "/productionCosting/factor/list", method: "get", params: query, }); } export function costingFactorAdd(data) { return request({ url: "/productionCosting/factor", method: "post", data }); } export function costingFactorUpdate(data) { return request({ url: "/productionCosting/factor", method: "put", data }); } export function costingFactorDel(id) { return request({ url: "/productionCosting/factor/" + id, method: "delete" }); } /* ===================== æåº¦å·¥ä½æ¥é ç½® ===================== */ export function costingWorkdayList(query) { return request({ url: "/productionCosting/workdayConfig/list", method: "get", params: query, }); } export function costingWorkdayAdd(data) { return request({ url: "/productionCosting/workdayConfig", method: "post", data }); } export function costingWorkdayUpdate(data) { return request({ url: "/productionCosting/workdayConfig", method: "put", data }); } export function costingWorkdayDel(id) { return request({ url: "/productionCosting/workdayConfig/" + id, method: "delete" }); } /* ===================== ä¸ææ°æ®æº ===================== */ // å·¥åºä¸æ export function costingProcessOptions(query) { return request({ url: "/technologyOperation/listPage", method: "get", params: { current: 1, size: 1000, ...query }, }); } // åå·ä¸æ export function costingModelOptions(query) { return request({ url: "/basic/product/pageModel", method: "get", params: { current: 1, size: 1000, ...query }, }); } // é¨é¨ä¸æ export function costingDeptOptions(query) { return request({ url: "/system/dept/list", method: "get", params: query, }); } src/views/productionManagement/productionCosting/components/CostConfigDialog.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,414 @@ <template> <el-dialog v-model="visible" title="çäº§æ ¸ç®é ç½®" width="900px" append-to-body @close="handleClose" > <el-tabs v-model="activeTab"> <!-- é¨é¨ç±»åé ç½® --> <el-tab-pane label="é¨é¨ç±»å" name="dept"> <div class="tab-toolbar"> <el-button type="primary" size="small" @click="openDeptForm()">æ°å¢</el-button> </div> <el-table :data="deptList" border size="small" max-height="420"> <el-table-column label="é¨é¨" prop="deptName" min-width="140" /> <el-table-column label="ç±»å" min-width="110"> <template #default="{ row }">{{ deptTypeText(row.deptType) }}</template> </el-table-column> <el-table-column label="夿³¨" prop="remark" min-width="160" /> <el-table-column label="æä½" width="140" fixed="right"> <template #default="{ row }"> <el-button link type="primary" @click="openDeptForm(row)">ç¼è¾</el-button> <el-button link type="danger" @click="removeDept(row)">å é¤</el-button> </template> </el-table-column> </el-table> </el-tab-pane> <!-- æ åæ°éé ç½® --> <el-tab-pane label="æ åæ°é" name="standard"> <div class="tab-toolbar"> <el-select v-model="standardQuery.deptType" placeholder="é¨é¨ç±»å" clearable size="small" style="width: 140px" @change="loadStandard"> <el-option label="æåé¨é¨" :value="1" /> <el-option label="ç½çé¨é¨" :value="2" /> </el-select> <el-button type="primary" size="small" @click="openStandardForm()">æ°å¢</el-button> </div> <el-table :data="standardList" border size="small" max-height="400"> <el-table-column label="é¨é¨ç±»å" min-width="100"> <template #default="{ row }">{{ deptTypeText(row.deptType) }}</template> </el-table-column> <el-table-column label="å·¥åº" prop="processName" min-width="110" /> <el-table-column label="åå·" prop="modelName" min-width="140" /> <el-table-column label="æ åæ°é" prop="standardQuantity" min-width="100" /> <el-table-column label="åä½" prop="unit" min-width="80" /> <el-table-column label="æä½" width="140" fixed="right"> <template #default="{ row }"> <el-button link type="primary" @click="openStandardForm(row)">ç¼è¾</el-button> <el-button link type="danger" @click="removeStandard(row)">å é¤</el-button> </template> </el-table-column> </el-table> </el-tab-pane> <!-- æç®çé ç½® --> <el-tab-pane label="æç®ç" name="factor"> <div class="tab-toolbar"> <el-select v-model="factorQuery.processId" placeholder="å·¥åº" clearable filterable size="small" style="width: 180px" @change="loadFactor"> <el-option v-for="p in processOptions" :key="p.id" :label="p.name" :value="p.id" /> </el-select> <el-button type="primary" size="small" @click="openFactorForm()">æ°å¢</el-button> </div> <el-table :data="factorList" border size="small" max-height="400"> <el-table-column label="å·¥åº" prop="processName" min-width="110" /> <el-table-column label="åå·¥" prop="staffName" min-width="110" /> <el-table-column label="å°æç´æç®ç" prop="violinFactor" min-width="110" /> <el-table-column label="大æç´æç®ç" prop="violaFactor" min-width="110" /> <el-table-column label="è´æ¯æç®ç" prop="bassFactor" min-width="100" /> <el-table-column label="æä½" width="140" fixed="right"> <template #default="{ row }"> <el-button link type="primary" @click="openFactorForm(row)">ç¼è¾</el-button> <el-button link type="danger" @click="removeFactor(row)">å é¤</el-button> </template> </el-table-column> </el-table> </el-tab-pane> <!-- æåº¦å·¥ä½æ¥é ç½® --> <el-tab-pane label="æåº¦å·¥ä½æ¥" name="workday"> <div class="tab-toolbar"> <el-button type="primary" size="small" @click="openWorkdayForm()">æ°å¢</el-button> <span class="tip">æªé ç½®çæä»½é»è®¤æâå¨ä¸~å¨äºâ计ç®</span> </div> <el-table :data="workdayList" border size="small" max-height="400"> <el-table-column label="å¹´æ" prop="yearMonth" min-width="110" /> <el-table-column label="工使¥(天)" prop="workDays" min-width="100" /> <el-table-column label="满å¤å·¥æ¶(å°æ¶)" min-width="120"> <template #default="{ row }">{{ (row.workDays || 0) * 8 }}</template> </el-table-column> <el-table-column label="夿³¨" prop="remark" min-width="160" /> <el-table-column label="æä½" width="140" fixed="right"> <template #default="{ row }"> <el-button link type="primary" @click="openWorkdayForm(row)">ç¼è¾</el-button> <el-button link type="danger" @click="removeWorkday(row)">å é¤</el-button> </template> </el-table-column> </el-table> </el-tab-pane> </el-tabs> <!-- é¨é¨ç±»å表å --> <el-dialog v-model="deptFormVisible" :title="deptForm.id ? 'ç¼è¾é¨é¨ç±»å' : 'æ°å¢é¨é¨ç±»å'" width="420px" append-to-body> <el-form :model="deptForm" label-width="90px"> <el-form-item label="é¨é¨" required> <el-select v-model="deptForm.deptId" placeholder="è¯·éæ©é¨é¨" filterable style="width: 100%"> <el-option v-for="d in deptOptions" :key="d.deptId" :label="d.deptName" :value="d.deptId" /> </el-select> </el-form-item> <el-form-item label="ç±»å" required> <el-select v-model="deptForm.deptType" placeholder="è¯·éæ©ç±»å" style="width: 100%"> <el-option label="æåé¨é¨" :value="1" /> <el-option label="ç½çé¨é¨" :value="2" /> </el-select> </el-form-item> <el-form-item label="夿³¨"> <el-input v-model="deptForm.remark" /> </el-form-item> </el-form> <template #footer> <el-button @click="deptFormVisible = false">åæ¶</el-button> <el-button type="primary" @click="submitDept">确认</el-button> </template> </el-dialog> <!-- æ åæ°é表å --> <el-dialog v-model="standardFormVisible" :title="standardForm.id ? 'ç¼è¾æ åæ°é' : 'æ°å¢æ åæ°é'" width="460px" append-to-body> <el-form :model="standardForm" label-width="90px"> <el-form-item label="é¨é¨ç±»å" required> <el-select v-model="standardForm.deptType" placeholder="è¯·éæ©" style="width: 100%"> <el-option label="æåé¨é¨" :value="1" /> <el-option label="ç½çé¨é¨" :value="2" /> </el-select> </el-form-item> <el-form-item label="å·¥åº" required> <el-select v-model="standardForm.processId" placeholder="è¯·éæ©å·¥åº" filterable style="width: 100%"> <el-option v-for="p in processOptions" :key="p.id" :label="p.name" :value="p.id" /> </el-select> </el-form-item> <el-form-item label="åå·" :required="standardForm.deptType === 2"> <el-select v-model="standardForm.productModelId" placeholder="ç½çå¿ å¡«" filterable clearable style="width: 100%"> <el-option v-for="m in modelOptions" :key="m.id" :label="m.model + 'ï¼' + (m.productName || '') + 'ï¼'" :value="m.id" /> </el-select> </el-form-item> <el-form-item label="æ åæ°é" required> <el-input-number v-model="standardForm.standardQuantity" :min="0" :precision="4" style="width: 100%" /> </el-form-item> <el-form-item label="åä½"> <el-input v-model="standardForm.unit" /> </el-form-item> <el-form-item label="夿³¨"> <el-input v-model="standardForm.remark" /> </el-form-item> </el-form> <template #footer> <el-button @click="standardFormVisible = false">åæ¶</el-button> <el-button type="primary" @click="submitStandard">确认</el-button> </template> </el-dialog> <!-- æç®ç表å --> <el-dialog v-model="factorFormVisible" :title="factorForm.id ? 'ç¼è¾æç®ç' : 'æ°å¢æç®ç'" width="460px" append-to-body> <el-form :model="factorForm" label-width="110px"> <el-form-item label="å·¥åº" required> <el-select v-model="factorForm.processId" placeholder="è¯·éæ©å·¥åº" filterable style="width: 100%"> <el-option v-for="p in processOptions" :key="p.id" :label="p.name" :value="p.id" /> </el-select> </el-form-item> <el-form-item label="åå·¥" required> <el-select v-model="factorForm.staffId" placeholder="è¯·éæ©åå·¥" filterable style="width: 100%" @change="onStaffChange"> <el-option v-for="u in staffOptions" :key="u.userId" :label="u.nickName || u.userName" :value="u.userId" /> </el-select> </el-form-item> <el-form-item label="å°æç´æç®ç"> <el-input-number v-model="factorForm.violinFactor" :min="0" :precision="4" style="width: 100%" /> </el-form-item> <el-form-item label="大æç´æç®ç"> <el-input-number v-model="factorForm.violaFactor" :min="0" :precision="4" style="width: 100%" /> </el-form-item> <el-form-item label="è´æ¯æç®ç"> <el-input-number v-model="factorForm.bassFactor" :min="0" :precision="4" style="width: 100%" /> </el-form-item> <el-form-item label="夿³¨"> <el-input v-model="factorForm.remark" /> </el-form-item> </el-form> <template #footer> <el-button @click="factorFormVisible = false">åæ¶</el-button> <el-button type="primary" @click="submitFactor">确认</el-button> </template> </el-dialog> <!-- æåº¦å·¥ä½æ¥è¡¨å --> <el-dialog v-model="workdayFormVisible" :title="workdayForm.id ? 'ç¼è¾æåº¦å·¥ä½æ¥' : 'æ°å¢æåº¦å·¥ä½æ¥'" width="420px" append-to-body> <el-form :model="workdayForm" label-width="110px"> <el-form-item label="å¹´æ" required> <el-date-picker v-model="workdayForm.yearMonth" type="month" value-format="YYYY-MM" placeholder="è¯·éæ©å¹´æ" style="width: 100%" /> </el-form-item> <el-form-item label="工使¥(天)" required> <el-input-number v-model="workdayForm.workDays" :min="0" :max="31" :precision="0" style="width: 100%" /> </el-form-item> <el-form-item label="夿³¨"> <el-input v-model="workdayForm.remark" /> </el-form-item> </el-form> <template #footer> <el-button @click="workdayFormVisible = false">åæ¶</el-button> <el-button type="primary" @click="submitWorkday">确认</el-button> </template> </el-dialog> </el-dialog> </template> <script setup> import { ref, reactive, getCurrentInstance } from "vue"; import request from "@/utils/request"; import { costingDeptList, costingDeptAdd, costingDeptUpdate, costingDeptDel, costingStandardList, costingStandardAdd, costingStandardUpdate, costingStandardDel, costingFactorList, costingFactorAdd, costingFactorUpdate, costingFactorDel, costingWorkdayList, costingWorkdayAdd, costingWorkdayUpdate, costingWorkdayDel, costingProcessOptions, costingModelOptions, costingDeptOptions, } from "@/api/productionManagement/productionCosting.js"; const { proxy } = getCurrentInstance(); const emit = defineEmits(["close"]); const visible = ref(false); const activeTab = ref("dept"); const deptList = ref([]); const standardList = ref([]); const factorList = ref([]); const workdayList = ref([]); const processOptions = ref([]); const modelOptions = ref([]); const deptOptions = ref([]); const staffOptions = ref([]); const standardQuery = reactive({ deptType: undefined }); const factorQuery = reactive({ processId: undefined }); const deptTypeText = v => (v === 1 ? "æåé¨é¨" : v === 2 ? "ç½çé¨é¨" : "-"); /* ---------- å è½½ ---------- */ const loadDept = () => costingDeptList().then(res => (deptList.value = res.data || [])); const loadStandard = () => costingStandardList({ ...standardQuery }).then(res => (standardList.value = res.data || [])); const loadFactor = () => costingFactorList({ ...factorQuery }).then(res => (factorList.value = res.data || [])); const loadWorkday = () => costingWorkdayList().then(res => (workdayList.value = res.data || [])); const loadOptions = () => { costingProcessOptions().then(res => { processOptions.value = res?.data?.records || []; }); costingModelOptions().then(res => { modelOptions.value = res?.records || []; }); costingDeptOptions().then(res => { deptOptions.value = res?.data || []; }); request({ url: "/system/user/list", method: "get", params: { pageNum: 1, pageSize: 1000 } }).then(res => { staffOptions.value = res?.rows || []; }); }; const openDialog = () => { visible.value = true; loadOptions(); loadDept(); loadStandard(); loadFactor(); loadWorkday(); }; const handleClose = () => emit("close"); /* ---------- é¨é¨ç±»å ---------- */ const deptFormVisible = ref(false); const deptForm = reactive({ id: null, deptId: null, deptType: 1, remark: "" }); const openDeptForm = row => { Object.assign(deptForm, { id: null, deptId: null, deptType: 1, remark: "" }, row || {}); deptFormVisible.value = true; }; const submitDept = () => { if (!deptForm.deptId || !deptForm.deptType) return proxy.$modal.msgWarning("请填åé¨é¨ä¸ç±»å"); const fn = deptForm.id ? costingDeptUpdate : costingDeptAdd; fn({ ...deptForm }).then(() => { proxy.$modal.msgSuccess("ä¿åæå"); deptFormVisible.value = false; loadDept(); }); }; const removeDept = row => { proxy.$modal.confirm("确认å é¤è¯¥é ç½®åï¼").then(() => costingDeptDel(row.id)).then(() => { proxy.$modal.msgSuccess("å 餿å"); loadDept(); }).catch(() => {}); }; /* ---------- æ åæ°é ---------- */ const standardFormVisible = ref(false); const standardForm = reactive({ id: null, deptType: 1, processId: null, productModelId: null, standardQuantity: 0, unit: "", remark: "", }); const openStandardForm = row => { Object.assign(standardForm, { id: null, deptType: standardQuery.deptType || 1, processId: null, productModelId: null, standardQuantity: 0, unit: "", remark: "", }, row || {}); standardFormVisible.value = true; }; const submitStandard = () => { if (!standardForm.deptType || !standardForm.processId) return proxy.$modal.msgWarning("请填åé¨é¨ç±»åä¸å·¥åº"); if (standardForm.deptType === 2 && !standardForm.productModelId) return proxy.$modal.msgWarning("ç½çé¨é¨å¿ 须鿩åå·"); const fn = standardForm.id ? costingStandardUpdate : costingStandardAdd; fn({ ...standardForm }).then(() => { proxy.$modal.msgSuccess("ä¿åæå"); standardFormVisible.value = false; loadStandard(); }); }; const removeStandard = row => { proxy.$modal.confirm("确认å é¤è¯¥é ç½®åï¼").then(() => costingStandardDel(row.id)).then(() => { proxy.$modal.msgSuccess("å 餿å"); loadStandard(); }).catch(() => {}); }; /* ---------- æç®ç ---------- */ const factorFormVisible = ref(false); const factorForm = reactive({ id: null, processId: null, staffId: null, staffName: "", violinFactor: 1, violaFactor: 1, bassFactor: 1, remark: "", }); const openFactorForm = row => { Object.assign(factorForm, { id: null, processId: factorQuery.processId || null, staffId: null, staffName: "", violinFactor: 1, violaFactor: 1, bassFactor: 1, remark: "", }, row || {}); factorFormVisible.value = true; }; const onStaffChange = id => { const u = staffOptions.value.find(s => s.userId === id); factorForm.staffName = u ? u.nickName || u.userName : ""; }; const submitFactor = () => { if (!factorForm.processId || !factorForm.staffId) return proxy.$modal.msgWarning("请填åå·¥åºä¸åå·¥"); const fn = factorForm.id ? costingFactorUpdate : costingFactorAdd; fn({ ...factorForm }).then(() => { proxy.$modal.msgSuccess("ä¿åæå"); factorFormVisible.value = false; loadFactor(); }); }; const removeFactor = row => { proxy.$modal.confirm("确认å é¤è¯¥é ç½®åï¼").then(() => costingFactorDel(row.id)).then(() => { proxy.$modal.msgSuccess("å 餿å"); loadFactor(); }).catch(() => {}); }; /* ---------- æåº¦å·¥ä½æ¥ ---------- */ const workdayFormVisible = ref(false); const workdayForm = reactive({ id: null, yearMonth: "", workDays: 22, remark: "" }); const openWorkdayForm = row => { Object.assign(workdayForm, { id: null, yearMonth: "", workDays: 22, remark: "" }, row || {}); workdayFormVisible.value = true; }; const submitWorkday = () => { if (!workdayForm.yearMonth) return proxy.$modal.msgWarning("è¯·éæ©å¹´æ"); if (workdayForm.workDays == null) return proxy.$modal.msgWarning("请填å工使¥å¤©æ°"); const fn = workdayForm.id ? costingWorkdayUpdate : costingWorkdayAdd; fn({ ...workdayForm }).then(() => { proxy.$modal.msgSuccess("ä¿åæå"); workdayFormVisible.value = false; loadWorkday(); }); }; const removeWorkday = row => { proxy.$modal.confirm("确认å é¤è¯¥é ç½®åï¼").then(() => costingWorkdayDel(row.id)).then(() => { proxy.$modal.msgSuccess("å 餿å"); loadWorkday(); }).catch(() => {}); }; defineExpose({ openDialog }); </script> <style scoped> .tab-toolbar { display: flex; gap: 10px; margin-bottom: 10px; } .tip { color: #909399; font-size: 12px; line-height: 32px; } </style> src/views/productionManagement/productionCosting/index.vue
@@ -1,579 +1,247 @@ <template> <div class="app-container"> <div class="table_list"> <el-row :gutter="16" class="content-row"> <!-- 左侧å°è´¦ + é¡¶é¨çé --> <el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8" class="left-col"> <div class="left-panel"> <el-alert title="å·¥èµæ ¸ç®å£å¾ï¼ä» ç»è®¡å®¡æ ¸éè¿çæ¥å·¥æ°æ®ã" type="info" :closable="false" show-icon /> <div class="left-header"> <el-form :model="searchForm" inline> <el-form-item label="æ¥å·¥äººåï¼"> <el-input v-model="searchForm.schedulingUserName" placeholder="请è¾å ¥" clearable style="width: 160px" @change="reloadData" /> </el-form-item> <el-form-item label="å·¥åç¼å·ï¼"> <el-input v-model="searchForm.workOrderNo" placeholder="请è¾å ¥" clearable style="width: 160px" @change="reloadData" /> </el-form-item> <el-form-item prop="dateType"> <el-radio-group v-model="searchForm.dateType" size="small" @change="handleDateTypeChange"> <el-radio-button label="day">æ¥</el-radio-button> <el-radio-button label="month">æ</el-radio-button> </el-radio-group> </el-form-item> <el-form-item label="æ¥æï¼" prop="dateRange"> <el-date-picker v-model="searchForm.dateRange" :type="searchForm.dateType === 'day' ? 'date' : 'daterange'" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 200px" @change="handleDateRangeChange" /> </el-form-item> </el-form> </div> <PIMTable rowKey="id" :column="leftTableColumn" :tableData="leftTableData" :tableLoading="tableLoading" :page="page" @row-click="handleLeftRowClick" @pagination="pagination"></PIMTable> </div> </el-col> <!-- å³ä¾§æç» --> <el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16" class="right-col"> <div class="right-panel"> <div class="right-toolbar"> <el-button type="primary" @click="handleBatchAccounting">æ¹éæ ¸ç®</el-button> </div> <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page1" :isSelection="true" @selection-change="handleSelectionChange" :tableLoading="tableLoading1" style="margin-right: 20px;" @pagination="pagination1"></PIMTable> </div> </el-col> </el-row> </div> <el-dialog v-model="adjustDialogVisible" title="å个修æ£" width="520px"> <el-form :model="adjustForm" label-width="120px"> <el-form-item label="æ¥å·¥åå·"> <el-input v-model="adjustForm.reportNo" disabled /> <!-- é¡¶é¨çé + ä¿¡æ¯ --> <el-card shadow="never" class="filter-card"> <el-form :model="searchForm" inline> <el-form-item label="é¨é¨ï¼"> <el-radio-group v-model="searchForm.deptType" @change="handleQuery"> <el-radio-button :label="1">æåé¨é¨</el-radio-button> <el-radio-button :label="2">ç½çé¨é¨</el-radio-button> </el-radio-group> </el-form-item> <el-form-item label="æ¥å·¥äººå"> <el-input v-model="adjustForm.schedulingUserName" disabled /> <el-form-item label="æä»½ï¼"> <el-date-picker v-model="searchForm.yearMonth" type="month" value-format="YYYY-MM" placeholder="è¯·éæ©æä»½" style="width: 160px" @change="handleQuery" /> </el-form-item> <el-form-item label="ä¿®æ£éé¢"> <el-input-number v-model="adjustForm.wages" :min="0" :precision="2" style="width: 100%" /> <el-form-item label="ç产人ï¼"> <el-input v-model="searchForm.staffName" placeholder="请è¾å ¥" clearable style="width: 160px" @keyup.enter="handleQuery" @clear="handleQuery" /> </el-form-item> <el-form-item label="ä¿®æ£è¯´æ"> <el-input v-model="adjustForm.remark" type="textarea" :rows="3" /> <el-form-item> <el-button type="primary" icon="Search" @click="handleQuery">æç´¢</el-button> <el-button icon="Setting" @click="openConfig">é ç½®</el-button> <el-button icon="Download" @click="handleExport">导åº</el-button> </el-form-item> </el-form> <template #footer> <el-button @click="adjustDialogVisible = false">åæ¶</el-button> <el-button type="primary" @click="confirmAdjust">确认</el-button> </template> </el-dialog> <el-alert :title="infoText" type="info" :closable="false" show-icon /> </el-card> <el-row :gutter="16" class="content-row"> <!-- å·¦ï¼æåå·¥æ±æ» --> <el-col :xs="24" :sm="24" :md="24" :lg="9" :xl="9"> <div class="panel-title">æ±æ»å°è´¦ï¼{{ searchForm.deptType === 1 ? 'æåé¨é¨' : 'ç½çé¨é¨' }}ï¼</div> <PIMTable rowKey="staffId" :column="summaryColumns" :tableData="summaryData" :tableLoading="loading" :isShowPagination="false" :highlightCurrentRow="true" @row-click="handleSummaryRowClick" /> </el-col> <!-- å³ï¼æç» --> <el-col :xs="24" :sm="24" :md="24" :lg="15" :xl="15"> <div class="panel-title"> æç»{{ detailStaffName ? 'ï¼' + detailStaffName + 'ï¼' : '' }} </div> <PIMTable rowKey="rowKey" :column="detailColumns" :tableData="detailData" :tableLoading="loadingDetail" :isShowPagination="false" /> </el-col> </el-row> <!-- é ç½®å¼¹çª --> <CostConfigDialog ref="configDialog" @close="handleQuery" /> </div> </template> <script setup> import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue"; import { ElMessageBox } from "element-plus"; import dayjs from "dayjs"; import { onMounted, ref, reactive, computed, getCurrentInstance } from "vue"; import CostConfigDialog from "./components/CostConfigDialog.vue"; import { salesLedgerProductionAccountingListProductionDetails, salesLedgerProductionAccountingList, productionAccountingBatchAccounting, productionAccountingSingleAdjust, costingSummary, costingDetail, costingWorkday, } from "@/api/productionManagement/productionCosting.js"; const { proxy } = getCurrentInstance(); const formatTag = (val, map) => { const item = map[String(val)]; if (!item) return "-"; return item.label; }; const tableColumn = ref([ { label: "æ¥å·¥åå·", prop: "reportNo", minWidth: 140, }, { label: "çäº§æ¥æ", prop: "schedulingDate", minWidth: 100, }, { label: "ç产人", prop: "schedulingUserName", minWidth: 100, }, { label: "å®¡æ ¸ç¶æ", prop: "auditStatus", minWidth: 100, dataType: "tag", formatData: val => formatTag(val, { 0: { label: "å¾ å®¡æ ¸" }, 1: { label: "å®¡æ ¸éè¿" }, 2: { label: "å®¡æ ¸ä¸éè¿" }, }), formatType: val => { const typeMap = { 0: "warning", 1: "success", 2: "danger", }; return typeMap[String(val)] || "info"; }, }, { label: "æ¥å·¥ç±»å", prop: "reportType", minWidth: 100, dataType: "tag", formatData: val => formatTag(val, { 0: { label: "åæ ¼" }, 1: { label: "轻微è¿å·¥" }, 2: { label: "严éè¿å·¥" }, 3: { label: "æ¥åº" }, }), formatType: val => { const typeMap = { 0: "success", 1: "warning", 2: "danger", 3: "info", }; return typeMap[String(val)] || "info"; }, }, { label: "éå®ååå·", prop: "salesContractNo", minWidth: 140, }, { label: "产å大类", prop: "productName", minWidth: 100, }, { label: "äº§åæ åæ°é", prop: "productStandardQuantity", minWidth: 120, }, { label: "è§æ ¼åå·", prop: "productModelName", minWidth: 100, }, { label: "åä½", prop: "unit", minWidth: 100, }, { label: "å·¥åº", prop: "process", minWidth: 100, }, { label: "æ åæ°é", prop: "standardQuantity", minWidth: 100, }, { label: "å·¥åç¼å·", prop: "workOrderNo", minWidth: 140, }, { label: "补修çå·¥æ¶", prop: "repairWorkHour", minWidth: 120, }, { label: "ç产æ°é", prop: "quantity", minWidth: 100, }, { label: "æç®å·¥æ¶", prop: "convertedWorkHours", minWidth: 100, }, ]); // å·¦ä¾§æ±æ»å°è´¦åï¼ç产人ã产éãå·¥èµãåæ ¼çï¼ const leftTableColumn = ref([ { label: "ç产人", prop: "schedulingUserName", minWidth: 100, }, { label: "产é", prop: "finishedNum", minWidth: 100, }, { label: "æç®å·¥æ¶å计", prop: "workHours", minWidth: 100, }, { label: "åæ ¼ç", prop: "outputRate", minWidth: 100, formatData: val => { if (val == null || val === "") return "-"; return parseFloat(val).toFixed(2) + "%"; }, }, ]); const tableData = ref([]); const tableLoading = ref(false); const tableLoading1 = ref(false); const leftTableData = ref([]); const selectedRows = ref([]); const adjustDialogVisible = ref(false); const adjustForm = reactive({ id: null, reportNo: "", schedulingUserName: "", wages: 0, remark: "", }); // æ¥ / æ 忢ï¼é»è®¤ææ¥ï¼ const page = reactive({ current: 1, size: 100, total: 0, }); const page1 = reactive({ current: 1, size: 100, total: 0, }); const data = reactive({ searchForm: { schedulingUserName: "", workOrderNo: "", salesContractNo: "", dateType: "day", dateRange: dayjs().format("YYYY-MM-DD"), entryDate: dayjs().format("YYYY-MM-DD"), entryDateStart: undefined, entryDateEnd: undefined, deptType: 1, yearMonth: "", staffName: "", }, }); const { searchForm } = toRefs(data); const { searchForm } = data; const pagination = obj => { page.current = obj.page; page.size = obj.limit; getList(); }; const summaryData = ref([]); const detailData = ref([]); const loading = ref(false); const loadingDetail = ref(false); const detailStaffName = ref(""); const workInfo = reactive({ workDays: 0, dayHours: 8, monthHours: 0 }); const pagination1 = obj => { page1.current = obj.page; page1.size = obj.limit; getList1(); }; const configDialog = ref(); const handleSelectionChange = selection => { selectedRows.value = selection || []; }; const infoText = computed( () => `彿工使¥ ${workInfo.workDays} 天 · æ¯æ¥ ${workInfo.dayHours} å°æ¶ · 满å¤å·¥æ¶ ${workInfo.monthHours} å°æ¶ï¼æååæ° = ææ¶é´å计 ÷ (工使¥ à 8)ï¼` ); const handleDateRangeChange = value => { if (value) { if (searchForm.value.dateType === "day") { searchForm.value.entryDate = value; } else { searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); } } else { searchForm.value.entryDate = undefined; searchForm.value.entryDateStart = undefined; searchForm.value.entryDateEnd = undefined; } reloadData(); }; const summaryColumns = ref([ { label: "ç产人", prop: "staffName", minWidth: 100 }, { label: "产é", prop: "output", minWidth: 90 }, { label: "ææ¶é´å计", prop: "monthTimeTotal", minWidth: 110 }, { label: "æååæ°", prop: "conversionRatio", minWidth: 90, formatData: v => (v == null ? "-" : Number(v).toFixed(2)), }, ]); const getList = () => { tableLoading.value = true; const params = { ...searchForm.value, ...page }; salesLedgerProductionAccountingList(params) .then(res => { const records = res.data.records || []; leftTableData.value = records; page.total = res.data.total || 0; }) .finally(() => { tableLoading.value = false; }); }; const getList1 = () => { tableLoading1.value = true; const params = { ...page1, ...searchForm.value }; salesLedgerProductionAccountingListProductionDetails(params) .then(res => { tableData.value = res.data.records || []; page1.total = res.data.total || 0; }) .finally(() => { tableLoading1.value = false; }); }; // æå»ºå·¦ä¾§æ±æ»å°è´¦ï¼æçäº§äººæ±æ»äº§éãå·¥èµçï¼ const buildLeftTableData = records => { const map = {}; records.forEach(item => { const key = item.schedulingUserName || "æªç¥"; if (!map[key]) { map[key] = { id: key, schedulingUserName: key, finishedNum: 0, wages: 0, qualifiedRate: item.qualifiedRate ?? null, }; } map[key].finishedNum += Number(item.finishedNum || 0); map[key].wages += Number(item.wages || 0); if (item.qualifiedRate != null) { map[key].qualifiedRate = item.qualifiedRate; } }); leftTableData.value = Object.values(map); }; // 左侧æ¥/æåæ¢ const handleDateTypeChange = value => { // è¿éåªä½ä¸ºç鿡件çä¸é¨åï¼ç´æ¥éæ°æ¥è¯¢å表 if (value === "day") { searchForm.value.entryDate = dayjs().format("YYYY-MM-DD"); searchForm.value.dateRange = searchForm.value.entryDate; } else { searchForm.value.entryDateStart = dayjs() .startOf("month") .format("YYYY-MM-DD"); searchForm.value.entryDateEnd = dayjs().endOf("month").format("YYYY-MM-DD"); searchForm.value.dateRange = [ searchForm.value.entryDateStart, searchForm.value.entryDateEnd, const detailColumns = computed(() => { if (searchForm.deptType === 2) { return [ { label: "ç产人", prop: "staffName", minWidth: 90 }, { label: "å·¥åº", prop: "processName", minWidth: 90 }, { label: "产å大类", prop: "productName", minWidth: 100 }, { label: "åå·/é¶ä»¶", prop: "modelName", minWidth: 140 }, { label: "æäº§é", prop: "quantity", minWidth: 90 }, { label: "æ åæ°é", prop: "standardQuantity", minWidth: 90 }, { label: "æç®æ¶é¿", prop: "convertedHours", minWidth: 90 }, ]; } return [ { label: "ç产人", prop: "staffName", minWidth: 90 }, { label: "å·¥åº", prop: "processName", minWidth: 80 }, { label: "å°æç´æ°é", prop: "violinQty", minWidth: 90 }, { label: "大æç´æ°é", prop: "celloQty", minWidth: 90 }, { label: "è´æ¯æ°é", prop: "bassQty", minWidth: 80 }, { label: "大ææç®ç", prop: "celloFactor", minWidth: 90 }, { label: "è´æ¯æç®ç", prop: "bassFactor", minWidth: 90 }, { label: "æäº§æåå°æç´", prop: "foldedQuantity", minWidth: 120 }, { label: "æ åæ°é", prop: "standardQuantity", minWidth: 90 }, { label: "æç®æ¶é¿", prop: "convertedHours", minWidth: 90 }, ]; }); reloadData(); const defaultMonth = () => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`; }; const reloadData = () => { page.current = 1; page1.current = 1; getList(); tableData.value = []; const buildParams = () => ({ deptType: searchForm.deptType, yearMonth: searchForm.yearMonth, staffName: detailStaffName.value || undefined, }); const fetchWorkday = () => { costingWorkday(searchForm.yearMonth).then(res => { const d = res.data || {}; workInfo.workDays = d.workDays || 0; workInfo.dayHours = d.dayHours || 8; workInfo.monthHours = d.monthHours || 0; }); }; // ç¹å»å·¦ä¾§è¡ï¼å·å³ä¾§æç»ï¼æçäº§äººè¿æ»¤ï¼ const handleLeftRowClick = row => { searchForm.value.schedulingUserName = row.schedulingUserName || ""; handleQuery(); const getSummary = () => { loading.value = true; costingSummary({ deptType: searchForm.deptType, yearMonth: searchForm.yearMonth, staffName: searchForm.staffName || undefined, }) .then(res => { summaryData.value = res.data || []; }) .finally(() => { loading.value = false; }); }; // æ¥è¯¢å表 /** æç´¢æé®æä½ */ const getDetail = () => { loadingDetail.value = true; costingDetail(buildParams()) .then(res => { detailData.value = (res.data || []).map((item, idx) => ({ ...item, rowKey: `${item.staffId}-${item.processId}-${item.productModelId ?? ""}-${idx}`, })); }) .finally(() => { loadingDetail.value = false; }); }; const handleQuery = () => { page1.current = 1; getList1(); detailStaffName.value = ""; getSummary(); getDetail(); fetchWorkday(); }; // å¯¼åº const handleOut = () => { ElMessageBox.confirm("éä¸çå 容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { proxy.download( "/productionAccount/exportSalarySlip", { ...searchForm.value }, "çäº§å·¥èµæ¡.xlsx" ); }) .catch(() => { proxy.$modal.msg("已忶"); }); const handleSummaryRowClick = row => { detailStaffName.value = row.staffName || ""; getDetail(); }; const handleBatchAccounting = () => { ElMessageBox.confirm("ç¡®å®è¦æå½åç鿡件æ§è¡æ¹éæ ¸ç®åï¼", "æ¹éæ ¸ç®", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { const { dateRange, ...batchPayload } = searchForm.value; return productionAccountingBatchAccounting(batchPayload); }) .then(() => { proxy.$modal.msgSuccess("æ¹éæ ¸ç®æå"); reloadData(); }) .catch(() => { proxy.$modal.msg("已忶"); }); const openConfig = () => { configDialog.value?.openDialog(); }; const handleSingleAdjust = () => { if (!selectedRows.value.length) { proxy.$modal.msgWarning("请å å¾é䏿¡è®°å½"); return; } if (selectedRows.value.length > 1) { proxy.$modal.msgWarning("å个修æ£åªè½éæ©ä¸æ¡è®°å½"); return; } const row = selectedRows.value[0]; adjustForm.id = row.id; adjustForm.reportNo = row.reportNo || ""; adjustForm.schedulingUserName = row.schedulingUserName || ""; adjustForm.wages = Number(row.wages || 0); adjustForm.remark = ""; adjustDialogVisible.value = true; }; const confirmAdjust = () => { const selectedRow = selectedRows.value[0]; if (!selectedRow || !selectedRow.userId) { proxy.$modal.msgWarning("å½åè®°å½ç¼ºå°æ¥å·¥äººåä¿¡æ¯ï¼æ æ³ä¿®æ£"); return; } if (!adjustForm.wages && adjustForm.wages !== 0) { proxy.$modal.msgWarning("请è¾å ¥ä¿®æ£éé¢"); return; } productionAccountingSingleAdjust({ productionProductMainId: adjustForm.id, schedulingUserId: selectedRow.userId, schedulingUserName: adjustForm.schedulingUserName, adjustAmount: adjustForm.wages, adjustRemark: adjustForm.remark, adjustUser: proxy.$store?.state?.user?.userId || undefined, }).then(() => { proxy.$modal.msgSuccess("ä¿®æ£æå"); adjustDialogVisible.value = false; reloadData(); }).catch(() => {}); }; const handleRowAdjust = row => { selectedRows.value = [row]; handleSingleAdjust(); const handleExport = () => { proxy.download( "/productionCosting/export", { deptType: searchForm.deptType, yearMonth: searchForm.yearMonth, staffName: detailStaffName.value || undefined, }, "çäº§æ ¸ç®æç».xlsx" ); }; onMounted(() => { getList(); searchForm.yearMonth = defaultMonth(); handleQuery(); }); </script> <style scoped lang="scss"> .filter-card { margin-bottom: 12px; } .content-row { width: 100%; } .content-row .left-col, .content-row .right-col { margin-bottom: 16px; } .left-panel, .right-panel { display: flex; flex-direction: column; gap: 10px; min-width: 0; } .right-toolbar { display: flex; justify-content: flex-end; gap: 10px; } .left-header { display: flex; align-items: center; gap: 12px; } .left-title { font-size: 16px; color: #ffffff; } .header-filters { display: flex; align-items: center; flex: 1; justify-content: flex-end; gap: 8px; } .search_title { color: #ffffff; } .ml10 { margin-left: 10px; .panel-title { font-weight: 600; margin-bottom: 8px; color: #303133; } </style>