| src/views/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/procurementManagement/receivingManagement/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/procurementManagement/receivingManagement/modal/ReceiptForm.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/views/index.vue
@@ -39,10 +39,10 @@ <div class="data-desc">æ¬æéå®é¢/å </div> <div class="data-value">{{ businessInfo.monthSaleMoney }}</div> </div> <div> <div class="data-desc">æªå¼ç¥¨éé¢/å </div> <div class="data-value">{{ businessInfo.monthSaleHaveMoney }}</div> </div> <!-- <div>--> <!-- <div class="data-desc">æªå¼ç¥¨éé¢/å </div>--> <!-- <div class="data-value">{{ businessInfo.monthSaleHaveMoney }}</div>--> <!-- </div>--> </div> </div> @@ -53,10 +53,10 @@ <div class="data-desc">æ¬æéè´é¢/å </div> <div class="data-value">{{ businessInfo.monthPurchaseMoney }}</div> </div> <div> <div class="data-desc">å¾ ä»æ¬¾éé¢/å </div> <div class="data-value">{{ businessInfo.monthPurchaseHaveMoney }}</div> </div> <!-- <div>--> <!-- <div class="data-desc">å¾ ä»æ¬¾éé¢/å </div>--> <!-- <div class="data-value">{{ businessInfo.monthPurchaseHaveMoney }}</div>--> <!-- </div>--> </div> </div> <div class="data-card inventory"> src/views/procurementManagement/receivingManagement/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,320 @@ <template> <div class="app-container"> <div class="search_form"> <el-form :model="searchForm" :inline="true"> <el-form-item label="ä¾åºååç§°ï¼"> <el-input v-model="searchForm.supplierName" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="éè´ååå·ï¼"> <el-input v-model="searchForm.purchaseContractNumber" style="width: 240px" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="éå®ååå·ï¼"> <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="项ç®åç§°ï¼"> <el-input v-model="searchForm.projectName" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="å½å ¥æ¥æï¼"> <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-form-item> </el-form> </div> <div class="table_list"> <el-table :data="tableData" border v-loading="tableLoading" :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 21.5em)" > <el-table-column type="expand"> <template #default="props"> <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable" > <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column label="产å大类" prop="productCategory" /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> <el-table-column label="åä½" prop="unit" /> <el-table-column label="æ°é" prop="quantity" /> <el-table-column label="å¯ç¨æ°é" prop="availableQuality" /> <el-table-column label="éè´§æ°é" prop="returnQuality" /> <el-table-column label="ç¨ç(%)" prop="taxRate" /> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> </el-table> </template> </el-table-column> <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column label="éè´ååå·" prop="purchaseContractNumber" width="160" show-overflow-tooltip /> <el-table-column label="éå®ååå·" prop="salesContractNo" width="160" show-overflow-tooltip /> <el-table-column label="ä¾åºååç§°" prop="supplierName" width="160" show-overflow-tooltip /> <el-table-column label="项ç®åç§°" prop="projectName" width="320" show-overflow-tooltip /> <el-table-column label="æ¶è´§ç¶æ" prop="status" width="100" show-overflow-tooltip> <template #default="scope"> <el-tag :type="getReceiptStatusType(scope.row.status)" size="small"> {{ receiptStatusText[scope.row.status] || 'æªç¥ç¶æ' }} </el-tag> </template> </el-table-column> <el-table-column label="审æ¹ç¶æ" prop="approvalStatus" width="100" show-overflow-tooltip> <template #default="scope"> <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small"> {{ approvalStatusText[scope.row.approvalStatus] || 'æªç¥ç¶æ' }} </el-tag> </template> </el-table-column> <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="100" show-overflow-tooltip /> <el-table-column label="仿¬¾æ¹å¼" prop="paymentMethod" width="100" show-overflow-tooltip /> <el-table-column label="ååéé¢(å )" prop="contractAmount" width="200" show-overflow-tooltip :formatter="formattedNumber" /> <el-table-column label="å½å ¥äºº" prop="recorderName" width="120" show-overflow-tooltip /> <el-table-column label="å½å ¥æ¥æ" prop="entryDate" width="100" show-overflow-tooltip /> <el-table-column label="夿³¨" prop="remarks" width="200" show-overflow-tooltip /> <el-table-column fixed="right" label="æä½" width="120" align="center"> <template #default="scope"> <el-button link type="primary" :disabled="isConfirmed(scope.row)" @click="confirmReceipt(scope.row)" > {{ isConfirmed(scope.row) ? 'å·²æ¶è´§' : '确认æ¶è´§' }} </el-button> </template> </el-table-column> </el-table> <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" :page="page.current" :limit="page.size" @pagination="paginationChange" /> </div> </div> </template> <script setup> import { onMounted, reactive, ref } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import dayjs from 'dayjs' import Pagination from '@/components/PIMTable/Pagination.vue' import { productList, purchaseListPage, updateApprovalStatus } from '@/api/procurementManagement/procurementLedger.js' const tableData = ref([]) const tableLoading = ref(false) const total = ref(0) const expandedRowKeys = ref([]) const receiptResultMap = ref({}) const page = reactive({ current: 1, size: 100 }) const searchForm = reactive({ supplierName: '', purchaseContractNumber: '', salesContractNo: '', projectName: '', entryDate: [], entryDateStart: undefined, entryDateEnd: undefined }) const approvalStatusText = { 1: 'å¾ å®¡æ ¸', 2: '审æ¹ä¸', 3: '审æ¹éè¿', 4: '审æ¹å¤±è´¥' } const receiptStatusText = { 1: 'å¾ æ¶è´§', 2: 'æ¶è´§ä¸', 3: 'å·²æ¶è´§' } const getApprovalStatusType = (status) => { const typeMap = { 1: 'info', 2: 'warning', 3: 'success', 4: 'danger' } return typeMap[status] } const getReceiptStatusType = (status) => { const typeMap = { 1: 'info', 2: 'warning', 3: 'success' } return typeMap[status] } const formattedNumber = (_row, _column, cellValue) => { const value = Number(cellValue) return Number.isFinite(value) ? value.toFixed(2) : '0.00' } const createSummary = (columns, data, sumFields) => { const sums = [] columns.forEach((column, index) => { if (index === 0) { sums[index] = 'å计' return } if (!sumFields.includes(column.property)) { sums[index] = '' return } const totalValue = data.reduce((sum, item) => sum + Number(item?.[column.property] || 0), 0) sums[index] = Number.isFinite(totalValue) ? totalValue.toFixed(2) : '0.00' }) return sums } const summarizeMainTable = ({ columns, data }) => createSummary(columns, data, ['contractAmount']) const summarizeChildrenTable = ({ columns, data }) => createSummary(columns, data, ['quantity', 'availableQuality', 'returnQuality', 'taxInclusiveUnitPrice', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']) const isConfirmed = (row) => Number(row?.status) === 3 || Boolean(receiptResultMap.value[row?.id]) const handleQuery = () => { page.current = 1 getList() } const changeDaterange = (value) => { if (value) { searchForm.entryDateStart = dayjs(value[0]).format('YYYY-MM-DD') searchForm.entryDateEnd = dayjs(value[1]).format('YYYY-MM-DD') } else { searchForm.entryDateStart = undefined searchForm.entryDateEnd = undefined } handleQuery() } const paginationChange = (obj) => { page.current = obj.page page.size = obj.limit getList() } const expandChange = async (row, expandedRows) => { if (expandedRows.length > 0) { expandedRowKeys.value = [] try { 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 || [] expandedRowKeys.value.push(row.id) } } catch (_error) { ElMessage.error('å 载产åå表失败') const index = expandedRows.findIndex((item) => item.id === row.id) if (index > -1) { expandedRows.splice(index, 1) } } } else { expandedRowKeys.value = [] } } const getList = () => { tableLoading.value = true const { entryDate, ...rest } = searchForm purchaseListPage({ ...rest, ...page, approvalStatus: 3 }) .then((res) => { tableData.value = (res.data?.records || []).map((record) => ({ ...record, children: [] })) total.value = res.data?.total || 0 expandedRowKeys.value = [] }) .finally(() => { tableLoading.value = false }) } const confirmReceipt = async (row) => { try { await ElMessageBox.confirm('æ¯å¦ç¡®è®¤æ¶è´§ï¼', '确认æ¶è´§', { type: 'warning', confirmButtonText: '确认', cancelButtonText: 'åæ¶' }) await updateApprovalStatus({ id: row.id, status: 3 }) receiptResultMap.value[row.id] = true ElMessage.success('确认æ¶è´§æå') getList() } catch (error) { if (error !== 'cancel' && error !== 'close') { ElMessage.error('确认æ¶è´§å¤±è´¥') } } } onMounted(() => { getList() }) </script> src/views/procurementManagement/receivingManagement/modal/ReceiptForm.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,338 @@ <template> <FormDialog v-model="dialogVisible" title="确认æ¶è´§" width="70%" operation-type="edit" @close="handleClose" @cancel="handleClose" @confirm="handleConfirm" > <el-form ref="formRef" :model="detailData" label-width="140px" label-position="top"> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="éè´ååå·ï¼"> <el-input :model-value="detailData.purchaseContractNumber || ''" disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="éå®ååå·ï¼"> <el-input :model-value="detailData.salesContractNo || ''" disabled /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="ä¾åºååç§°ï¼"> <el-input :model-value="detailData.supplierName || ''" disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="项ç®åç§°ï¼"> <el-input :model-value="detailData.projectName || ''" disabled /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="仿¬¾æ¹å¼ï¼"> <el-input :model-value="detailData.paymentMethod || ''" disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ç¾è®¢æ¥æï¼"> <el-input :model-value="detailData.executionDate || ''" disabled /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å½å ¥äººï¼"> <el-input :model-value="detailData.recorderName || ''" disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å½å ¥æ¥æï¼"> <el-input :model-value="detailData.entryDate || ''" disabled /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="审æ¹äººéæ©ï¼"> <div class="approver-nodes-container"> <div v-for="(name, index) in approverNames" :key="`${name}-${index}`" class="approver-node-item"> <div class="approver-node-header"> <span class="approver-node-label">审æ¹èç¹ {{ index + 1 }}</span> </div> <el-input :model-value="name" disabled /> </div> <el-empty v-if="!approverNames.length" description="ææ å®¡æ¹äººä¿¡æ¯" /> </div> </el-form-item> </el-col> </el-row> <el-form-item label="产åä¿¡æ¯ï¼"> <el-table :data="detailData.productData" border show-summary :summary-method="summarizeProductTable"> <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column label="产å大类" prop="productCategory" /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> <el-table-column label="åä½" prop="unit" width="70" /> <el-table-column label="æ°é" prop="quantity" width="90" /> <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="120" show-overflow-tooltip /> <el-table-column label="ç¨ç(%)" prop="taxRate" width="80" /> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" width="150" :formatter="formattedNumber" /> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" width="150" :formatter="formattedNumber" /> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" width="150" :formatter="formattedNumber" /> <el-table-column label="æ¯å¦è´¨æ£" prop="isChecked" width="100"> <template #default="scope"> <el-tag :type="scope.row.isChecked ? 'success' : 'info'"> {{ scope.row.isChecked ? 'æ¯' : 'å¦' }} </el-tag> </template> </el-table-column> <el-table-column label="æ¯å¦åæ ¼" min-width="180"> <template #default="scope"> <el-form-item :prop="`productData.${scope.$index}.isQualified`" :rules="[{ required: true, message: 'è¯·éæ©æ¯å¦åæ ¼', trigger: 'change' }]" class="inline-form-item" > <el-radio-group v-model="scope.row.isQualified"> <el-radio :label="1">åæ ¼</el-radio> <el-radio :label="2">ä¸åæ ¼</el-radio> </el-radio-group> </el-form-item> </template> </el-table-column> <el-table-column label="ä¸åæ ¼åå " min-width="220"> <template #default="scope"> <el-form-item :prop="`productData.${scope.$index}.reason`" :rules="[{ validator: (_rule, value, callback) => validateReason(scope.row, value, callback), trigger: 'blur' }]" class="inline-form-item" > <el-input v-model="scope.row.reason" :disabled="scope.row.isQualified !== 2" placeholder="ä¸åæ ¼æ¶è¯·å¡«ååå " clearable /> </el-form-item> </template> </el-table-column> </el-table> </el-form-item> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="夿³¨ï¼"> <el-input :model-value="detailData.remarks || ''" type="textarea" :rows="2" disabled /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="éä»¶ææï¼"> <div class="file-list"> <el-tag v-for="file in detailData.salesLedgerFiles" :key="file.id || file.fileName" class="file-tag"> {{ file.fileName || file.name }} </el-tag> <el-empty v-if="!detailData.salesLedgerFiles?.length" description="ææ éä»¶" /> </div> </el-form-item> </el-col> </el-row> </el-form> </FormDialog> </template> <script setup> import { computed, reactive, ref, watch } from 'vue' import { ElMessage } from 'element-plus' import FormDialog from '@/components/Dialog/FormDialog.vue' const props = defineProps({ modelValue: { type: Boolean, default: false }, record: { type: Object, default: () => ({}) } }) const emit = defineEmits(['update:modelValue', 'submit']) const formRef = ref() const dialogVisible = computed({ get: () => props.modelValue, set: (value) => emit('update:modelValue', value) }) const createDefaultDetail = () => ({ id: '', purchaseContractNumber: '', salesContractNo: '', supplierName: '', projectName: '', paymentMethod: '', executionDate: '', recorderName: '', entryDate: '', remarks: '', approveUserIds: '', approverNames: [], salesLedgerFiles: [], productData: [] }) const detailData = reactive(createDefaultDetail()) const approverNames = computed(() => detailData.approverNames || []) const formattedNumber = (_row, _column, cellValue) => { const value = Number(cellValue) return Number.isFinite(value) ? value.toFixed(2) : '0.00' } const summarizeProductTable = ({ columns, data }) => { const sumFields = ['quantity', 'taxInclusiveUnitPrice', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice'] const sums = [] columns.forEach((column, index) => { if (index === 0) { sums[index] = 'å计' return } if (!sumFields.includes(column.property)) { sums[index] = '' return } const total = data.reduce((sum, item) => sum + Number(item?.[column.property] || 0), 0) sums[index] = Number.isFinite(total) ? total.toFixed(2) : '0.00' }) return sums } const validateReason = (row, value, callback) => { if (row.isQualified === 2 && (!value || !value.trim())) { callback(new Error('ä¸åæ ¼æ¶å¿ 须填ååå ')) return } callback() } watch( () => props.record, (value) => { const nextData = createDefaultDetail() Object.assign(nextData, value || {}) nextData.productData = (value?.productData || []).map((item) => ({ ...item, isQualified: item.isQualified ?? undefined, reason: item.reason || '' })) nextData.salesLedgerFiles = value?.salesLedgerFiles || [] nextData.approverNames = value?.approverNames || [] Object.assign(detailData, nextData) }, { immediate: true, deep: true } ) const handleClose = () => { dialogVisible.value = false } const handleConfirm = async () => { if (!formRef.value) { return } try { await formRef.value.validate() const unqualifiedRows = detailData.productData.filter((item) => item.isQualified === 2) emit('submit', { id: detailData.id, purchaseContractNumber: detailData.purchaseContractNumber, supplierName: detailData.supplierName, projectName: detailData.projectName, productData: detailData.productData.map((item) => ({ id: item.id, specificationModel: item.specificationModel, quantity: item.quantity, isQualified: item.isQualified, reason: item.reason || '' })), unqualifiedQuantity: unqualifiedRows.reduce((sum, item) => sum + Number(item.quantity || 0), 0) }) dialogVisible.value = false } catch (_error) { ElMessage.warning('请å å®ææ¯æ¡äº§åçæ¶è´§å¤æ') } } </script> <style scoped lang="scss"> .approver-nodes-container { display: flex; flex-wrap: wrap; gap: 16px; width: 100%; } .approver-node-item { flex: 0 0 calc(33.333% - 12px); min-width: 220px; padding: 12px; background-color: #fff; border-radius: 4px; border: 1px solid #dcdfe6; } .approver-node-header { margin-bottom: 8px; } .approver-node-label { font-size: 13px; font-weight: 500; color: #606266; } .inline-form-item { margin-bottom: 0; } .file-list { display: flex; flex-wrap: wrap; gap: 8px; width: 100%; } .file-tag { margin-right: 0; } @media (max-width: 1200px) { .approver-node-item { flex: 0 0 calc(50% - 8px); } } @media (max-width: 768px) { .approver-node-item { flex: 0 0 100%; } } </style>