| src/api/salesManagement/deliveryLedger.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/basicData/customerFile/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/basicData/supplierManage/components/HomeTab.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/basicData/supplierManage/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/procurementManagement/procurementLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionCosting/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionOrder/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionReporting/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/salesManagement/receiptPaymentLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/salesManagement/salesLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/salesManagement/deliveryLedger.js
@@ -27,3 +27,12 @@ data: query, }); } // 兼容旧页面(销售台账)发货接口 export function addShippingInfo(data) { return request({ url: "/shippingInfo/add", method: "post", data, }); } src/views/basicData/customerFile/index.vue
@@ -15,10 +15,10 @@ style="width: 240px" clearable @change="handleQuery"> <el-option label="零售客户" value="零售客户" /> <el-option label="进销商客户" value="进销商客户" /> <el-option label="对公" value="1" /> <el-option label="对私" value="2" /> </el-select> <el-button type="primary" @click="handleQuery" @@ -125,10 +125,10 @@ <el-select v-model="form.customerType" placeholder="请选择" clearable> <el-option label="零售客户" value="零售客户" /> <el-option label="进销商客户" value="进销商客户" /> <el-option label="对公" :value="1" /> <el-option label="对私" :value="2" /> </el-select> </el-form-item> </el-col> @@ -276,7 +276,8 @@ </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitReminderForm">确认</el-button> <el-button type="primary" @click="submitReminderForm">确认</el-button> <el-button @click="closeReminderDialog">取消</el-button> </div> </template> @@ -359,7 +360,8 @@ </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitNegotiationForm">确认</el-button> <el-button type="primary" @click="submitNegotiationForm">确认</el-button> <el-button @click="closeNegotiationDialog">取消</el-button> </div> </template> @@ -383,7 +385,7 @@ <el-col :span="12"> <div class="info-item"> <span class="info-label">客户分类:</span> <span class="info-value">{{ detailForm.customerType }}</span> <span class="info-value">{{ detailForm.customerType==1?"对公":"对私" }}</span> </div> </el-col> </el-row> @@ -713,6 +715,9 @@ label: "客户分类", prop: "customerType", width: 120, formatData: params => { return params == 1 ? "对公" : "对私"; }, }, { label: "客户名称", @@ -854,20 +859,20 @@ }, rules: { customerName: [{ required: true, message: "请输入", trigger: "blur" }], taxpayerIdentificationNumber: [ { required: true, message: "请输入", trigger: "blur" }, ], companyAddress: [{ required: true, message: "请输入", trigger: "blur" }], companyPhone: [{ required: true, message: "请输入", trigger: "blur" }], // taxpayerIdentificationNumber: [ // { required: true, message: "请输入", trigger: "blur" }, // ], // companyAddress: [{ required: true, message: "请输入", trigger: "blur" }], // companyPhone: [{ required: true, message: "请输入", trigger: "blur" }], // contactPerson: [{ required: true, message: "请输入", trigger: "blur" }], // contactPhone: [{ required: true, message: "请输入", trigger: "blur" }], maintainer: [{ required: false, message: "请选择", trigger: "change" }], maintenanceTime: [ { required: false, message: "请选择", trigger: "change" }, ], basicBankAccount: [{ required: true, message: "请输入", trigger: "blur" }], bankAccount: [{ required: true, message: "请输入", trigger: "blur" }], bankCode: [{ required: true, message: "请输入", trigger: "blur" }], // basicBankAccount: [{ required: true, message: "请输入", trigger: "blur" }], // bankAccount: [{ required: true, message: "请输入", trigger: "blur" }], // bankCode: [{ required: true, message: "请输入", trigger: "blur" }], customerType: [{ required: true, message: "请选择", trigger: "change" }], }, }); src/views/basicData/supplierManage/components/HomeTab.vue
@@ -3,185 +3,172 @@ <div class="search_form"> <div> <span class="search_title">供应商档案:</span> <el-input v-model="searchForm.supplierName" <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="输入供应商名称搜索" @change="handleQuery" clearable :prefix-icon="Search" /> <el-button type="primary" @click="handleQuery" style="margin-left: 10px" >搜索</el-button > :prefix-icon="Search" /> <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> </div> <div> <el-button type="primary" @click="openForm('add')" >新增供应商</el-button > <el-button type="primary" @click="openForm('add')">新增供应商</el-button> <el-button @click="handleOut">导出</el-button> <el-button type="info" plain icon="Upload" @click="handleImport" >导入</el-button > <el-button type="danger" plain @click="handleDelete">删除</el-button> <el-button type="info" plain icon="Upload" @click="handleImport">导入</el-button> <el-button type="danger" plain @click="handleDelete">删除</el-button> </div> </div> <div class="table_list"> <PIMTable rowKey="id" <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true" @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination" ></PIMTable> @pagination="pagination"></PIMTable> </div> <el-dialog v-model="dialogFormVisible" <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增供应商信息' : '编辑供应商信息'" width="70%" @close="closeDia" > <el-form :model="form" @close="closeDia"> <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef" > ref="formRef"> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="供应商名称:" prop="supplierName"> <el-input v-model="form.supplierName" <el-form-item label="供应商名称:" prop="supplierName"> <el-input v-model="form.supplierName" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="纳税人识别号:" prop="taxpayerIdentificationNum" > <el-input v-model="form.taxpayerIdentificationNum" <el-form-item label="纳税人识别号:" prop="taxpayerIdentificationNum"> <el-input v-model="form.taxpayerIdentificationNum" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="公司地址:" prop="companyAddress"> <el-input v-model="form.companyAddress" <el-form-item label="公司地址:" prop="companyAddress"> <el-input v-model="form.companyAddress" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="公司电话:" prop="companyPhone"> <el-input v-model="form.companyPhone" <el-form-item label="公司电话:" prop="companyPhone"> <el-input v-model="form.companyPhone" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="开户行:" prop="bankAccountName"> <el-input v-model="form.bankAccountName" <el-form-item label="开户行:" prop="bankAccountName"> <el-input v-model="form.bankAccountName" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="账号:" prop="bankAccountNum"> <el-input v-model="form.bankAccountNum" <el-form-item label="账号:" prop="bankAccountNum"> <el-input v-model="form.bankAccountNum" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="联系人:" prop="contactUserName"> <el-input v-model="form.contactUserName" <el-form-item label="联系人:" prop="contactUserName"> <el-input v-model="form.contactUserName" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="联系电话:" prop="contactUserPhone"> <el-input v-model="form.contactUserPhone" <el-form-item label="联系电话:" prop="contactUserPhone"> <el-input v-model="form.contactUserPhone" placeholder="请输入" clearable /> clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="维护人:" prop="maintainUserId"> <el-select v-model="form.maintainUserId" <el-form-item label="维护人:" prop="maintainUserId"> <el-select v-model="form.maintainUserId" placeholder="请选择" clearable disabled > <el-option v-for="item in userList" disabled> <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.userId" /> :value="item.userId" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="维护时间:" prop="maintainTime"> <el-date-picker style="width: 100%" <el-form-item label="维护时间:" prop="maintainTime"> <el-date-picker style="width: 100%" v-model="form.maintainTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable /> clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="供应商类型:" prop="supplierType"> <el-select v-model="form.supplierType" placeholder="请选择" clearable> <el-option label="甲" value="甲" /> <el-option label="乙" value="乙" /> <el-option label="丙" value="丙" /> <el-option label="丁" value="丁" /> <el-form-item label="供应商类型:" prop="supplierType"> <el-select v-model="form.supplierType" placeholder="请选择" clearable> <el-option label="对公" :value="1" /> <el-option label="对私" :value="2" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="是否白名单:" prop="isWhite"> <el-select v-model="form.isWhite" placeholder="请选择" clearable> <el-option label="是" :value="0" /> <el-option label="否" :value="1" /> <el-form-item label="是否白名单:" prop="isWhite"> <el-select v-model="form.isWhite" placeholder="请选择" clearable> <el-option label="是" :value="0" /> <el-option label="否" :value="1" /> </el-select> </el-form-item> </el-col> @@ -189,21 +176,18 @@ </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitForm">确认</el-button> <el-button type="primary" @click="submitForm">确认</el-button> <el-button @click="closeDia">取消</el-button> </div> </template> </el-dialog> <!-- 供应商导入对话框 --> <el-dialog :title="upload.title" <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body > <el-upload ref="uploadRef" append-to-body> <el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" @@ -213,26 +197,23 @@ :on-success="handleFileSuccess" :on-error="handleFileError" :auto-upload="false" drag > drag> <el-icon class="el-icon--upload"><upload-filled /></el-icon> <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> <template #tip> <div class="el-upload__tip text-center"> <span>仅允许导入xls、xlsx格式文件。</span> <el-link type="primary" <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate" >下载模板</el-link > @click="importTemplate">下载模板</el-link> </div> </template> </el-upload> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitFileForm">确 定</el-button> <el-button type="primary" @click="submitFileForm">确 定</el-button> <el-button @click="upload.open = false">取 消</el-button> </div> </template> @@ -269,6 +250,9 @@ label: "供应商类型", prop: "supplierType", width: 120, formatData: params => { return params == 1 ? "对公" : "对私"; }, }, { label: "纳税人识别号", @@ -283,7 +267,7 @@ { label: "联系方式", prop: "companyPhone", width:150 width: 150, }, { label: "开户行", @@ -312,19 +296,19 @@ { label: "维护时间", prop: "maintainTime", width:100 width: 100, }, { dataType: "action", label: "操作", align: "center", fixed: 'right', fixed: "right", width: 150, operation: [ { name: "编辑", type: "text", clickFun: (row) => { clickFun: row => { openForm("edit", row); }, }, @@ -332,10 +316,10 @@ //资质附件 name: "资质文件", type: "text", clickFun: (row) => { openFilesFormDia(row) } } clickFun: row => { openFilesFormDia(row); }, }, ], }, ]); @@ -348,7 +332,7 @@ size: 100, total: 0, }); const filesDia = ref() const filesDia = ref(); // 用户信息表单弹框数据 const operationType = ref(""); const dialogFormVisible = ref(false); @@ -372,18 +356,20 @@ }, rules: { supplierName: [{ required: true, message: "请输入", trigger: "blur" }], taxpayerIdentificationNum: [ { required: true, message: "请输入", trigger: "blur" }, ], companyAddress: [{ required: true, message: "请输入", trigger: "blur" }], companyPhone: [{ required: true, message: "请输入", trigger: "blur" }], bankAccountName: [{ required: true, message: "请输入", trigger: "blur" }], bankAccountNum: [{ required: true, message: "请输入", trigger: "blur" }], // taxpayerIdentificationNum: [ // { required: true, message: "请输入", trigger: "blur" }, // ], // companyAddress: [{ required: true, message: "请输入", trigger: "blur" }], // companyPhone: [{ required: true, message: "请输入", trigger: "blur" }], // bankAccountName: [{ required: true, message: "请输入", trigger: "blur" }], // bankAccountNum: [{ required: true, message: "请输入", trigger: "blur" }], contactUserName: [{ required: false, message: "请输入", trigger: "blur" }], contactUserPhone: [{ required: false, message: "请输入", trigger: "blur" }], maintainUserId: [{ required: false, message: "请选择", trigger: "change" }], maintainTime: [{ required: false, message: "请选择", trigger: "change" }], supplierType: [{ required: true, message: "请选择供应商类型", trigger: "change" }], supplierType: [ { required: true, message: "请选择供应商类型", trigger: "change" }, ], }, }); const { searchForm, form, rules } = toRefs(data); @@ -394,7 +380,7 @@ page.current = 1; getList(); }; const pagination = (obj) => { const pagination = obj => { page.current = obj.page; page.size = obj.limit; getList(); @@ -406,7 +392,7 @@ } const getList = () => { tableLoading.value = true; listSupplier({ ...searchForm.value, ...page, isWhite: 0 }).then((res) => { listSupplier({ ...searchForm.value, ...page, isWhite: 0 }).then(res => { tableLoading.value = false; tableData.value = res.data.records; page.total = res.data.total; @@ -433,7 +419,11 @@ } /** 下载模板 */ function importTemplate() { proxy.download("/system/supplier/downloadTemplate", {}, "供应商导入模板.xlsx"); proxy.download( "/system/supplier/downloadTemplate", {}, "供应商导入模板.xlsx" ); } /**文件上传中处理 */ @@ -462,7 +452,7 @@ proxy.$modal.msgError("文件上传失败"); }; // 表格选择数据 const handleSelectionChange = (selection) => { const handleSelectionChange = selection => { selectedRows.value = selection; }; // 打开弹框 @@ -471,11 +461,11 @@ form.value = {}; form.value.maintainUserId = userStore.id; form.value.maintainTime = getCurrentDate(); userListNoPage().then((res) => { userListNoPage().then(res => { userList.value = res.data; }); if (type === "edit") { getSupplier(row.id).then((res) => { getSupplier(row.id).then(res => { form.value = { ...res.data }; }); } @@ -483,7 +473,7 @@ }; // 提交表单 const submitForm = () => { proxy.$refs["formRef"].validate((valid) => { proxy.$refs["formRef"].validate(valid => { if (valid) { if (operationType.value === "edit") { submitEdit(); @@ -495,7 +485,7 @@ }; // 提交新增 const submitAdd = () => { addSupplier(form.value).then((res) => { addSupplier(form.value).then(res => { proxy.$modal.msgSuccess("提交成功"); closeDia(); getList(); @@ -503,7 +493,7 @@ }; // 提交修改 const submitEdit = () => { updateSupplier(form.value).then((res) => { updateSupplier(form.value).then(res => { proxy.$modal.msgSuccess("提交成功"); closeDia(); getList(); @@ -522,7 +512,11 @@ type: "warning", }) .then(() => { proxy.download("/system/supplier/export", { isWhite: 0 }, "供应商档案.xlsx"); proxy.download( "/system/supplier/export", { isWhite: 0 }, "供应商档案.xlsx" ); }) .catch(() => { proxy.$modal.msg("已取消"); @@ -533,12 +527,14 @@ let ids = []; if (selectedRows.value.length > 0) { // 检查是否有他人维护的数据 const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName); const unauthorizedData = selectedRows.value.filter( item => item.maintainUserName !== userStore.nickName ); if (unauthorizedData.length > 0) { proxy.$modal.msgWarning("不可删除他人维护的数据"); return; } ids = selectedRows.value.map((item) => item.id); ids = selectedRows.value.map(item => item.id); } else { proxy.$modal.msgWarning("请选择数据"); return; @@ -551,7 +547,7 @@ .then(() => { tableLoading.value = true; delSupplier(ids) .then((res) => { .then(res => { proxy.$modal.msgSuccess("删除成功"); getList(); }) @@ -573,10 +569,10 @@ return `${year}-${month}-${day}`; } // 打开附件弹框 const openFilesFormDia = (row) => { const openFilesFormDia = row => { nextTick(() => { filesDia.value?.openDialog(row) }) filesDia.value?.openDialog(row); }); }; onMounted(() => { src/views/basicData/supplierManage/index.vue
@@ -1,11 +1,14 @@ <!-- 在你的主页面中 --> <template> <div class="app-container"> <el-tabs v-model="activeTab" @tab-change="handleTabChange"> <el-tab-pane label="正常供应商" name="home"> <el-tabs v-model="activeTab" @tab-change="handleTabChange"> <el-tab-pane label="正常供应商" name="home"> <HomeTab ref="homeTab" /> </el-tab-pane> <el-tab-pane label="黑名单" name="blacklist"> <el-tab-pane label="黑名单" name="blacklist"> <BlacklistTab ref="blacklistTab" /> </el-tab-pane> </el-tabs> @@ -13,31 +16,35 @@ </template> <script> import HomeTab from './components/HomeTab.vue' import BlacklistTab from './components/BlacklistTab.vue' import HomeTab from "./components/HomeTab.vue"; import BlacklistTab from "./components/BlacklistTab.vue"; export default { name: 'MainPage', name: "MainPage", components: { HomeTab, BlacklistTab BlacklistTab, }, data() { return { activeTab: 'home' } activeTab: "home", }; }, methods: { handleTabChange(tabName) { this.activeTab = tabName this.activeTab = tabName; this.$nextTick(() => { if (tabName === 'home') { this.$refs.homeTab && this.$refs.homeTab.getList && this.$refs.homeTab.getList() } else if (tabName === 'blacklist') { this.$refs.blacklistTab && this.$refs.blacklistTab.getList && this.$refs.blacklistTab.getList() if (tabName === "home") { this.$refs.homeTab && this.$refs.homeTab.getList && this.$refs.homeTab.getList(); } else if (tabName === "blacklist") { this.$refs.blacklistTab && this.$refs.blacklistTab.getList && this.$refs.blacklistTab.getList(); } }) }); }, } } }, }; </script> src/views/index.vue
@@ -9,14 +9,15 @@ <div class="welcome-banner"> <div class="welcome-title"> <span class="welcome-user">{{ userStore.roleName || '系统管理员' }}</span> <span> 您好!祝您开心每一天</span> <span> 您好!!祝您开心每一天</span> </div> <div class="welcome-time">登录于: {{ userStore.currentLoginTime }}</div> </div> <!-- 用户信息卡片 --> <div class="user-card"> <img :src="userStore.avatar" class="avatar" alt="" /> <img :src="userStore.avatar" class="avatar" alt="" /> <div class="user-card-main"> <div class="user-name">{{ userStore.name }}</div> <div class="user-role">{{ userStore.roleName }}</div> @@ -44,7 +45,6 @@ <div class="data-value">{{ businessInfo.monthSaleHaveMoney }}</div> </div> </div> </div> <div class="data-card purchase"> <div class="data-title">采购数据</div> @@ -76,8 +76,10 @@ <!-- 右:待办事项 --> <div class="todo-panel"> <div class="section-title">待办事项</div> <ul class="todo-list" v-if="todoList.length > 0"> <li v-for="item in todoList" :key="item.id"> <ul class="todo-list" v-if="todoList.length > 0"> <li v-for="item in todoList" :key="item.id"> <div style="display: flex;flex-direction: column;justify-content: space-between;width: 100%;gap: 20px"> <div style="display: flex;justify-content: space-between;align-items: center;"> <div class="todo-title">待办编号:{{ item.approveId }}</div> @@ -88,7 +90,8 @@ </div> </li> </ul> <div v-else style="text-align: center"> <div v-else style="text-align: center"> 暂无数据 </div> </div> @@ -98,23 +101,36 @@ <div class="process-panel__header"> <div class="section-title">工序数据生产统计明细</div> <div style="display: flex; gap: 10px; align-items: center;"> <el-button type="primary" size="small" plain icon="Filter" @click="openProcessDialog">选择工序</el-button> <el-button type="info" size="small" plain icon="Refresh" @click="resetProcessFilter">重置</el-button> <el-radio-group v-model="processRange" size="small" @change="refreshProcessStats"> <el-button type="primary" size="small" plain icon="Filter" @click="openProcessDialog">选择工序</el-button> <el-button type="info" size="small" plain icon="Refresh" @click="resetProcessFilter">重置</el-button> <el-radio-group v-model="processRange" size="small" @change="refreshProcessStats"> <el-radio-button :value="1">日</el-radio-button> <el-radio-button :value="2">周</el-radio-button> <el-radio-button :value="3">月</el-radio-button> </el-radio-group> </div> </div> <div class="process-panel__body"> <div class="process-panel__chart"> <Echarts :chartStyle="{ width: '100%', height: '100%' }" :grid="processGrid" :series="processSeries" :tooltip="processTooltip" :xAxis="processXAxis" :yAxis="processYAxis" style="height: 100%" <Echarts :chartStyle="{ width: '100%', height: '100%' }" :grid="processGrid" :series="processSeries" :tooltip="processTooltip" :xAxis="processXAxis" :yAxis="processYAxis" style="height: 100%" @click="handleChartClick" /> </div> <div class="process-panel__aside"> <div class="process-legend"> <div class="process-legend__item"> @@ -127,9 +143,7 @@ <span class="dot dot-teal"></span><span>产出量</span> </div> </div> <div class="process-card process-card--name">{{ processAside.processName }}</div> <div class="process-card"> <div class="process-card__label">累计总投入</div> <div class="process-card__value">{{ formatAmount(processAside.totalInput) }} @@ -149,13 +163,18 @@ </div> </div> </div> <!-- 工序选择弹窗 --> <el-dialog v-model="processDialogVisible" title="选择工序" width="500px" append-to-body> <el-dialog v-model="processDialogVisible" title="选择工序" width="500px" append-to-body> <div class="process-selection-wrapper"> <el-checkbox-group v-model="tempProcessIds"> <div class="process-grid"> <el-checkbox v-for="item in processOptions" :key="item.id" :label="item.id" border> <el-checkbox v-for="item in processOptions" :key="item.id" :label="item.id" border> {{ item.name }} </el-checkbox> </div> @@ -164,7 +183,8 @@ <template #footer> <span class="dialog-footer"> <el-button @click="processDialogVisible = false">取消</el-button> <el-button type="primary" @click="handleProcessDialogConfirm">确认</el-button> <el-button type="primary" @click="handleProcessDialogConfirm">确认</el-button> </span> </template> </el-dialog> @@ -174,7 +194,9 @@ <div class="section-title">客户合同金额分析</div> <div class="contract-summary"> <div class="contract-info"> <img src="../assets/images/khtitle.png" alt="" style="width: 42px" /> <img src="../assets/images/khtitle.png" alt="" style="width: 42px" /> <div class="contract-card"> <div class="contract-name">总合同金额(元)</div> <div class="contract-meta"> @@ -184,16 +206,20 @@ </div> </div> </div> <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 180px;margin-top: 20px"> <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 180px;margin-top: 20px"> <div> <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie" :series="materialPieSeries" <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie" :series="materialPieSeries" :tooltip="pieTooltip"></Echarts> </div> <ul class="contract-list"> <li v-for="item in materialPieSeries[0].data" :key="item.name"> <li v-for="item in materialPieSeries[0].data" :key="item.name"> <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 class="line" :style="{ color: item.itemStyle.color }">●{{ item.name }}</div> <div style="width: 70px">{{ item.rate }}%</div> <div>¥{{ item.value }}</div> </div> @@ -210,17 +236,26 @@ <!-- <el-radio-button label="按季度" :value="3" />--> <!-- </el-radio-group>--> </div> <Echarts ref="chart" :color="barColors2" :chartStyle="chartStyle" :grid="grid" :series="barSeries" :tooltip="tooltip" :xAxis="xAxis" :yAxis="yAxis" style="height: 260px"></Echarts> <Echarts ref="chart" :color="barColors2" :chartStyle="chartStyle" :grid="grid" :series="barSeries" :tooltip="tooltip" :xAxis="xAxis" :yAxis="yAxis" style="height: 260px"></Echarts> </div> </div> <!-- 底部横向两栏 --> <div class="dashboard-row"> <div class="main-panel"> <div style="display: flex;justify-content: space-between;align-items: center;margin-bottom: 10px;"> <div class="section-title" style="margin-bottom: 0;">质量统计</div> <el-radio-group v-model="qualityRange" size="small" @change="qualityStatisticsInfo"> <div class="section-title" style="margin-bottom: 0;">质量统计</div> <el-radio-group v-model="qualityRange" size="small" @change="qualityStatisticsInfo"> <el-radio-button :value="1">周</el-radio-button> <el-radio-button :value="2">月</el-radio-button> <el-radio-button :value="3">季度</el-radio-button> @@ -231,42 +266,55 @@ <div class="quality-card two">过程检验数量 <span>{{ qualityStatisticsObject.processNum }}件</span></div> <div class="quality-card three">出厂已检数量 <span>{{ qualityStatisticsObject.factoryNum }}件</span></div> </div> <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="barLegend" :series="barSeries1" :tooltip="tooltip" :xAxis="xAxis1" :yAxis="yAxis1" style="height: 260px"></Echarts> <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="barLegend" :series="barSeries1" :tooltip="tooltip" :xAxis="xAxis1" :yAxis="yAxis1" style="height: 260px"></Echarts> </div> <div class="main-panel"> <div class="section-title">回款与开票分析</div> <Echarts ref="invoiceChart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries" :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 270px;" /> <Echarts ref="invoiceChart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries" :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 270px;" /> </div> </div> </div> </template> <script setup> import { ref, onMounted, computed, reactive } from 'vue' import { ref, onMounted, computed, reactive } from "vue"; import Echarts from "@/components/Echarts/echarts.vue"; import * as echarts from 'echarts'; import * as echarts from "echarts"; import useUserStore from "@/store/modules/user.js"; import { analysisCustomerContractAmounts, getAmountHalfYear, analysisCustomerContractAmounts, getAmountHalfYear, getBusiness, homeTodos, processDataProductionStatistics, statisticsReceivablePayable, qualityInspectionStatistics qualityInspectionStatistics, } from "@/api/viewIndex.js"; import { list } from '@/api/productionManagement/productionProcess'; import { list } from "@/api/productionManagement/productionProcess"; const userStore = useUserStore(); const userStore = useUserStore() const processOptions = ref([]) const selectedProcessIds = ref([]) const tempProcessIds = ref([]) const processDialogVisible = ref(false) const activeProcessIndex = ref(0) const processOptions = ref([]); const selectedProcessIds = ref([]); const tempProcessIds = ref([]); const processDialogVisible = ref(false); const activeProcessIndex = ref(0); const businessInfo = ref({ inventoryNum: 0, @@ -275,455 +323,483 @@ monthSaleHaveMoney: 0, monthSaleMoney: 0, todayInventoryNum: 0, }) }); const qualityStatisticsObject = ref({ supplierNum: 0, processNum: 0, factoryNum: 0, }) const sum = ref(0) const yny = ref(0) const chain = ref(0) }); const sum = ref(0); const yny = ref(0); const chain = ref(0); const pieLegend = reactive({ show: false, }) }); const barSeries = ref([ { type: 'bar', type: "bar", data: [], label: { show: true, } }, ]) }, ]); const barSeries1 = ref([ { name: '原材料不合格数', type: 'bar', name: "原材料不合格数", type: "bar", barGap: 0, emphasis: { focus: 'series' focus: "series", }, data: [] data: [], }, { name: '过程不合格数', type: 'bar', name: "过程不合格数", type: "bar", emphasis: { focus: 'series' focus: "series", }, data: [] data: [], }, { name: '出厂不合格数', type: 'bar', name: "出厂不合格数", type: "bar", emphasis: { focus: 'series' focus: "series", }, data: [] data: [], }, ]) ]); const chartStyle = { width: '100%', height: '100%' // 设置图表容器的高度 } width: "100%", height: "100%", // 设置图表容器的高度 }; const chartStylePie = { width: '140%', height: '140%' // 设置图表容器的高度 } width: "140%", height: "140%", // 设置图表容器的高度 }; const grid = { left: '3%', right: '4%', bottom: '3%', containLabel: true } left: "3%", right: "4%", bottom: "3%", containLabel: true, }; const barLegend = { show: true, data: ['原材料不合格数', '过程不合格数', '出厂不合格数'] } data: ["原材料不合格数", "过程不合格数", "出厂不合格数"], }; const barLegend1 = { show: true, data: ['预付账款', '应付账款', '预收账款', '应收账款'] } data: ["预付账款", "应付账款", "预收账款", "应收账款"], }; const lineLegend = { show: true, data: ['开票', '回款'] } data: ["开票", "回款"], }; const tooltip = { trigger: 'axis', trigger: "axis", axisPointer: { type: 'shadow' } } const xAxis = [{ type: 'value', }] const xAxis1 = ref([{ type: 'category', type: "shadow", }, }; const xAxis = [ { type: "value", }, ]; const xAxis1 = ref([ { type: "category", axisTick: { show: false }, data: [] }]) const yAxis = [{ type: 'category', data: ['应付账款', '应收账款',] }] const yAxis1 = [{ type: 'value' }] data: [], }, ]); const yAxis = [ { type: "category", data: ["应付账款", "应收账款"], }, ]; const yAxis1 = [ { type: "value", }, ]; const pieTooltip = reactive({ trigger: 'item', trigger: "item", formatter: function (params) { // 动态生成提示信息,基于数据项的 name 属性 const description = params.name === '本月回款金额' ? '本月回款金额' : '应收款金额'; const description = params.name === "本月回款金额" ? "本月回款金额" : "应收款金额"; return `${description} ${formatNumber(params.value)}元 ${params.percent}%`; }, position: 'right' }) position: "right", }); const materialPieSeries = ref([ { type: 'pie', radius: ['66%', '90%'], type: "pie", radius: ["66%", "90%"], avoidLabelOverlap: false, itemStyle: { borderColor: '#fff', borderWidth: 2 borderColor: "#fff", borderWidth: 2, }, label: { show: false show: false, }, data: [] } ]) data: [], }, ]); const lineSeries = ref([ { type: 'line', type: "line", data: [], label: { show: true show: true, }, showSymbol: true, // 显示圆点 }, ]) ]); const tooltipLine = { trigger: 'axis', } trigger: "axis", }; const yAxis2 = ref([ { type: 'value', } ]) type: "value", }, ]); const xAxis2 = ref([ { type: 'category', type: "category", data: [], axisLabel: { interval: 0, formatter: function (value) { return value.replace(/~/g, '\n'); return value.replace(/~/g, "\n"); }, } } ]) }, }, ]); // 待办事项 const todoList = ref([]) const radio1 = ref(1) const qualityRange = ref(1) const todoList = ref([]); const radio1 = ref(1); const qualityRange = ref(1); // 图表引用 const barChart = ref(null) const lineChart = ref(null) const barColors2 = ['#5181DB', '#D369E0', '#F2CA6D', '#60CCA8'] const barChart = ref(null); const lineChart = ref(null); const barColors2 = ["#5181DB", "#D369E0", "#F2CA6D", "#60CCA8"]; // 随机颜色生成函数 const getRandomColor = () => { return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0'); } return ( "#" + Math.floor(Math.random() * 0xffffff) .toString(16) .padStart(6, "0") ); }; onMounted(() => { getBusinessData() analysisCustomer() todoInfoS() statisticsReceivable() qualityStatisticsInfo() getAmountHalfYearNum() getProcessList() }) getBusinessData(); analysisCustomer(); todoInfoS(); statisticsReceivable(); qualityStatisticsInfo(); getAmountHalfYearNum(); getProcessList(); }); // 数据统计 const getBusinessData = () => { getBusiness().then((res) => { businessInfo.value = { ...res.data } }) } getBusiness().then(res => { businessInfo.value = { ...res.data }; }); }; // 合同金额 const analysisCustomer = () => { analysisCustomerContractAmounts().then((res) => { sum.value = res.data.sum yny.value = res.data.yny chain.value = res.data.chain analysisCustomerContractAmounts().then(res => { sum.value = res.data.sum; yny.value = res.data.yny; chain.value = res.data.chain; // 为每个数据项分配随机颜色 materialPieSeries.value[0].data = res.data.item.map(item => ({ ...item, itemStyle: { color: getRandomColor() } })) }) } itemStyle: { color: getRandomColor() }, })); }); }; // 待办事项 const todoInfoS = () => { homeTodos().then((res) => { todoList.value = res.data }) } homeTodos().then(res => { todoList.value = res.data; }); }; // 获取工序列表 const getProcessList = () => { list().then(res => { processOptions.value = res.data }) } processOptions.value = res.data; }); }; const openProcessDialog = () => { tempProcessIds.value = [...selectedProcessIds.value] processDialogVisible.value = true } tempProcessIds.value = [...selectedProcessIds.value]; processDialogVisible.value = true; }; const handleProcessDialogConfirm = () => { selectedProcessIds.value = [...tempProcessIds.value] processDialogVisible.value = false refreshProcessStats() } selectedProcessIds.value = [...tempProcessIds.value]; processDialogVisible.value = false; refreshProcessStats(); }; const resetProcessFilter = () => { selectedProcessIds.value = [] tempProcessIds.value = [] refreshProcessStats() } selectedProcessIds.value = []; tempProcessIds.value = []; refreshProcessStats(); }; const handleChartClick = (params) => { const handleChartClick = params => { if (params && params.dataIndex !== undefined) { activeProcessIndex.value = params.dataIndex activeProcessIndex.value = params.dataIndex; } } }; // 应付应收统计 const statisticsReceivable = () => { statisticsReceivablePayable({ type: radio1.value }).then((res) => { statisticsReceivablePayable({ type: radio1.value }).then(res => { barSeries.value[0].data = [ // { value: res.data.prepayMoney, itemStyle: { color: barColors2[0] } }, { value: res.data.payableMoney, itemStyle: { color: barColors2[0] } }, // { value: res.data.advanceMoney, itemStyle: { color: barColors2[2] } }, { value: res.data.receivableMoney, itemStyle: { color: barColors2[1] } } ] }) } { value: res.data.receivableMoney, itemStyle: { color: barColors2[1] } }, ]; }); }; // 质检统计 const qualityStatisticsInfo = () => { qualityInspectionStatistics({ type: qualityRange.value }).then((res) => { xAxis1.value[0].data = [] barSeries1.value[0].data = [] barSeries1.value[1].data = [] barSeries1.value[2].data = [] qualityInspectionStatistics({ type: qualityRange.value }).then(res => { xAxis1.value[0].data = []; barSeries1.value[0].data = []; barSeries1.value[1].data = []; barSeries1.value[2].data = []; res.data.item.forEach(item => { xAxis1.value[0].data.push(item.date) barSeries1.value[0].data.push(item.supplierNum) barSeries1.value[1].data.push(item.processNum) barSeries1.value[2].data.push(item.factoryNum) }) qualityStatisticsObject.value.supplierNum = res.data.supplierNum qualityStatisticsObject.value.processNum = res.data.processNum qualityStatisticsObject.value.factoryNum = res.data.factoryNum }) } xAxis1.value[0].data.push(item.date); barSeries1.value[0].data.push(item.supplierNum); barSeries1.value[1].data.push(item.processNum); barSeries1.value[2].data.push(item.factoryNum); }); qualityStatisticsObject.value.supplierNum = res.data.supplierNum; qualityStatisticsObject.value.processNum = res.data.processNum; qualityStatisticsObject.value.factoryNum = res.data.factoryNum; }); }; const getAmountHalfYearNum = async () => { const res = await getAmountHalfYear() console.log(res) const monthName = [] const receiptAmount = [] const invoiceAmount = [] const res = await getAmountHalfYear(); console.log(res); const monthName = []; const receiptAmount = []; const invoiceAmount = []; res.data.forEach(item => { monthName.push(item.month) receiptAmount.push(item.receiptAmount) invoiceAmount.push(item.invoiceAmount) }) monthName.push(item.month); receiptAmount.push(item.receiptAmount); invoiceAmount.push(item.invoiceAmount); }); // 正确响应式赋值:创建新的 xAxis 和 series 对象 xAxis2.value[0].data = monthName xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~')); xAxis2.value[0].data = monthName; xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, "\n~")); lineSeries.value = [ { name: '开票', type: 'line', name: "开票", type: "line", data: invoiceAmount, stack: 'Total', stack: "Total", areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: 'rgba(131, 207, 255, 1)' color: "rgba(131, 207, 255, 1)", }, { offset: 1, color: 'rgba(186, 228, 255, 1)' } ]) color: "rgba(186, 228, 255, 1)", }, ]), }, itemStyle: { color: '#2D99FF', borderColor: '#2D99FF' color: "#2D99FF", borderColor: "#2D99FF", }, emphasis: { focus: 'series' focus: "series", }, lineStyle: { width: 0 width: 0, }, showSymbol: true, }, { name: '回款', type: 'line', name: "回款", type: "line", data: receiptAmount, stack: 'Total', stack: "Total", lineStyle: { width: 0 width: 0, }, itemStyle: { color: '#83CFFF', borderColor: '#83CFFF' color: "#83CFFF", borderColor: "#83CFFF", }, showSymbol: true, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: 'rgba(54, 153, 255, 1)' color: "rgba(54, 153, 255, 1)", }, { offset: 1, color: 'rgba(89, 169, 254, 1)' } ]) color: "rgba(89, 169, 254, 1)", }, ]), }, emphasis: { focus: 'series' focus: "series", }, } ] } }, ]; }; // 工序数据生产统计明细(假数据 + 图表) const processRange = ref(1) const processChartData = ref([]) const processRange = ref(1); const processChartData = ref([]); const processXAxis = ref([ { nameTextStyle: { color: 'rgba(0,0,0,0.35)', fontSize: 12 }, axisLabel: { color: 'rgba(0,0,0,0.35)' }, splitLine: { lineStyle: { color: 'rgba(0,0,0,0.06)', type: 'dashed' } }, nameTextStyle: { color: "rgba(0,0,0,0.35)", fontSize: 12 }, axisLabel: { color: "rgba(0,0,0,0.35)" }, splitLine: { lineStyle: { color: "rgba(0,0,0,0.06)", type: "dashed" } }, }, ]) ]); const processYAxis = ref([ { type: 'category', type: "category", axisTick: { show: false }, axisLine: { show: false }, axisLabel: { color: 'rgba(0,0,0,0.45)' }, axisLabel: { color: "rgba(0,0,0,0.45)" }, data: [], }, ]) ]); const processGrid = reactive({ left: 0, right: 100, top: 30, bottom: 20, containLabel: true }) const processGrid = reactive({ left: 0, right: 100, top: 30, bottom: 20, containLabel: true, }); const processTooltip = reactive({ trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: (params) => { const name = params?.[0]?.name ?? '' const list = Array.isArray(params) ? params : [] trigger: "axis", axisPointer: { type: "shadow" }, formatter: params => { const name = params?.[0]?.name ?? ""; const list = Array.isArray(params) ? params : []; const lines = list .map((p) => { const colorBox = `<span style="display:inline-block;margin-right:6px;border-radius:2px;width:10px;height:10px;background:${p.color}"></span>` return `${colorBox}${p.seriesName} <b style="float:right;">${Number(p.value || 0).toFixed(2)}</b>` .map(p => { const colorBox = `<span style="display:inline-block;margin-right:6px;border-radius:2px;width:10px;height:10px;background:${p.color}"></span>`; return `${colorBox}${p.seriesName} <b style="float:right;">${Number( p.value || 0 ).toFixed(2)}</b>`; }) .join('<br/>') return `<div style="min-width:140px;"><div style="font-weight:700;margin-bottom:6px;">${name}</div>${lines}</div>` .join("<br/>"); return `<div style="min-width:140px;"><div style="font-weight:700;margin-bottom:6px;">${name}</div>${lines}</div>`; }, }) }); const processSeries = computed(() => { const input = processChartData.value.map((i) => i.input) const scrap = processChartData.value.map((i) => i.scrap) const output = processChartData.value.map((i) => i.output) const input = processChartData.value.map(i => i.input); const scrap = processChartData.value.map(i => i.scrap); const output = processChartData.value.map(i => i.output); return [ { name: '投入量', type: 'bar', stack: 'total', name: "投入量", type: "bar", stack: "total", barWidth: 22, itemStyle: { color: '#1E5BFF', borderRadius: [6, 0, 0, 6] }, itemStyle: { color: "#1E5BFF", borderRadius: [6, 0, 0, 6] }, data: input, }, { name: '报废量', type: 'bar', stack: 'total', name: "报废量", type: "bar", stack: "total", barWidth: 22, itemStyle: { color: '#F7B500' }, itemStyle: { color: "#F7B500" }, data: scrap, }, { name: '产出量', type: 'bar', stack: 'total', name: "产出量", type: "bar", stack: "total", barWidth: 22, itemStyle: { color: '#19C6C6', borderRadius: [0, 6, 6, 0] }, itemStyle: { color: "#19C6C6", borderRadius: [0, 6, 6, 0] }, data: output, }, ] }) ]; }); const processAside = computed(() => { const list = processChartData.value const item = list[activeProcessIndex.value] || {} const list = processChartData.value; const item = list[activeProcessIndex.value] || {}; return { processName: item.name || '暂无数据', processName: item.name || "暂无数据", totalInput: item.input || 0, totalScrap: item.scrap || 0, totalOutput: item.output || 0, } }) }; }); const formatAmount = (n) => { const num = Number(n || 0) return num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) } const formatAmount = n => { const num = Number(n || 0); return num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2, }); }; const refreshProcessStats = () => { processDataProductionStatistics({ type: processRange.value, processIds: selectedProcessIds.value.length > 0 ? selectedProcessIds.value.join(',') : null processIds: selectedProcessIds.value.length > 0 ? selectedProcessIds.value.join(",") : null, }).then(res => { processChartData.value = res.data.map(item => ({ name: item.processName, input: item.totalInput, scrap: item.totalScrap, output: item.totalOutput })) processYAxis.value[0].data = processChartData.value.map((i) => i.name) activeProcessIndex.value = 0 }) } output: item.totalOutput, })); processYAxis.value[0].data = processChartData.value.map(i => i.name); activeProcessIndex.value = 0; }); }; onMounted(() => { getBusinessData() analysisCustomer() todoInfoS() statisticsReceivable() qualityStatisticsInfo() getAmountHalfYearNum() refreshProcessStats() }) getBusinessData(); analysisCustomer(); todoInfoS(); statisticsReceivable(); qualityStatisticsInfo(); getAmountHalfYearNum(); refreshProcessStats(); }); </script> <style scoped> @@ -752,7 +828,12 @@ .welcome-banner { padding: 10px 10px; background: linear-gradient(135deg, rgba(229, 240, 255, 0.9), rgba(214, 232, 255, 0.7), rgba(207, 236, 255, 0.9)); background: linear-gradient( 135deg, rgba(229, 240, 255, 0.9), rgba(214, 232, 255, 0.7), rgba(207, 236, 255, 0.9) ); } .welcome-title { @@ -839,7 +920,7 @@ .data-title { font-weight: 700; font-size: 26px; color: #FFFFFF; color: #ffffff; } .data-num { @@ -885,14 +966,14 @@ .data-desc { font-weight: 500; font-size: 13px; color: #FFFFFF; color: #ffffff; } .data-value { font-size: 18px; font-weight: 500; margin: 10px 0; color: #FFFFFF; color: #ffffff; } .top-left { @@ -939,7 +1020,7 @@ } .todo-title::before { content: ''; content: ""; /* 必需,表示这里有一个内容 */ position: absolute; left: -10px; @@ -952,7 +1033,7 @@ /* 圆的直径 */ height: 6px; /* 圆的直径 */ background: #498CEB; background: #498ceb; border-radius: 50%; /* 让其变成圆形 */ } @@ -1003,10 +1084,10 @@ position: absolute; left: 0; top: 4px; content: ''; content: ""; width: 4px; height: 18px; background-color: #002FA7; background-color: #002fa7; border-radius: 2px; } @@ -1072,13 +1153,13 @@ } .line::after { content: ''; content: ""; position: absolute; right: 2px; top: 0; bottom: 0; width: 1px; background-color: #C9C5C5; background-color: #c9c5c5; border-radius: 2px; } @@ -1115,7 +1196,6 @@ .quality-card.three { background-image: url("../assets/images/chuchang.png"); } .quality-card span { @@ -1184,15 +1264,15 @@ } .dot-blue { background: #1E5BFF; background: #1e5bff; } .dot-yellow { background: #F7B500; background: #f7b500; } .dot-teal { background: #19C6C6; background: #19c6c6; } .process-card { @@ -1203,7 +1283,7 @@ .process-card--name { background: rgba(235, 242, 255, 1); color: #1E5BFF; color: #1e5bff; font-weight: 800; font-size: 14px; } src/views/procurementManagement/procurementLedger/index.vue
@@ -33,6 +33,18 @@ prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="供应商类型:"> <el-select v-model="searchForm.supplierType" placeholder="请选择" style="width: 220px" clearable @change="handleQuery"> <el-option label="对公" value="1" /> <el-option label="对私" value="2" /> </el-select> </el-form-item> <el-form-item label="录入日期:"> <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" @@ -53,7 +65,9 @@ <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;"> <el-button type="primary" @click="openForm('add')">新增台账</el-button> <el-button type="primary" plain @click="handleImport">导入</el-button> <el-button type="primary" plain @click="handleImport">导入</el-button> <el-button @click="handleOut">导出</el-button> <el-button type="danger" plain @@ -94,18 +108,29 @@ prop="availableQuality" /> <el-table-column label="退货数量" prop="returnQuality" /> <el-table-column label="税率(%)" prop="taxRate" /> prop="taxRate" v-if="props.row.supplierType === 1" /> <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> :formatter="formattedNumber" v-if="props.row.supplierType === 1" /> <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> :formatter="formattedNumber" v-if="props.row.supplierType === 1" /> <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> :formatter="formattedNumber" v-if="props.row.supplierType === 1" /> <el-table-column label="单价(对私)" prop="unitPrice" :formatter="formattedNumber" v-if="props.row.supplierType === 2" /> <el-table-column label="总价(对私)" prop="totalPrice" :formatter="formattedNumber" v-if="props.row.supplierType === 2" /> </el-table> </template> </el-table-column> @@ -125,6 +150,14 @@ prop="supplierName" width="160" show-overflow-tooltip /> <el-table-column label="供应商类型" prop="supplierType" width="100" show-overflow-tooltip> <template #default="scope"> {{ scope.row.supplierType === 1 ? '对公' : '对私' }} </template> </el-table-column> <el-table-column label="项目名称" prop="projectName" width="320" @@ -134,8 +167,7 @@ width="100" show-overflow-tooltip> <template #default="scope"> <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small"> {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }} </el-tag> @@ -232,11 +264,12 @@ <el-select v-model="form.supplierId" placeholder="请选择" filterable clearable> clearable @change="handleSupplierChange"> <el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id" >{{item.supplierName + '---' + item.supplierType}}</el-option> :value="item.id">{{item.supplierName + '---' + (item.supplierType === 1 ? item.taxpayerIdentificationNum || '无' : '对私')}}</el-option> </el-select> </el-form-item> </el-col> @@ -304,38 +337,33 @@ <template #label> <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;"> <span>审批人选择:</span> <el-button type="primary" size="small" @click="addApproverNode" icon="Plus">新增节点</el-button> <el-button type="primary" size="small" @click="addApproverNode" icon="Plus">新增节点</el-button> </div> </template> <div class="approver-nodes-container"> <div v-for="(node, index) in approverNodes" <div v-for="(node, index) in approverNodes" :key="node.id" class="approver-node-item" > class="approver-node-item"> <div class="approver-node-header"> <span class="approver-node-label">审批节点 {{ index + 1 }}</span> <el-button v-if="approverNodes.length > 1" <el-button v-if="approverNodes.length > 1" type="danger" size="small" text @click="removeApproverNode(index)" icon="Delete" >删除</el-button> icon="Delete">删除</el-button> </div> <el-select v-model="node.userId" <el-select v-model="node.userId" placeholder="请选择审批人" filterable style="width: 100%;" > <el-option v-for="user in userList" style="width: 100%;"> <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" /> :value="user.userId" /> </el-select> </div> </div> @@ -346,6 +374,7 @@ <el-form-item label="产品信息:" prop="entryDate"> <el-button type="primary" v-if="currentSupplierType" @click="openProductForm('add')">添加</el-button> <el-button plain type="danger" @@ -373,8 +402,7 @@ :value="item.templateName"> <div style="display: flex; justify-content: space-between; align-items: center;"> <span>{{ item.templateName }}</span> <el-icon v-if="item.id" <el-icon v-if="item.id" class="delete-icon" @click.stop="handleDeleteTemplate(item)" style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;"> @@ -420,19 +448,33 @@ show-overflow-tooltip /> <el-table-column label="税率(%)" prop="taxRate" width="80" /> width="80" v-if="currentSupplierType === 1" /> <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" /> width="150" v-if="currentSupplierType === 1" /> <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" /> width="150" v-if="currentSupplierType === 1" /> <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="150" /> width="150" v-if="currentSupplierType === 1" /> <el-table-column label="单价(对私)" prop="unitPrice" :formatter="formattedNumber" width="150" v-if="currentSupplierType === 2" /> <el-table-column label="总价(对私)" prop="totalPrice" :formatter="formattedNumber" width="150" v-if="currentSupplierType === 2" /> <el-table-column label="是否质检" prop="isChecked" width="150"> @@ -493,16 +535,13 @@ </el-form> </FormDialog> <!-- 导入弹窗 --> <FormDialog v-model="importUpload.open" <FormDialog v-model="importUpload.open" :title="importUpload.title" :width="'600px'" @close="importUpload.open = false" @confirm="submitImportFile" @cancel="importUpload.open = false" > <el-upload ref="importUploadRef" @cancel="importUpload.open = false"> <el-upload ref="importUploadRef" :limit="1" accept=".xlsx,.xls" :action="importUpload.url" @@ -513,8 +552,7 @@ :on-progress="importUpload.onProgress" :on-change="importUpload.onChange" :auto-upload="false" drag > drag> <i class="el-icon-upload"></i> <div class="el-upload__text"> 将文件拖到此处,或<em>点击上传</em> @@ -522,7 +560,9 @@ <template #tip> <div class="el-upload__tip"> 仅支持 xls/xlsx,大小不超过 10MB。 <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button> <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button> </div> </template> </el-upload> @@ -537,7 +577,7 @@ <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" :rules="getProductRules()" ref="productFormRef"> <el-row :gutter="30"> <el-col :span="24"> @@ -581,7 +621,8 @@ </el-col> <el-col :span="12"> <el-form-item label="税率(%):" prop="taxRate"> prop="taxRate" v-if="currentSupplierType === 1"> <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @@ -598,18 +639,6 @@ </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice"> <el-input-number v-model="productForm.taxInclusiveUnitPrice" :precision="2" :step="0.1" :min="0" clearable style="width: 100%" @change="mathNum" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="数量:" prop="quantity"> <el-input-number :step="0.1" @@ -619,14 +648,39 @@ style="width: 100%" v-model="productForm.quantity" placeholder="请输入" @change="currentSupplierType === 1 ? mathNum() : mathNumPrivate()" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice" v-if="currentSupplierType === 1"> <el-input-number v-model="productForm.taxInclusiveUnitPrice" :precision="2" :step="0.1" :min="0" clearable style="width: 100%" @change="mathNum" /> </el-form-item> <el-form-item label="单价(对私):" prop="unitPrice" v-else> <el-input-number v-model="productForm.unitPrice" :precision="2" :step="0.1" :min="0" clearable style="width: 100%" @change="mathNumPrivate" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice"> prop="taxInclusiveTotalPrice" v-if="currentSupplierType === 1"> <el-input-number v-model="productForm.taxInclusiveTotalPrice" :precision="2" :step="0.1" @@ -635,10 +689,34 @@ style="width: 100%" @change="reverseMathNum('taxInclusiveTotalPrice')" /> </el-form-item> <el-form-item label="总价(对私):" prop="totalPrice" v-else> <el-input-number v-model="productForm.totalPrice" :precision="2" :step="0.1" :min="0" clearable style="width: 100%" @change="reverseMathNumPrivate('totalPrice')" /> </el-form-item> </el-col> <el-col v-if="currentSupplierType === 2" :span="12"> <el-form-item label="库存预警数量:" prop="warnNum"> <el-input-number v-model="productForm.warnNum" :precision="2" :step="0.1" :min="0" clearable style="width: 100%" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice"> prop="taxExclusiveTotalPrice" v-if="currentSupplierType === 1"> <el-input-number v-model="productForm.taxExclusiveTotalPrice" :precision="2" :step="0.1" @@ -652,7 +730,8 @@ <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="发票类型:" prop="invoiceType"> prop="invoiceType" v-if="currentSupplierType === 1"> <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable> @@ -663,7 +742,8 @@ </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-col v-if="currentSupplierType === 1" :span="12"> <el-form-item label="库存预警数量:" prop="warnNum"> <el-input-number v-model="productForm.warnNum" @@ -690,11 +770,9 @@ </el-row> </el-form> </FormDialog> <FileListDialog ref="fileListRef" <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" title="附件列表" /> title="附件列表" /> </div> </template> @@ -712,8 +790,8 @@ import { Search, Delete } from "@element-plus/icons-vue"; import { ElMessageBox, ElMessage } from "element-plus"; import { userListNoPage } from "@/api/system/user.js"; import FormDialog from '@/components/Dialog/FormDialog.vue'; import FileListDialog from '@/components/Dialog/FileListDialog.vue'; import FormDialog from "@/components/Dialog/FormDialog.vue"; import FileListDialog from "@/components/Dialog/FileListDialog.vue"; import { getSalesLedgerWithProducts, addOrUpdateSalesLedgerProduct, @@ -748,6 +826,7 @@ const salesContractList = ref([]); const supplierList = ref([]); const tableLoading = ref(false); const currentSupplierType = ref(null); // 跟踪当前选择的供应商类型 const page = reactive({ current: 1, size: 100, @@ -766,11 +845,31 @@ const addApproverNode = () => { approverNodes.value.push({ id: nextApproverId++, userId: null }); }; const removeApproverNode = (index) => { const removeApproverNode = index => { approverNodes.value.splice(index, 1); }; // 订单审批状态显示文本 // 处理供应商选择变化 const handleSupplierChange = async supplierId => { const selectedSupplier = supplierList.value.find( item => item.id === supplierId ); if (selectedSupplier) { // 只有当当前供应商类型不为 null 且发生变化时,才清空产品表 // 这样在编辑时加载数据后调用该函数不会清空产品数据 if ( currentSupplierType.value !== null && currentSupplierType.value !== selectedSupplier.supplierType ) { productData.value = []; } currentSupplierType.value = selectedSupplier.supplierType; } else { currentSupplierType.value = null; productData.value = []; } await getTemplateList(); }; const approvalStatusText = { 1: "待审核", 2: "审批中", @@ -779,7 +878,7 @@ }; // 获取审批状态标签类型 const getApprovalStatusType = (status) => { const getApprovalStatusType = status => { const typeMap = { 1: "info", // 待审核 - 灰色 2: "warning", // 审批中 - 橙色 @@ -866,7 +965,8 @@ form.value.paymentMethod = matchedTemplate.paymentMethod; } // 模板数据中的产品字段是 productList,需要转换为 productData productData.value = matchedTemplate.productList || matchedTemplate.productData || []; productData.value = matchedTemplate.productList || matchedTemplate.productData || []; } else { // 未匹配到已有模板,视为新模板 currentTemplateId.value = null; @@ -902,6 +1002,7 @@ entryDate: null, // 录入日期 entryDateStart: undefined, entryDateEnd: undefined, supplierType: "", // 供应商类型 }, form: { purchaseContractNumber: "", @@ -985,6 +1086,43 @@ }, }); const { productForm, productRules } = toRefs(productFormData); const getProductRules = () => { const baseRules = { productId: [{ required: true, message: "请选择", trigger: "change" }], productModelId: [{ required: true, message: "请选择", trigger: "change" }], unit: [{ required: true, message: "请输入", trigger: "blur" }], quantity: [{ required: true, message: "请输入", trigger: "blur" }], warnNum: [{ required: true, message: "请选择", trigger: "change" }], isChecked: [{ required: true, message: "请选择", trigger: "change" }], }; if (currentSupplierType.value === 1) { // 对公供应商需要验证的字段 return { ...baseRules, taxInclusiveUnitPrice: [ { required: true, message: "请输入", trigger: "blur" }, ], taxRate: [{ required: true, message: "请选择", trigger: "change" }], taxInclusiveTotalPrice: [ { required: true, message: "请输入", trigger: "blur" }, ], taxExclusiveTotalPrice: [ { required: true, message: "请输入", trigger: "blur" }, ], invoiceType: [{ required: true, message: "请选择", trigger: "change" }], }; } else if (currentSupplierType.value === 2) { // 对私供应商需要验证的字段 return { ...baseRules, unitPrice: [{ required: true, message: "请输入", trigger: "blur" }], totalPrice: [{ required: true, message: "请输入", trigger: "blur" }], }; } else { return baseRules; } }; const upload = reactive({ // 上传的地址 url: import.meta.env.VITE_APP_BASE_API + "/file/upload", @@ -1000,7 +1138,7 @@ url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import", headers: { Authorization: "Bearer " + getToken() }, isUploading: false, beforeUpload: (file) => { beforeUpload: file => { const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls"); const isLt10M = file.size / 1024 / 1024 < 10; if (!isExcel) { @@ -1049,7 +1187,11 @@ // 下载导入模板(如后端路径不同,可在此处调整) const downloadTemplate = () => { proxy.download("/purchase/ledger/exportTemplate", {}, "采购台账导入模板.xlsx"); proxy.download( "/purchase/ledger/exportTemplate", {}, "采购台账导入模板.xlsx" ); }; const submitImportFile = () => { @@ -1114,8 +1256,8 @@ // 检查是否有产品数据 if (!productData.value || productData.value.length === 0) { ElMessage({ message: '请先添加产品信息', type: 'warning', message: "请先添加产品信息", type: "warning", }); return; } @@ -1136,7 +1278,12 @@ approveUserIds: approveUserIds, templateName: templateName.value.trim(), }; console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value); console.log( "template params ===>", params, "currentTemplateId:", currentTemplateId.value ); // 如果 currentTemplateId 有值,说明当前是“编辑已有模板” → 调用更新接口 // 否则为“新建模板” → 调用新增接口 @@ -1147,7 +1294,10 @@ ...params, }); } else { res = await addPurchaseTemplate(params); res = await addPurchaseTemplate({ ...params, templateType: currentSupplierType.value, }); } if (res && res.code === 200) { @@ -1179,22 +1329,34 @@ }; // 子表合计方法 const summarizeChildrenTable = param => { return proxy.summarizeTable( param, [ "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", // 检查是否有数据,以及数据的供应商类型 const hasData = param && param.data && param.data.length > 0; const supplierType = hasData ? param.data[0].supplierType : null; // 根据供应商类型确定要合计的字段 const fields = [ "ticketsNum", "ticketsAmount", "futureTickets", "futureTicketsAmount", ], { ]; if (supplierType === 1) { // 对公供应商 fields.unshift( "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice" ); } else if (supplierType === 2) { // 对私供应商 fields.unshift("unitPrice", "totalPrice"); } return proxy.summarizeTable(param, fields, { ticketsNum: { noDecimal: true }, // 不保留小数 futureTickets: { noDecimal: true }, // 不保留小数 } ); }); }; const paginationChange = obj => { page.current = obj.page; @@ -1238,7 +1400,12 @@ const res = await productList({ salesLedgerId: row.id, type: 2 }); const index = tableData.value.findIndex(item => item.id === row.id); if (index > -1) { tableData.value[index].children = res.data || []; // 为子表格数据添加供应商类型信息 const children = (res.data || []).map(item => ({ ...item, supplierType: row.supplierType, // 从父行获取供应商类型 })); tableData.value[index].children = children; expandedRowKeys.value.push(row.id); } } catch (error) { @@ -1260,11 +1427,17 @@ }; // 子表合计方法 const summarizeProTable = param => { return proxy.summarizeTable(param, [ const fields = []; if (currentSupplierType.value === 1) { fields.push( "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); "taxExclusiveTotalPrice" ); } else if (currentSupplierType.value === 2) { fields.push("unitPrice", "totalPrice"); } return proxy.summarizeTable(param, fields); }; // 打开弹框 const openForm = async (type, row) => { @@ -1275,8 +1448,8 @@ return; } } currentSupplierType.value = null; await getTemplateList(); operationType.value = type; form.value = {}; productData.value = []; @@ -1307,6 +1480,7 @@ form.value.entryDate = getCurrentDate(); if (type === "add") { await getTemplateList(); // 新增时生成采购合同号 try { const purchaseNoRes = await createPurchaseNo(); @@ -1330,9 +1504,13 @@ const approverIds = purchaseRes.approveUserIds.split(","); approverNodes.value = approverIds.map((id, index) => ({ id: index + 1, userId: Number(id) userId: Number(id), })); nextApproverId = approverIds.length + 1; } // 设置当前供应商类型 if (form.value.supplierId) { handleSupplierChange(form.value.supplierId); } } catch (error) { console.error("加载采购台账数据失败:", error); @@ -1408,7 +1586,9 @@ proxy.$modal.msgError("请为所有审批节点选择审批人!"); return; } const approveUserIds = approverNodes.value.map(node => node.userId).join(","); const approveUserIds = approverNodes.value .map(node => node.userId) .join(","); if (productData.value.length > 0) { // 新增时,需要从每个产品对象中删除 id 字段 @@ -1438,7 +1618,10 @@ } // 新增时不传递id const submitData = { ...form.value }; const submitData = { ...form.value, purchaseType: currentSupplierType.value, }; if (operationType.value === "add") { delete submitData.id; } @@ -1463,7 +1646,26 @@ const openProductForm = async (type, row, index) => { productOperationType.value = type; productOperationIndex.value = index; productForm.value = {}; productForm.value = { productId: "", productCategory: "", productModelId: "", specificationModel: "", unit: "", quantity: "", // 对公字段 taxInclusiveUnitPrice: "", taxRate: "", taxInclusiveTotalPrice: "", taxExclusiveTotalPrice: "", invoiceType: "", // 对私字段 unitPrice: "", totalPrice: "", // 公共字段 warnNum: "", isChecked: true, }; proxy.resetForm("productFormRef"); productFormVisible.value = true; @@ -1487,14 +1689,20 @@ return nodes[i].value; } if (nodes[i].children && nodes[i].children.length > 0) { const found = findProductIdByCategory(nodes[i].children, categoryName); const found = findProductIdByCategory( nodes[i].children, categoryName ); if (found) return found; } } return null; }; const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory); const productId = findProductIdByCategory( productOptions.value, productForm.value.productCategory ); if (productId) { productForm.value.productId = productId; // 获取型号列表并等待完成 @@ -1505,7 +1713,10 @@ await nextTick(); // 根据 specificationModel 查找 productModelId if (productForm.value.specificationModel && modelOptions.value.length > 0) { if ( productForm.value.specificationModel && modelOptions.value.length > 0 ) { const modelItem = modelOptions.value.find( item => item.model === productForm.value.specificationModel ); @@ -1611,15 +1822,21 @@ }); }; const submitProductEdit = () => { productForm.value.salesLedgerId = currentId.value; productForm.value.type = 2; addOrUpdateSalesLedgerProduct(productForm.value).then(res => { proxy.$modal.msgSuccess("提交成功"); // 直接修改本地数据,不再调用接口 if (productOperationType.value === "add") { // 新增产品 productData.value.push({ ...productForm.value }); } else if (productOperationType.value === "edit") { // 编辑产品 if ( productOperationIndex.value !== "" && productOperationIndex.value !== null ) { productData.value[productOperationIndex.value] = { ...productForm.value }; } } proxy.$modal.msgSuccess("操作成功"); closeProductDia(); getPurchaseById({ id: currentId.value, type: 2 }).then(res => { productData.value = res.productData; }); }); }; // 删除产品 const deleteProduct = () => { @@ -1627,7 +1844,13 @@ proxy.$modal.msgWarning("请选择数据"); return; } if (operationType.value === "add") { // 直接从本地数据中删除,不再调用接口 ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", { confirmButtonText: "确认", cancelButtonText: "取消", type: "warning", }) .then(() => { productSelectedRows.value.forEach(selectedRow => { const index = productData.value.findIndex( product => product.id === selectedRow.id @@ -1636,31 +1859,12 @@ productData.value.splice(index, 1); } }); } else { let ids = []; if (productSelectedRows.value.length > 0) { ids = productSelectedRows.value.map(item => item.id); } ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", { confirmButtonText: "确认", cancelButtonText: "取消", type: "warning", }) .then(() => { delProduct(ids).then(res => { proxy.$modal.msgSuccess("删除成功"); closeProductDia(); getPurchaseById({ id: currentId.value, type: 2 }).then( res => { productData.value = res.productData; } ); }); }) .catch(() => { proxy.$modal.msg("已取消"); }); } }; // 关闭产品弹框 const closeProductDia = () => { @@ -1826,6 +2030,27 @@ } } }; // 对私供应商的价格计算 const mathNumPrivate = () => { const { unitPrice, quantity } = productForm.value; if (unitPrice && quantity) { productForm.value.totalPrice = (unitPrice * quantity).toFixed(2); } }; // 对私供应商的反向价格计算 const reverseMathNumPrivate = type => { const { unitPrice, quantity, totalPrice } = productForm.value; if (type === "totalPrice") { if (quantity) { productForm.value.unitPrice = (totalPrice / quantity).toFixed(2); } else if (unitPrice) { productForm.value.quantity = (totalPrice / unitPrice).toFixed(2); } } }; // 销售合同选择改变方法 const salesLedgerChange = async row => { console.log("row", row); @@ -1855,14 +2080,21 @@ // 获取模板信息 const getTemplateList = async () => { let res = await getPurchaseTemplateList(); console.log("currentSupplierType.value", currentSupplierType.value); if (currentSupplierType.value) { let res = await getPurchaseTemplateList({ templateType: currentSupplierType.value, }); if (res && res.code === 200 && Array.isArray(res.data)) { templateList.value = res.data; } } else { templateList.value = []; } }; // 删除模板 const handleDeleteTemplate = async (item) => { const handleDeleteTemplate = async item => { if (!item.id) { proxy.$modal.msgWarning("无法删除该模板"); return; src/views/productionManagement/productionCosting/index.vue
@@ -1,21 +1,29 @@ <template> <div class="app-container"> <el-row :gutter="16" class="content-row"> <el-row :gutter="16" class="content-row"> <!-- 左侧台账 + 顶部筛选 --> <el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8" class="left-col"> <el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8" class="left-col"> <div class="left-panel"> <div class="left-header"> <el-form :model="searchForm" inline> <el-form :model="searchForm" inline> <el-form-item prop="dateType"> <el-radio-group v-model="searchForm.dateType" size="small" @change="handleDateTypeChange"> <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" <el-form-item label="日期:" prop="dateRange"> <el-date-picker v-model="searchForm.dateRange" :type="searchForm.dateType === 'day' ? 'date' : 'daterange'" range-separator="至" start-placeholder="开始日期" @@ -23,41 +31,40 @@ format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 200px" @change="handleDateRangeChange" /> @change="handleDateRangeChange" /> </el-form-item> </el-form> </div> <PIMTable rowKey="id" <PIMTable rowKey="id" :column="leftTableColumn" :tableData="leftTableData" :tableLoading="tableLoading" :page="page" @row-click="handleLeftRowClick" @pagination="pagination" ></PIMTable> @pagination="pagination"></PIMTable> </div> </el-col> <!-- 右侧明细 --> <el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16" class="right-col"> <el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16" class="right-col"> <div class="right-panel"> <el-form inline> <el-form-item> <el-button type="primary" @click="handleOut">导出</el-button> <el-button type="primary" @click="handleOut">导出</el-button> </el-form-item> </el-form> <PIMTable rowKey="id" <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page1" :tableLoading="tableLoading1" style="margin-right: 20px;" @pagination="pagination1" ></PIMTable> @pagination="pagination1"></PIMTable> </div> </el-col> </el-row> @@ -68,7 +75,10 @@ import {onMounted, ref} from "vue"; import { ElMessageBox } from "element-plus"; import dayjs from "dayjs"; import {salesLedgerProductionAccountingListProductionDetails, salesLedgerProductionAccountingList} from "@/api/productionManagement/productionCosting.js"; import { salesLedgerProductionAccountingListProductionDetails, salesLedgerProductionAccountingList, } from "@/api/productionManagement/productionCosting.js"; const { proxy } = getCurrentInstance(); const tableColumn = ref([ @@ -82,16 +92,16 @@ prop: "schedulingUserName", minWidth: 100, }, { label: "合同号", prop: "salesContractNo", minWidth: 100, }, { label: "客户名称", prop: "customerName", minWidth: 100, }, // { // label: "合同号", // prop: "salesContractNo", // minWidth: 100, // }, // { // label: "客户名称", // prop: "customerName", // minWidth: 100, // }, { label: "产品大类", prop: "productName", @@ -140,21 +150,19 @@ label: "产量", prop: "outputNum", minWidth: 100, }, { label: "工资", prop: "wages", minWidth: 100, }, { label: "合格率", prop: "outputRate", minWidth: 100, formatData: (val) => { if (val == null || val === '') return '-' return parseFloat(val).toFixed(2) formatData: val => { if (val == null || val === "") return "-"; return parseFloat(val).toFixed(2); }, }, ]); @@ -189,19 +197,19 @@ }); const { searchForm } = toRefs(data); const pagination = (obj) => { const pagination = obj => { page.current = obj.page; page.size = obj.limit; getList(); }; const pagination1 = (obj) => { const pagination1 = obj => { page1.current = obj.page; page1.size = obj.limit; getList1(); }; const handleDateRangeChange = (value) => { const handleDateRangeChange = value => { if (value) { if (searchForm.value.dateType === "day") { searchForm.value.entryDate = value; @@ -209,46 +217,46 @@ 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() reloadData(); }; const getList = () => { tableLoading.value = true; const params = { ...searchForm.value, ...page }; salesLedgerProductionAccountingList(params).then((res) => { salesLedgerProductionAccountingList(params) .then(res => { const records = res.data.records || []; leftTableData.value = records; page.total = res.data.total || 0; }).finally(() => { tableLoading.value = false; }) .finally(() => { tableLoading.value = false; }); }; const getList1 = () => { tableLoading1.value = true; const params = { ...page1, ...searchForm.value }; salesLedgerProductionAccountingListProductionDetails(params).then((res) => { tableData.value = res.data.records || [];; salesLedgerProductionAccountingListProductionDetails(params) .then(res => { tableData.value = res.data.records || []; page1.total = res.data.total || 0; }).finally(() => { tableLoading1.value = false; }) .finally(() => { tableLoading1.value = false; }); }; // 构建左侧汇总台账(按生产人汇总产量、工资等) const buildLeftTableData = (records) => { const buildLeftTableData = records => { const map = {}; records.forEach((item) => { records.forEach(item => { const key = item.schedulingUserName || "未知"; if (!map[key]) { map[key] = { @@ -269,29 +277,34 @@ }; // 左侧日/月切换 const handleDateTypeChange = (value) => { const handleDateTypeChange = value => { // 这里只作为筛选条件的一部分,直接重新查询列表 if (value === "day") { searchForm.value.entryDate = dayjs().format("YYYY-MM-DD"); searchForm.value.dateRange = searchForm.value.entryDate searchForm.value.dateRange = searchForm.value.entryDate; } else { searchForm.value.entryDateStart = dayjs().startOf("month").format("YYYY-MM-DD"); 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] searchForm.value.dateRange = [ searchForm.value.entryDateStart, searchForm.value.entryDateEnd, ]; } reloadData() reloadData(); }; const reloadData = () => { page.current = 1; page1.current = 1; getList(); tableData.value = [] } tableData.value = []; }; // 点击左侧行,刷右侧明细(按生产人过滤) const handleLeftRowClick = (row) => { const handleLeftRowClick = row => { searchForm.value.schedulingUserName = row.schedulingUserName || ""; handleQuery(); }; @@ -303,7 +316,6 @@ getList1(); }; // 导出 const handleOut = () => { ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", { @@ -312,7 +324,11 @@ type: "warning", }) .then(() => { proxy.download("/salesLedger/productionAccounting/export", {}, "生产核算.xlsx"); proxy.download( "/salesLedger/productionAccounting/export", {}, "生产核算.xlsx" ); }) .catch(() => { proxy.$modal.msg("已取消"); src/views/productionManagement/productionOrder/index.vue
@@ -11,14 +11,14 @@ style="width: 160px;" @change="handleQuery" /> </el-form-item> <el-form-item label="合同号:"> <!-- <el-form-item label="合同号:"> <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" style="width: 160px;" @change="handleQuery" /> </el-form-item> </el-form-item> --> <el-form-item label="产品名称:"> <el-input v-model="searchForm.productCategory" placeholder="请输入" @@ -41,8 +41,10 @@ </el-form-item> </el-form> <div> <el-button type="primary" @click="isShowNewModal = true">新增</el-button> <el-button type="danger" @click="handleDelete">删除</el-button> <el-button type="primary" @click="isShowNewModal = true">新增</el-button> <el-button type="danger" @click="handleDelete">删除</el-button> <el-button @click="handleOut">导出</el-button> </div> </div> @@ -57,11 +59,9 @@ @selection-change="handleSelectionChange" @pagination="pagination"> <template #completionStatus="{ row }"> <el-progress :percentage="toProgressPercentage(row?.completionStatus)" <el-progress :percentage="toProgressPercentage(row?.completionStatus)" :color="progressColor(toProgressPercentage(row?.completionStatus))" :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" /> :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" /> </template> </PIMTable> </div> @@ -90,7 +90,6 @@ </span> </template> </el-dialog> <new-product-order v-if="isShowNewModal" v-model:visible="isShowNewModal" @completed="handleQuery" /> @@ -106,12 +105,15 @@ productOrderListPage, listProcessRoute, bindingRoute, listProcessBom, delProductOrder, listProcessBom, delProductOrder, } from "@/api/productionManagement/productionOrder.js"; import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js"; import {fileDel} from "@/api/financialManagement/revenueManagement.js"; import PIMTable from "@/components/PIMTable/PIMTable.vue"; const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue")); const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue") ); const { proxy } = getCurrentInstance(); @@ -122,32 +124,32 @@ { label: "生产订单号", prop: "npsNo", width: '120px', width: "120px", }, { label: "销售合同号", prop: "salesContractNo", width: '150px', }, { label: "客户名称", prop: "customerName", width: '200px', }, // { // label: "销售合同号", // prop: "salesContractNo", // width: "150px", // }, // { // label: "客户名称", // prop: "customerName", // width: "200px", // }, { label: "产品名称", prop: "productCategory", width: '120px', width: "120px", }, { label: "规格", prop: "specificationModel", width: '120px', width: "120px", }, { label: "工艺路线编号", prop: "processRouteCode", width: '200px', width: "200px", }, { label: "需求数量", @@ -176,12 +178,12 @@ formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), width: 120, }, { label: "交付日期", prop: "deliveryDate", formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), width: 120, }, // { // label: "交付日期", // prop: "deliveryDate", // formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), // width: 120, // }, { dataType: "action", label: "操作", @@ -253,18 +255,18 @@ // 添加表行类名方法 const tableRowClassName = ({ row }) => { if (!row.deliveryDate) return ''; if (row.isFh) return ''; if (!row.deliveryDate) return ""; if (row.isFh) return ""; const diff = row.deliveryDaysDiff; if (diff === 15) { return 'yellow'; return "yellow"; } else if (diff === 10) { return 'pink'; return "pink"; } else if (diff === 2) { return 'purple'; return "purple"; } else if (diff < 2) { return 'red'; return "red"; } }; @@ -402,14 +404,14 @@ }; // 表格选择数据 const handleSelectionChange = (selection) => { const handleSelectionChange = selection => { selectedRows.value = selection; }; const handleDelete = () => { let ids = []; if (selectedRows.value.length > 0) { ids = selectedRows.value.map((item) => item.id); ids = selectedRows.value.map(item => item.id); } else { proxy.$modal.msgWarning("请选择数据"); return; @@ -418,12 +420,14 @@ confirmButtonText: "确认", cancelButtonText: "取消", type: "warning", }).then(() => { delProductOrder(ids).then((res) => { }) .then(() => { delProductOrder(ids).then(res => { proxy.$modal.msgSuccess("删除成功"); getList(); }); }).catch(() => { }) .catch(() => { proxy.$modal.msg("已取消"); }); }; @@ -436,7 +440,11 @@ type: "warning", }) .then(() => { proxy.download("/productOrder/export", {...searchForm.value}, "生产订单.xlsx"); proxy.download( "/productOrder/export", { ...searchForm.value }, "生产订单.xlsx" ); }) .catch(() => { proxy.$modal.msg("已取消"); @@ -456,11 +464,11 @@ } ::v-deep .yellow { background-color: #FAF0DE; background-color: #faf0de; } ::v-deep .pink { background-color: #FAE1DE; background-color: #fae1de; } ::v-deep .red { @@ -468,6 +476,6 @@ } ::v-deep .purple{ background-color: #F4DEFA; background-color: #f4defa; } </style> src/views/productionManagement/productionReporting/index.vue
@@ -99,8 +99,7 @@ style="width: 100%" /> </template> </el-table-column> <el-table-column label="操作" > <el-table-column label="操作"> <template #default="scope"> <el-button link type="primary" @@ -172,11 +171,11 @@ prop: "workOrderNo", width: 120, }, { label: "销售合同号", prop: "salesContractNo", width: 120, }, // { // label: "销售合同号", // prop: "salesContractNo", // width: 120, // }, { label: "产品名称", prop: "productName", src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -142,6 +142,8 @@ :data="salesRecord" border :row-key="(row) => row.id" show-summary :summary-method="summarizeSalesTable" height="calc(100vh - 18.5em)" > <el-table-column align="center" label="序号" type="index" width="60" /> @@ -247,6 +249,18 @@ return summarizeTable; }; // 销售往来表合计(已发货/未发货) const summarizeSalesTable = (param) => { return proxy.summarizeTable( param, ["shippedQuantity", "unshippedQuantity"], { shippedQuantity: { noDecimal: false }, unshippedQuantity: { noDecimal: false }, } ); }; const receiptPaymentList = (id) => { const param = { customerId: id, src/views/salesManagement/salesLedger/index.vue
@@ -1,25 +1,53 @@ <template> <div class="app-container"> <div class="search_form"> <el-form :model="searchForm" :inline="true"> <el-form :model="searchForm" :inline="true"> <el-form-item label="客户名称:"> <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search" <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.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="项目名称:"> <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search" <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="客户类型:"> <el-select v-model="searchForm.customerType" placeholder="请选择" style="width: 220px" clearable @change="handleQuery"> <el-option label="对公" value="1" /> <el-option label="对私" value="2" /> </el-select> </el-form-item> <el-form-item label="录入日期:"> <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" placeholder="请选择" clearable @change="changeDaterange" /> <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" placeholder="请选择" clearable @change="changeDaterange" /> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery"> 搜索 </el-button> <el-button type="primary" @click="handleQuery"> 搜索 </el-button> </el-form-item> </el-form> </div> @@ -27,52 +55,92 @@ <div class="actions"> <div></div> <div> <el-button type="primary" @click="openForm('add')"> <el-button type="primary" @click="openForm('add')"> 新增台账 </el-button> <el-button type="primary" plain @click="handleImport">导入</el-button> <el-button type="primary" plain @click="handleImport">导入</el-button> <el-button @click="handleOut">导出</el-button> <el-button type="danger" plain @click="handleDelete">删除</el-button> <el-button type="primary" plain @click="handlePrint">打印</el-button> <el-button type="danger" plain @click="handleDelete">删除</el-button> <el-button type="primary" plain @click="handlePrint">打印</el-button> </div> </div> <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%" :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)"> <el-table-column align="center" type="selection" width="55" fixed="left"/> <el-table-column type="expand" width="60" fixed="left"> <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%" :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)"> <el-table-column align="center" type="selection" width="55" fixed="left" /> <el-table-column type="expand" width="60" fixed="left"> <template #default="props"> <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable"> <el-table-column align="center" label="序号" type="index"/> <el-table-column label="产品大类" prop="productCategory" /> <el-table-column label="规格型号" prop="specificationModel" /> <el-table-column label="单位" prop="unit" /> <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable"> <el-table-column align="center" label="序号" type="index" /> <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 === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)" type="success">充足</el-tag> <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)" type="success">已出库</el-tag> <el-tag v-else type="danger">不足</el-tag> <el-tag v-else type="danger">不足</el-tag> </template> </el-table-column> <el-table-column label="发货状态" width="140" align="center"> <el-table-column label="发货状态" width="140" align="center"> <template #default="scope"> <el-tag :type="getShippingStatusType(scope.row)" size="small"> <el-tag :type="getShippingStatusType(scope.row)" size="small"> {{ getShippingStatusText(scope.row) }} </el-tag> </template> </el-table-column> <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip /> <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip /> <el-table-column label="发货车牌" minWidth="100px" align="center"> <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip /> <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip /> <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> <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> @@ -87,16 +155,43 @@ </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 label="数量" prop="quantity" /> <!-- 对公客户显示的字段 --> <template v-if="props.row.customerType =='1'"> <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" /> </template> <!-- 对私客户显示的字段 --> <template v-else-if="props.row.customerType == 2"> <el-table-column label="单价" prop="unitPrice" :formatter="formattedNumber" /> <el-table-column label="总价" prop="totalPrice" :formatter="formattedNumber" /> <el-table-column label="运费" prop="freight" :formatter="formattedNumber" /> <el-table-column label="含运费单价" prop="priceWithFreight" :formatter="formattedNumber" /> </template> <!--操作--> <!-- <el-table-column Width="60px" label="操作" align="center"> <!-- <el-table-column Width="60px" label="操作" align="center"> <template #default="scope"> <el-button link <el-button link type="primary" :disabled="!canShip(scope.row)" @click="openDeliveryForm(scope.row)"> @@ -107,50 +202,127 @@ </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="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="entryDate" width="120" show-overflow-tooltip /> <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip /> <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip /> <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip /> <el-table-column fixed="right" label="操作" width="130" align="center"> <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="customerName" width="300" show-overflow-tooltip /> <el-table-column label="客户类型" prop="customerType" width="100"> <template #default="scope"> <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">编辑</el-button> <el-button link type="primary" @click="downLoadFile(scope.row)">附件</el-button> {{scope.row.customerType == 1 ? '对公' : '对私'}} </template> </el-table-column> <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="entryDate" width="120" show-overflow-tooltip /> <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip /> <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip /> <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip /> <el-table-column fixed="right" label="操作" width="130" align="center"> <template #default="scope"> <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">编辑</el-button> <el-button link type="primary" @click="downLoadFile(scope.row)">附件</el-button> </template> </el-table-column> </el-table> <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" :page="page.current" :limit="page.size" @pagination="paginationChange" /> <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" :page="page.current" :limit="page.size" @pagination="paginationChange" /> </div> <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" :width="'70%'" :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia"> <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" :width="'70%'" :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia"> <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> <!-- 报价单导入入口:放在表单顶部,选择后反显客户/业务员等 --> <el-row v-if="operationType === 'add'" style="margin-bottom: 10px;"> <el-col :span="24" style="text-align: right;"> <el-button type="primary" plain @click="openQuotationDialog"> <el-row v-if="operationType === 'add'" style="margin-bottom: 10px;"> <el-col :span="24" style="text-align: right;"> <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"> <el-input v-model="form.salesContractNo" placeholder="自动生成" clearable disabled /> <el-form-item label="销售合同号:" prop="salesContractNo"> <el-input v-model="form.salesContractNo" placeholder="自动生成" clearable disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="业务员:" prop="salesman"> <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'"> <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" <el-form-item label="业务员:" prop="salesman"> <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> </el-form-item> @@ -158,83 +330,174 @@ </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="客户名称:" prop="customerId"> <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-form-item label="客户名称:" prop="customerId"> <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" @change="handleCustomerChange"> <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"> {{ item.customerName + "——" }} {{item.customerType=="1"?item.taxpayerIdentificationNumber || '无':"对私"}} </el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="项目名称:" prop="projectName"> <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" /> <el-form-item label="项目名称:" prop="projectName"> <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="签订日期:" prop="executionDate"> <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" /> <el-form-item label="签订日期:" prop="executionDate"> <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="付款方式"> <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" /> <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="录入人:" prop="entryPerson"> <el-form-item label="录入人:" prop="entryPerson"> <el-select v-model="form.entryPerson" filterable default-first-option :reserve-keyword="false" placeholder="请选择" clearable @change="changs"> <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> :reserve-keyword="false" placeholder="请选择" clearable @change="changs"> <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="录入日期:" prop="entryDate"> <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable /> <el-form-item label="录入日期:" prop="entryDate"> <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="交货日期:" prop="entryDate"> <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable /> <el-form-item label="交货日期:" prop="entryDate"> <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="请选择" clearable /> </el-form-item> </el-col> </el-row> <el-row> <el-form-item label="产品信息:" prop="entryDate"> <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button> <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button> <el-row :gutter="30"> <el-form-item label="产品信息:" prop="entryDate"> <el-button v-if="operationType !== 'view' && currentCustomerType" type="primary" @click="openProductForm('add')">添加</el-button> <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" :disabled="!productData.length">删除</el-button> </el-form-item> </el-row> <el-table :data="productData" border @selection-change="productSelected" show-summary <el-table :data="productData" border @selection-change="productSelected" show-summary :summary-method="summarizeMainTable"> <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" :selectable="(row) => !isProductShipped(row)" /> <el-table-column align="center" label="序号" type="index" width="60" /> <el-table-column label="产品大类" prop="productCategory" /> <el-table-column label="规格型号" prop="specificationModel" /> <el-table-column label="单位" prop="unit" /> <el-table-column label="数量" prop="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 fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'"> <el-table-column align="center" label="序号" type="index" width="60" /> <el-table-column label="产品大类" prop="productCategory" /> <el-table-column label="规格型号" prop="specificationModel" /> <el-table-column label="单位" prop="unit" /> <el-table-column label="数量" prop="quantity" /> <!-- 对公客户显示的字段 --> <template v-if="currentCustomerType == 1"> <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" /> </template> <!-- 对私客户显示的字段 --> <template v-else-if="currentCustomerType == 2"> <el-table-column label="单价" prop="unitPrice" :formatter="formattedNumber" /> <el-table-column label="总价" prop="totalPrice" :formatter="formattedNumber" /> <el-table-column label="运费" prop="freight" :formatter="formattedNumber" /> <el-table-column label="含运费单价" prop="priceWithFreight" :formatter="formattedNumber" /> </template> <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'"> <template #default="scope"> <el-button link type="primary" size="small" <el-button link type="primary" size="small" :disabled="isProductShipped(scope.row)" @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button> </template> @@ -242,19 +505,35 @@ </el-table> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="备注:" prop="remarks"> <el-input v-model="form.remarks" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" /> <el-form-item label="备注:" prop="remarks"> <el-input v-model="form.remarks" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="附件材料:" prop="salesLedgerFiles"> <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError" :on-success="handleUploadSuccess" :on-remove="handleRemove"> <el-button type="primary" v-if="operationType !== 'view'">上传</el-button> <template #tip v-if="operationType !== 'view'"> <el-form-item label="附件材料:" prop="salesLedgerFiles"> <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError" :on-success="handleUploadSuccess" :on-remove="handleRemove"> <el-button type="primary" v-if="operationType !== 'view'">上传</el-button> <template #tip v-if="operationType !== 'view'"> <div class="el-upload__tip"> 文件格式支持 doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z @@ -266,167 +545,308 @@ </el-row> </el-form> </FormDialog> <!-- 从报价单导入(仅审批通过) --> <el-dialog v-model="quotationDialogVisible" <el-dialog v-model="quotationDialogVisible" title="选择审批通过的销售报价单" width="80%" :close-on-click-modal="false" > :close-on-click-modal="false"> <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;"> <el-input v-model="quotationSearchForm.quotationNo" <el-input v-model="quotationSearchForm.quotationNo" placeholder="请输入报价单号" clearable style="max-width: 260px;" @change="fetchQuotationList" /> <el-input v-model="quotationSearchForm.customer" @change="fetchQuotationList" /> <el-input v-model="quotationSearchForm.customer" placeholder="请输入客户名称" clearable style="max-width: 260px;" @change="fetchQuotationList" /> <el-button type="primary" @click="fetchQuotationList">搜索</el-button> @change="fetchQuotationList" /> <el-button type="primary" @click="fetchQuotationList">搜索</el-button> <el-button @click="resetQuotationSearch">重置</el-button> </div> <el-table :data="quotationList" <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"> 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"> <el-table-column fixed="right" label="操作" width="120" align="center"> <template #default="scope"> <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button> <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button> </template> </el-table-column> </el-table> <pagination v-show="quotationPage.total > 0" <pagination v-show="quotationPage.total > 0" :total="quotationPage.total" layout="total, sizes, prev, pager, next, jumper" :page="quotationPage.current" :limit="quotationPage.size" @pagination="quotationPaginationChange" /> @pagination="quotationPaginationChange" /> <template #footer> <el-button @click="quotationDialogVisible = false">关闭</el-button> </template> </el-dialog> <FormDialog v-model="productFormVisible" <FormDialog v-model="productFormVisible" :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" :width="'40%'" :operation-type="productOperationType" @close="closeProductDia" @confirm="submitProduct" @cancel="closeProductDia"> <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef"> <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef"> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="产品大类:" prop="productCategory"> <el-form-item label="产品大类:" prop="productCategory"> <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable> <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> </el-select> --> <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" /> <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="规格型号:" prop="productModelId"> <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel" filterable> <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> <el-form-item label="规格型号:" prop="productModelId"> <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel" filterable> <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="单位:" prop="unit"> <el-input v-model="productForm.unit" placeholder="请输入" clearable /> <el-form-item label="单位:" prop="unit"> <el-input v-model="productForm.unit" placeholder="请输入" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="税率(%):" prop="taxRate"> <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate"> <el-option label="1" value="1" /> <el-option label="6" value="6" /> <el-option label="13" value="13" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice"> <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%" <el-form-item label="数量:" prop="quantity"> <el-input-number :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable :precision="2" placeholder="请输入" clearable @change="calculateFromUnitPrice" /> @change="calculateFromQuantity" style="width: 100%" /> </el-form-item> </el-col> <!-- <el-col :span="12"> <el-form-item label="税率(%):" prop="taxRate"> <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate"> <el-option label="1" value="1" /> <el-option label="6" value="6" /> <el-option label="13" value="13" /> </el-select> </el-form-item> </el-col> --> </el-row> <el-row :gutter="30"> </el-row> <!-- 对公客户显示的字段 --> <template v-if="currentCustomerType == 1"> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="税率(%):" prop="taxRate"> <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate"> <el-option label="1" value="1" /> <el-option label="6" value="6" /> <el-option label="13" value="13" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="数量:" prop="quantity"> <el-input-number :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice"> <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%" :precision="2" @change="calculateFromQuantity" style="width: 100%" /> placeholder="请输入" clearable @change="calculateFromUnitPrice" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice"> <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" /> <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice"> <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice"> <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" /> <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice"> <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="发票类型:" prop="invoiceType"> <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable> <el-option label="增普票" value="增普票" /> <el-option label="增专票" value="增专票" /> <el-form-item label="发票类型:" prop="invoiceType"> <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable> <el-option label="增普票" value="增普票" /> <el-option label="增专票" value="增专票" /> </el-select> </el-form-item> </el-col> </el-row> </template> <!-- 对私客户显示的字段 --> <template v-else-if="currentCustomerType == 2"> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="单价:" prop="unitPrice"> <el-input-number :step="0.01" :min="0" v-model="productForm.unitPrice" style="width: 100%" :precision="2" placeholder="请输入" clearable @change="calculatePrivatePrice" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="总价:" prop="totalPrice"> <el-input-number :step="0.01" :min="0" v-model="productForm.totalPrice" style="width: 100%" :precision="2" placeholder="请输入" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="运费:" prop="freight"> <el-input-number :step="0.01" :min="0" v-model="productForm.freight" style="width: 100%" :precision="2" placeholder="请输入" clearable @change="calculatePrivatePrice" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="含运费单价:" prop="priceWithFreight"> <el-input-number :step="0.01" :min="0" v-model="productForm.priceWithFreight" style="width: 100%" :precision="2" placeholder="请输入" clearable @change="calculatePrivateTotal" /> </el-form-item> </el-col> </el-row> </template> </el-form> </FormDialog> <!-- 导入弹窗 --> <FormDialog v-model="importUpload.open" <FormDialog v-model="importUpload.open" :title="importUpload.title" :width="'600px'" @close="importUpload.open = false" @confirm="submitImportFile" @cancel="importUpload.open = false" > <el-upload ref="importUploadRef" @cancel="importUpload.open = false"> <el-upload ref="importUploadRef" :limit="1" accept=".xlsx,.xls" :action="importUpload.url" @@ -437,8 +857,7 @@ :on-progress="importUpload.onProgress" :on-change="importUpload.onChange" :auto-upload="false" drag > drag> <i class="el-icon-upload"></i> <div class="el-upload__text"> 将文件拖到此处,或<em>点击上传</em> @@ -446,43 +865,45 @@ <template #tip> <div class="el-upload__tip"> 仅支持 xls/xlsx,大小不超过 10MB。 <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button> <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button> </div> </template> </el-upload> </FormDialog> <!-- 附件列表弹窗 --> <FileListDialog ref="fileListRef" <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" title="附件列表" /> title="附件列表" /> <!-- 打印预览弹窗 --> <el-dialog v-model="printPreviewVisible" <el-dialog v-model="printPreviewVisible" title="打印预览" width="90%" :close-on-click-modal="false" class="print-preview-dialog" > class="print-preview-dialog"> <div class="print-preview-container"> <div class="print-preview-header"> <el-button type="primary" @click="executePrint">执行打印</el-button> <el-button type="primary" @click="executePrint">执行打印</el-button> <el-button @click="printPreviewVisible = false">关闭预览</el-button> </div> <div class="print-preview-content"> <div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;"> <div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;"> 暂无打印数据 </div> <div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;"> <div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;"> 共 {{ printData.length }} 条数据待打印 </div> <div v-for="(item, index) in printData" :key="index" class="print-page"> <div v-for="(item, index) in printData" :key="index" class="print-page"> <div class="delivery-note"> <div class="header"> <div class="document-title">零售发货单</div> </div> <div class="info-section"> <div class="info-row"> <div> @@ -503,7 +924,6 @@ <span class="value">{{ item.salesContractNo }}</span> </div> </div> <div class="table-section"> <table class="product-table"> <thead> @@ -517,7 +937,8 @@ </tr> </thead> <tbody> <tr v-for="product in item.products" :key="product.id"> <tr v-for="product in item.products" :key="product.id"> <td>{{ product.productCategory || '' }}</td> <td>{{ product.specificationModel || '' }}</td> <td>{{ product.unit || '' }}</td> @@ -526,7 +947,8 @@ <td>{{ product.taxInclusiveTotalPrice || '0' }}</td> </tr> <tr v-if="!item.products || item.products.length === 0"> <td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td> <td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td> </tr> </tbody> <tfoot> @@ -541,7 +963,6 @@ </tfoot> </table> </div> <div class="footer-section"> <div class="footer-row"> <div class="footer-item"> @@ -574,65 +995,61 @@ </div> </el-dialog> <!-- 发货弹框 --> <el-dialog v-model="deliveryFormVisible" <el-dialog v-model="deliveryFormVisible" title="发货信息" width="40%" @close="closeDeliveryDia" > <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef"> @close="closeDeliveryDia"> <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef"> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="发货类型:" prop="type"> <el-select v-model="deliveryForm.type" <el-form-item label="发货类型:" prop="type"> <el-select v-model="deliveryForm.type" placeholder="请选择发货类型" style="width: 100%" > <el-option label="货车" value="货车" /> <el-option label="快递" value="快递" /> style="width: 100%"> <el-option label="货车" value="货车" /> <el-option label="快递" value="快递" /> </el-select> </el-form-item> </el-col> </el-row> <!-- 审批人选择(仿协同审批里的审批人节点选择) --> <el-row> <el-col :span="24"> <el-form-item> <template #label> <span>审批人选择:</span> <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button> <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button> </template> <div style="display: flex; align-items: flex-end; flex-wrap: wrap;"> <div v-for="(node, index) in approverNodes" <div v-for="(node, index) in approverNodes" :key="node.id" style="margin-right: 20px; text-align: center; margin-bottom: 10px;" > style="margin-right: 20px; text-align: center; margin-bottom: 10px;"> <div> <span>审批人</span> → </div> <el-select v-model="node.userId" <el-select v-model="node.userId" placeholder="选择人员" filterable style="width: 140px; margin-bottom: 8px;" > <el-option v-for="user in userList" style="width: 140px; margin-bottom: 8px;"> <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" /> :value="user.userId" /> </el-select> <div> <el-button type="danger" <el-button type="danger" @click="removeApproverNode(index)" v-if="approverNodes.length > 1" >删除</el-button> v-if="approverNodes.length > 1">删除</el-button> </div> </div> </div> @@ -642,7 +1059,8 @@ </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitDelivery">确认发货</el-button> <el-button type="primary" @click="submitDelivery">确认发货</el-button> <el-button @click="closeDeliveryDia">取消</el-button> </div> </template> @@ -659,8 +1077,8 @@ import { UploadFilled, Download } 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 FileListDialog from "@/components/Dialog/FileListDialog.vue"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; import { ledgerListPage, @@ -671,7 +1089,8 @@ delLedger, addOrUpdateSalesLedgerProduct, delProduct, delLedgerFile, getProductInventory, delLedgerFile, getProductInventory, } from "@/api/salesManagement/salesLedger.js"; import { modelList, productTreeList } from "@/api/basicData/product.js"; import useFormData from "@/hooks/useFormData.js"; @@ -699,6 +1118,7 @@ // 用户信息表单弹框数据 const operationType = ref(""); const dialogFormVisible = ref(false); const currentCustomerType = ref(""); // 当前客户类型:对公/对私 const data = reactive({ searchForm: { customerName: "", // 客户名称 @@ -706,6 +1126,7 @@ entryDate: null, // 录入日期 entryDateStart: undefined, entryDateEnd: undefined, customerType: "", }, form: { salesContractNo: "", @@ -739,11 +1160,17 @@ specificationModel: "", unit: "", quantity: "", // 对公字段 taxInclusiveUnitPrice: "", taxRate: "", taxInclusiveTotalPrice: "", taxExclusiveTotalPrice: "", invoiceType: "", // 对私字段 unitPrice: "", totalPrice: "", freight: 0, priceWithFreight: "", }, productRules: { productCategory: [{ required: true, message: "请选择", trigger: "change" }], @@ -753,6 +1180,7 @@ ], unit: [{ required: true, message: "请输入", trigger: "blur" }], quantity: [{ required: true, message: "请输入", trigger: "blur" }], // 对公字段验证 taxInclusiveUnitPrice: [ { required: true, message: "请输入", trigger: "blur" }, ], @@ -764,6 +1192,11 @@ { required: true, message: "请输入", trigger: "blur" }, ], invoiceType: [{ required: true, message: "请选择", trigger: "change" }], // 对私字段验证 unitPrice: [{ required: true, message: "请输入", trigger: "blur" }], totalPrice: [{ required: true, message: "请输入", trigger: "blur" }], freight: [{ required: true, message: "请输入", trigger: "blur" }], priceWithFreight: [{ required: true, message: "请输入", trigger: "blur" }], }, }); const { productForm, productRules } = toRefs(productFormData); @@ -803,9 +1236,7 @@ type: "货车", // 货车, 快递 }, deliveryRules: { type: [ { required: true, message: "请选择发货类型", trigger: "change" } ] type: [{ required: true, message: "请选择发货类型", trigger: "change" }], }, }); const { deliveryForm, deliveryRules } = toRefs(deliveryFormData); @@ -816,7 +1247,7 @@ const addApproverNode = () => { approverNodes.value.push({ id: nextApproverId++, userId: null }); }; const removeApproverNode = (index) => { const removeApproverNode = index => { approverNodes.value.splice(index, 1); }; @@ -828,8 +1259,8 @@ url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import", headers: { Authorization: "Bearer " + getToken() }, isUploading: false, beforeUpload: (file) => { const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls'); beforeUpload: file => { const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls"); const isLt10M = file.size / 1024 / 1024 < 10; if (!isExcel) { proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!"); @@ -842,13 +1273,13 @@ return true; }, onChange: (file, fileList) => { console.log('文件状态改变', file, fileList); console.log("文件状态改变", file, fileList); }, onProgress: (event, file, fileList) => { console.log('上传中...', event.percent); console.log("上传中...", event.percent); }, onSuccess: (response, file, fileList) => { console.log('上传成功', response, file, fileList); console.log("上传成功", response, file, fileList); importUpload.isUploading = false; if (response.code === 200) { proxy.$modal.msgSuccess("导入成功"); @@ -862,13 +1293,13 @@ } }, onError: (error, file, fileList) => { console.error('上传失败', error, file, fileList); console.error("上传失败", error, file, fileList); importUpload.isUploading = false; proxy.$modal.msgError("导入失败,请重试"); }, }); const changeDaterange = (value) => { const changeDaterange = value => { if (value) { searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); @@ -890,7 +1321,7 @@ expandedRowKeys.value = []; getList(); }; const paginationChange = (obj) => { const paginationChange = obj => { page.current = obj.page; page.size = obj.limit; getList(); @@ -903,10 +1334,10 @@ // 移除录入日期的默认值设置,只保留范围日期字段 delete params.entryDate; return ledgerListPage(params) .then((res) => { .then(res => { tableLoading.value = false; tableData.value = res.records; tableData.value.map((item) => { tableData.value.map(item => { item.children = []; }); total.value = res.total; @@ -919,7 +1350,7 @@ // 获取产品大类tree数据 const getProductOptions = () => { // 返回 Promise,便于在编辑产品时等待加载完成 return productTreeList().then((res) => { return productTreeList().then(res => { productOptions.value = convertIdToValue(res); return productOptions.value; }); @@ -928,14 +1359,14 @@ return parseFloat(cellValue).toFixed(2); }; // 获取tree子数据 const getModels = (value) => { const getModels = value => { productForm.value.productCategory = findNodeById(productOptions.value, value); modelList({ id: value }).then((res) => { modelList({ id: value }).then(res => { modelOptions.value = res; }); }; const getProductModel = (value) => { const index = modelOptions.value.findIndex((item) => item.id === value); const getProductModel = value => { const index = modelOptions.value.findIndex(item => item.id === value); if (index !== -1) { productForm.value.specificationModel = modelOptions.value[index].model; productForm.value.unit = modelOptions.value[index].unit; @@ -959,7 +1390,7 @@ return null; // 没有找到节点,返回null }; function convertIdToValue(data) { return data.map((item) => { return data.map(item => { const { id, children, ...rest } = item; const newItem = { ...rest, @@ -986,12 +1417,12 @@ return null; } // 表格选择数据 const handleSelectionChange = (selection) => { const handleSelectionChange = selection => { // 过滤掉子数据 selectedRows.value = selection.filter((item) => item.children !== undefined); selectedRows.value = selection.filter(item => item.children !== undefined); console.log("selection", selectedRows.value); }; const productSelected = (selectedRows) => { const productSelected = selectedRows => { productSelectedRows.value = selectedRows; }; const expandedRowKeys = ref([]); @@ -1000,10 +1431,15 @@ if (expandedRows.length > 0) { expandedRowKeys.value = []; try { productList({ salesLedgerId: row.id, type: 1 }).then((res) => { const index = tableData.value.findIndex((item) => item.id === row.id); 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; // 为子表格数据添加客户类型信息 const childrenData = res.data.map(item => ({ ...item, customerType: row.customerType, })); tableData.value[index].children = childrenData; } expandedRowKeys.value.push(row.id); }); @@ -1017,22 +1453,22 @@ // 添加表行类名方法 const tableRowClassName = ({ row }) => { if (!row.deliveryDate) return ''; if (row.isFh) return ''; if (!row.deliveryDate) return ""; if (row.isFh) return ""; const diff = row.deliveryDaysDiff; if (diff === 15) { return 'yellow'; return "yellow"; } else if (diff === 10) { return 'pink'; return "pink"; } else if (diff === 2) { return 'purple'; return "purple"; } else if (diff < 2) { return 'red'; return "red"; } }; // 主表合计方法 const summarizeMainTable = (param) => { const summarizeMainTable = param => { return proxy.summarizeTable(param, [ "contractAmount", "taxInclusiveTotalPrice", @@ -1040,23 +1476,48 @@ ]); }; // 子表合计方法 const summarizeChildrenTable = (param) => { const summarizeChildrenTable = param => { return proxy.summarizeTable(param, [ "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // 客户选择变化处理 const handleCustomerChange = customerId => { if (customerId) { const customer = customerOption.value.find(item => item.id === customerId); if (customer) { // 如果客户类型发生变化,清空表格数据 if ( currentCustomerType.value && currentCustomerType.value !== customer.customerType ) { productData.value = []; } currentCustomerType.value = customer.customerType; } } else { currentCustomerType.value = ""; productData.value = []; } }; // 打开弹框 const openForm = async (type, row) => { operationType.value = type; form.value = {}; productData.value = []; selectedQuotation.value = null; currentCustomerType.value = ""; let userLists = await userListNoPage(); userList.value = userLists.data; customerList().then((res) => { customerList().then(res => { customerOption.value = res; // 如果是编辑模式且有客户ID,设置客户类型 if (type === "edit" && form.value.customerId) { handleCustomerChange(form.value.customerId); } }); form.value.entryPerson = userStore.id; if (type === "add") { @@ -1066,11 +1527,17 @@ form.value.executionDate = getCurrentDate(); } else { currentId.value = row.id; getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => { getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => { form.value = { ...res }; form.value.entryPerson = Number(res.entryPerson); productData.value = form.value.productData; fileList.value = form.value.salesLedgerFiles; productData.value = JSON.parse( JSON.stringify(form.value.productData || []) ); fileList.value = form.value.salesLedgerFiles ? form.value.salesLedgerFiles : []; // 设置客户类型 handleCustomerChange(form.value.customerId); }); } // let userAll = await userStore.getInfo() @@ -1127,14 +1594,14 @@ }; // 报价单弹框分页切换 const quotationPaginationChange = (obj) => { const quotationPaginationChange = obj => { quotationPage.current = obj.page; quotationPage.size = obj.limit; fetchQuotationList(); }; // 选中报价单后回填到台账表单 const applyQuotation = (row) => { const applyQuotation = row => { if (!row) return; selectedQuotation.value = row; @@ -1143,9 +1610,13 @@ // 客户名称 -> customerId const qCustomerName = String(row.customer || "").trim(); const customer = (customerOption.value || []).find((c) => { const customer = (customerOption.value || []).find(c => { const name = String(c.customerName || "").trim(); return name === qCustomerName || name.includes(qCustomerName) || qCustomerName.includes(name); return ( name === qCustomerName || name.includes(qCustomerName) || qCustomerName.includes(name) ); }); if (customer?.id) { form.value.customerId = customer.id; @@ -1156,12 +1627,15 @@ // 产品信息映射:报价 products -> 台账 productData const products = Array.isArray(row.products) ? row.products : []; productData.value = products.map((p) => { 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); const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( taxInclusiveTotalPrice, taxRate ); return { // 台账字段 productCategory: p.product || p.productName || "", @@ -1212,16 +1686,16 @@ if (operationType.value === "edit") { let ids = []; ids.push(file.id); delLedgerFile(ids).then((res) => { delLedgerFile(ids).then(res => { proxy.$modal.msgSuccess("删除成功"); }); } } // 提交表单 const submitForm = () => { proxy.$refs["formRef"].validate((valid) => { proxy.$refs["formRef"].validate(valid => { if (valid) { console.log('productData.value--', productData.value) console.log("productData.value--", productData.value); if (productData.value !== null && productData.value.length > 0) { form.value.productData = proxy.HaveJson(productData.value); } else { @@ -1230,11 +1704,12 @@ } let tempFileIds = []; if (fileList.value !== null && fileList.value.length > 0) { tempFileIds = fileList.value.map((item) => item.tempId); tempFileIds = fileList.value.map(item => item.tempId); } form.value.tempFileIds = tempFileIds; form.value.type = 1; addOrUpdateSalesLedger(form.value).then((res) => { form.value.salesType = currentCustomerType.value; addOrUpdateSalesLedger(form.value).then(res => { proxy.$modal.msgSuccess("提交成功"); closeDia(); getList(); @@ -1265,16 +1740,20 @@ productIndex.value = index; // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表 try { const options = productOptions.value && productOptions.value.length > 0 const options = productOptions.value && productOptions.value.length > 0 ? productOptions.value : await getProductOptions(); const categoryId = findNodeIdByLabel(options, productForm.value.productCategory); 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 m => m.model === productForm.value.specificationModel ); if (currentModel) { productForm.value.productModelId = currentModel.id; @@ -1285,21 +1764,50 @@ console.error("加载产品规格型号失败", e); } } else { getProductOptions() getProductOptions(); } productFormVisible.value = true; }; // 提交产品表单 const submitProduct = () => { proxy.$refs["productFormRef"].validate((valid) => { // 动态验证规则 const dynamicRules = { ...productRules.value }; // 根据客户类型调整验证规则 if (currentCustomerType.value == 1) { // 对私字段不需要验证 delete dynamicRules.unitPrice; delete dynamicRules.totalPrice; delete dynamicRules.freight; delete dynamicRules.priceWithFreight; } else if (currentCustomerType.value == 2) { // 对公字段不需要验证 delete dynamicRules.taxInclusiveUnitPrice; delete dynamicRules.taxRate; delete dynamicRules.taxInclusiveTotalPrice; delete dynamicRules.taxExclusiveTotalPrice; delete dynamicRules.invoiceType; } proxy.$refs["productFormRef"].validate((valid, fields) => { if (valid) { if (operationType.value === "edit") { submitProductEdit(); // 编辑模式下直接修改本地表格数据 if (productOperationType.value === "add") { productData.value.push({ ...productForm.value }); } else { productData.value.splice(productIndex.value, 1, { ...productForm.value, }); } closeProductDia(); } else { if(productOperationType.value === "add"){ productData.value.push({ ...productForm.value }); }else{ productData.value[productIndex.value] = { ...productForm.value } productData.value.splice(productIndex.value, 1, { ...productForm.value, }); } closeProductDia(); } @@ -1307,13 +1815,31 @@ }); }; const submitProductEdit = () => { productForm.value.salesLedgerId = currentId.value; productForm.value.type = 1 addOrUpdateSalesLedgerProduct(productForm.value).then((res) => { // 根据客户类型调整提交的数据 const productDataToSubmit = { ...productForm.value }; if (currentCustomerType.value == 1) { // 对私字段不需要提交 delete productDataToSubmit.unitPrice; delete productDataToSubmit.totalPrice; delete productDataToSubmit.freight; delete productDataToSubmit.priceWithFreight; } else if (currentCustomerType.value == 2) { // 对公字段不需要提交 delete productDataToSubmit.taxInclusiveUnitPrice; delete productDataToSubmit.taxRate; delete productDataToSubmit.taxInclusiveTotalPrice; delete productDataToSubmit.taxExclusiveTotalPrice; delete productDataToSubmit.invoiceType; } productDataToSubmit.salesLedgerId = currentId.value; productDataToSubmit.type = 1; addOrUpdateSalesLedgerProduct(productDataToSubmit).then(res => { proxy.$modal.msgSuccess("提交成功"); closeProductDia(); getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => { productData.value = res.productData; getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(res => { productData.value = JSON.parse(JSON.stringify(res.productData || [])); }); }); }; @@ -1325,46 +1851,23 @@ } // 检查是否有已发货或审核通过的产品 const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row)); const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row) ); if (shippedProducts.length > 0) { proxy.$modal.msgWarning("已发货或审核通过的产品不能删除"); return; } if (operationType.value === "add") { productSelectedRows.value.forEach((selectedRow) => { // 无论是新增还是编辑模式,都直接修改本地表格数据 productSelectedRows.value.forEach(selectedRow => { const index = productData.value.findIndex( (product) => product.id === selectedRow.id product => product.id === selectedRow.id ); if (index !== -1) { productData.value.splice(index, 1); } }); } else { let ids = []; if (productSelectedRows.value.length > 0) { ids = productSelectedRows.value.map((item) => item.id); } ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", { confirmButtonText: "确认", cancelButtonText: "取消", type: "warning", }) .then(() => { delProduct(ids).then((res) => { proxy.$modal.msgSuccess("删除成功"); closeProductDia(); getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then( (res) => { productData.value = res.productData; } ); }); }) .catch(() => { proxy.$modal.msg("已取消"); }); } }; // 关闭产品弹框 const closeProductDia = () => { @@ -1406,7 +1909,7 @@ }); }; /** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */ const isProductShipped = (product) => { const isProductShipped = product => { if (!product) return false; const status = String(product.shippingStatus || "").trim(); // 如果发货状态是"已发货"或"审核通过",则不可编辑和删除 @@ -1414,14 +1917,16 @@ }; /** 判断销售订单下是否存在已发货/发货完成的产品(不可删除) */ const hasShippedProducts = (products) => { const hasShippedProducts = products => { if (!products || !products.length) return false; return products.some((p) => { return products.some(p => { const status = String(p.shippingStatus || "").trim(); // 有发货日期或车牌号视为已发货 if (p.shippingDate || p.shippingCarNumber) return true; // 已进行发货、发货完成、已发货 均不可删除 return status === "已进行发货" || status === "发货完成" || status === "已发货"; return ( status === "已进行发货" || status === "发货完成" || status === "已发货" ); }); }; @@ -1431,12 +1936,13 @@ proxy.$modal.msgWarning("请选择数据"); return; } const ids = selectedRows.value.map((item) => item.id); const ids = selectedRows.value.map(item => item.id); // 检查是否有已进行发货或发货完成的销售订单,若有则不允许删除 const cannotDeleteNames = []; for (const row of selectedRows.value) { let products = row.children && row.children.length > 0 ? row.children : null; let products = row.children && row.children.length > 0 ? row.children : null; if (!products) { try { const res = await productList({ salesLedgerId: row.id, type: 1 }); @@ -1450,7 +1956,9 @@ } } if (cannotDeleteNames.length > 0) { proxy.$modal.msgWarning("已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、")); proxy.$modal.msgWarning( "已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、") ); return; } @@ -1460,7 +1968,7 @@ type: "warning", }) .then(() => { delLedger(ids).then((res) => { delLedger(ids).then(res => { proxy.$modal.msgSuccess("删除成功"); getList(); }); @@ -1487,12 +1995,15 @@ for (const row of selectedRows.value) { try { // 调用productList接口查询产品数据 const productRes = await productList({ salesLedgerId: row.id, type: 1 }); const productRes = await productList({ salesLedgerId: row.id, type: 1, }); // 将产品数据整合到销售台账记录中 const rowWithProducts = { ...row, products: productRes.data || [] products: productRes.data || [], }; printDataWithProducts.push(rowWithProducts); @@ -1501,17 +2012,16 @@ // 即使某个记录的产品数据获取失败,也要包含该记录 printDataWithProducts.push({ ...row, products: [] products: [], }); } } printData.value = printDataWithProducts; console.log('打印数据(包含产品):', printData.value); console.log("打印数据(包含产品):", printData.value); printPreviewVisible.value = true; } catch (error) { console.error('获取产品数据失败:', error); console.error("获取产品数据失败:", error); proxy.$modal.msgError("获取产品数据失败,请重试"); } finally { proxy.$modal.closeLoading(); @@ -1519,11 +2029,11 @@ }; // 执行打印 const executePrint = () => { console.log('开始执行打印,数据条数:', printData.value.length); console.log('打印数据:', printData.value); console.log("开始执行打印,数据条数:", printData.value.length); console.log("打印数据:", printData.value); // 创建一个新的打印窗口 const printWindow = window.open('', '_blank', 'width=800,height=600'); const printWindow = window.open("", "_blank", "width=800,height=600"); // 构建打印内容 let printContent = ` @@ -1674,16 +2184,23 @@ <div class="info-row"> <div> <span class="label">发货日期:</span> <span class="value">${formatDate(item.createTime)}</span> <span class="value">${formatDate( item.createTime )}</span> </div> <div> <span class="label">客户名称:</span> <span class="value">${item.customerName}</span> <span class="value">${ item.customerName }</span> </div> </div> <div class="info-row"> <span class="label">单号:</span> <span class="value">${item.salesContractNo || ''}</span> <span class="value">${ item.salesContractNo || "" }</span> </div> </div> @@ -1700,18 +2217,47 @@ </tr> </thead> <tbody> ${item.products && item.products.length > 0 ? item.products.map(product => ` ${ item.products && item .products .length > 0 ? item.products .map( product => ` <tr> <td>${product.productCategory || ''}</td> <td>${product.specificationModel || ''}</td> <td>${product.unit || ''}</td> <td>${product.taxInclusiveUnitPrice || '0'}</td> <td>${product.quantity || '0'}</td> <td>${product.taxInclusiveTotalPrice || '0'}</td> <td>${ product.productCategory || "" }</td> <td>${ product.specificationModel || "" }</td> <td>${ product.unit || "" }</td> <td>${ product.taxInclusiveUnitPrice || "0" }</td> <td>${ product.quantity || "0" }</td> <td>${ product.taxInclusiveTotalPrice || "0" }</td> </tr> `).join('') : '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>' ` ) .join( "" ) : '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>' } </tbody> <tfoot> @@ -1720,8 +2266,12 @@ <td class="total-value"></td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value">${getTotalQuantityForPrint(item.products)}</td> <td class="total-value">${getTotalAmountForPrint(item.products)}</td> <td class="total-value">${getTotalQuantityForPrint( item.products )}</td> <td class="total-value">${getTotalAmountForPrint( item.products )}</td> </tr> </tfoot> </table> @@ -1745,11 +2295,16 @@ <div class="footer-row"> <div class="footer-item"> <span class="label">操作员:</span> <span class="value">${userStore.nickName || '撕开前'}</span> <span class="value">${ userStore.nickName || "撕开前" }</span> </div> <div class="footer-item"> <span class="label">打印日期:</span> <span class="value">${formatDateTime(new Date())}</span> <span class="value">${formatDateTime( new Date() )}</span> </div> </div> </div> @@ -1777,7 +2332,7 @@ }; }; // 格式化日期 const formatDate = (dateString) => { const formatDate = dateString => { if (!dateString) return getCurrentDate(); const date = new Date(dateString); const year = date.getFullYear(); @@ -1786,7 +2341,7 @@ return `${year}/${month}/${day}`; }; // 格式化日期时间 const formatDateTime = (date) => { const formatDateTime = date => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); @@ -1796,8 +2351,8 @@ return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`; }; // 计算产品总数量 const getTotalQuantity = (products) => { if (!products || products.length === 0) return '0'; const getTotalQuantity = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.quantity) || 0); }, 0); @@ -1805,8 +2360,8 @@ }; // 计算产品总金额 const getTotalAmount = (products) => { if (!products || products.length === 0) return '0'; const getTotalAmount = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0); }, 0); @@ -1814,16 +2369,16 @@ }; // 用于打印的计算函数 const getTotalQuantityForPrint = (products) => { if (!products || products.length === 0) return '0'; const getTotalQuantityForPrint = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.quantity) || 0); }, 0); return total.toFixed(2); }; const getTotalAmountForPrint = (products) => { if (!products || products.length === 0) return '0'; const getTotalAmountForPrint = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0); }, 0); @@ -1890,7 +2445,9 @@ } if (isCalculating.value) return; const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice); const exclusiveTotalPrice = parseFloat( productForm.value.taxExclusiveTotalPrice ); const quantity = parseFloat(productForm.value.quantity); const taxRate = parseFloat(productForm.value.taxRate); @@ -1906,13 +2463,51 @@ productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); // 计算含税单价 = 含税总价 / 数量 productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2); productForm.value.taxInclusiveUnitPrice = ( inclusiveTotalPrice / quantity ).toFixed(2); isCalculating.value = false; }; // 对私客户价格计算:单价和运费变化时计算含运费单价和总价 const calculatePrivatePrice = () => { if (currentCustomerType.value == 2) { const unitPrice = parseFloat(productForm.value.unitPrice) || 0; const freight = parseFloat(productForm.value.freight) || 0; const quantity = parseFloat(productForm.value.quantity) || 0; // 计算含运费单价 productForm.value.priceWithFreight = (unitPrice + freight).toFixed(2); // 计算总价 productForm.value.totalPrice = ( parseFloat(productForm.value.priceWithFreight) * quantity ).toFixed(2); } }; // 对私客户价格计算:含运费单价变化时计算总价 const calculatePrivateTotal = () => { if (currentCustomerType.value == 2) { const priceWithFreight = parseFloat(productForm.value.priceWithFreight) || 0; const quantity = parseFloat(productForm.value.quantity) || 0; // 计算总价 productForm.value.totalPrice = (priceWithFreight * quantity).toFixed(2); } }; // 根据数量变化计算总价 const calculateFromQuantity = () => { // 对私客户使用对私计算逻辑 if (currentCustomerType.value == 2) { calculatePrivatePrice(); return; } // 对公客户使用原有的计算逻辑 if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请先选择税率"); return; @@ -1945,6 +2540,13 @@ // 根据含税单价变化计算总价 const calculateFromUnitPrice = () => { // 对私客户使用对私计算逻辑 if (currentCustomerType.value == 2) { calculatePrivatePrice(); return; } // 对公客户使用原有的计算逻辑 if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请先选择税率"); return; @@ -1977,13 +2579,21 @@ // 根据税率变化计算不含税总价 const calculateFromTaxRate = () => { // 对私客户不需要税率计算 if (currentCustomerType.value == 2) { return; } // 对公客户使用原有的计算逻辑 if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请先选择税率"); return; } if (isCalculating.value) return; const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); const inclusiveTotalPrice = parseFloat( productForm.value.taxInclusiveTotalPrice ); const taxRate = parseFloat(productForm.value.taxRate); if (!inclusiveTotalPrice || !taxRate) { @@ -1994,10 +2604,7 @@ // 计算不含税总价 productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( inclusiveTotalPrice, taxRate ); proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate); isCalculating.value = false; }; @@ -2005,62 +2612,62 @@ * 获取发货状态文本 * @param row 行数据 */ const getShippingStatusText = (row) => { const getShippingStatusText = row => { // 如果已发货(有发货日期或车牌号),显示"已发货" if (row.shippingDate || row.shippingCarNumber) { return '已发货'; return "已发货"; } // 获取发货状态字段 const status = row.shippingStatus; // 如果状态为空或未定义,默认为"待发货" if (status === null || status === undefined || status === '') { return '待发货'; if (status === null || status === undefined || status === "") { return "待发货"; } // 状态是字符串 const statusStr = String(status).trim(); const statusTextMap = { '待发货': '待发货', '待审核': '待审核', '审核中': '审核中', '审核拒绝': '审核拒绝', '审核通过': '审核通过', '已发货': '已发货' 待发货: "待发货", 待审核: "待审核", 审核中: "审核中", 审核拒绝: "审核拒绝", 审核通过: "审核通过", 已发货: "已发货", }; return statusTextMap[statusStr] || '待发货'; return statusTextMap[statusStr] || "待发货"; }; /** * 获取发货状态标签类型(颜色) * @param row 行数据 */ const getShippingStatusType = (row) => { const getShippingStatusType = row => { // 如果已发货(有发货日期或车牌号),显示绿色 if (row.shippingDate || row.shippingCarNumber) { return 'success'; return "success"; } // 获取发货状态字段 const status = row.shippingStatus; // 如果状态为空或未定义,默认为灰色(待发货) if (status === null || status === undefined || status === '') { return 'info'; if (status === null || status === undefined || status === "") { return "info"; } // 状态是字符串 const statusStr = String(status).trim(); const typeTextMap = { '待发货': 'info', '待审核': 'info', '审核中': 'warning', '审核拒绝': 'danger', '审核通过': 'success', '已发货': 'success' 待发货: "info", 待审核: "info", 审核中: "warning", 审核拒绝: "danger", 审核通过: "success", 已发货: "success", }; return typeTextMap[statusStr] || 'info'; return typeTextMap[statusStr] || "info"; }; /** @@ -2068,7 +2675,7 @@ * 只有在产品状态是充足,发货状态是待发货和审核拒绝的时候才可以发货 * @param row 行数据 */ const canShip = (row) => { const canShip = row => { // 产品状态必须是充足(approveStatus === 1) if (row.approveStatus !== 1) { return false; @@ -2083,8 +2690,8 @@ } // 发货状态必须是"待发货"或"审核拒绝" const statusStr = shippingStatus ? String(shippingStatus).trim() : ''; return statusStr === '待发货' || statusStr === '审核拒绝'; const statusStr = shippingStatus ? String(shippingStatus).trim() : ""; return statusStr === "待发货" || statusStr === "审核拒绝"; }; /** @@ -2092,21 +2699,23 @@ * * @param row 下载文件的相关信息对象 */ const fileListRef = ref(null) const fileListDialogVisible = ref(false) const downLoadFile = (row) => { getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => { 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) fileListRef.value.open(res.salesLedgerFiles); } }); } }; // 打开发货弹框 const openDeliveryForm = (row) => { const openDeliveryForm = row => { // 检查是否可以发货 if (!canShip(row)) { proxy.$modal.msgWarning("只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货"); proxy.$modal.msgWarning( "只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货" ); return; } @@ -2122,7 +2731,7 @@ // 提交发货表单 const submitDelivery = () => { proxy.$refs["deliveryFormRef"].validate((valid) => { proxy.$refs["deliveryFormRef"].validate(valid => { if (valid) { // 审批人必填校验(所有节点都要选人) const hasEmptyApprover = approverNodes.value.some(node => !node.userId); @@ -2130,7 +2739,9 @@ proxy.$modal.msgError("请为所有审批节点选择审批人!"); return; } const approveUserIds = approverNodes.value.map(node => node.userId).join(","); const approveUserIds = approverNodes.value .map(node => node.userId) .join(","); // 保存当前展开的行ID,以便发货后重新加载子表格数据 const currentExpandedKeys = [...expandedRowKeys.value]; const salesLedgerId = currentDeliveryRow.value.salesLedgerId; @@ -2139,8 +2750,7 @@ salesLedgerProductId: currentDeliveryRow.value.id, type: deliveryForm.value.type, approveUserIds, }) .then(() => { }).then(() => { proxy.$modal.msgSuccess("发货成功"); closeDeliveryDia(); // 刷新主表数据 @@ -2149,12 +2759,16 @@ if (currentExpandedKeys.length > 0) { // 使用 Promise.all 并行加载所有展开行的子表格数据 const loadPromises = currentExpandedKeys.map(ledgerId => { return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => { const index = tableData.value.findIndex((item) => item.id === ledgerId); return productList({ salesLedgerId: ledgerId, type: 1 }).then( res => { const index = tableData.value.findIndex( item => item.id === ledgerId ); if (index > -1) { tableData.value[index].children = res.data; } }); } ); }); Promise.all(loadPromises).then(() => { // 恢复展开状态 @@ -2162,7 +2776,7 @@ }); } }); }) }); } }); }; @@ -2182,7 +2796,7 @@ getList(); userListNoPage().then(res => { userList.value = res.data; }) }); getCurrentFactoryName(); }); </script> @@ -2193,19 +2807,19 @@ } ::v-deep .yellow { background-color: #FAF0DE; background-color: #faf0de; } ::v-deep .pink { background-color: #FAE1DE; background-color: #fae1de; } ::v-deep .red { background-color: #FAE1DE; background-color: #fae1de; } ::v-deep .purple{ background-color: #F4DEFA; background-color: #f4defa; } .table_list { @@ -2312,7 +2926,8 @@ border-collapse: collapse; border: 1px solid #000; th, td { th, td { border: 1px solid #000; padding: 6px; text-align: center;