From 2d3530a7c11557a40807ad7c0b9e302ce6ce3a9f Mon Sep 17 00:00:00 2001
From: hsy <1029150275@qq.com>
Date: 星期五, 28 八月 2026 10:16:32 +0800
Subject: [PATCH] feat: 综合业务模块页面重构与交互完善
---
src/api/salesManagement/indicatorStats.js | 17
src/views/projectManagement/config/index.vue | 109 +
src/api/projectManagement/config.js | 18
src/api/projectManagement/project.js | 8
src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js | 88
src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue | 19
src/views/qualityManagement/finalInspection/index.vue | 33
src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue | 20
src/views/equipmentManagement/ledger/Form.vue | 293 ++
src/views/inventoryManagement/fuelCardManagement/index.vue | 430 ++++
src/views/inventoryManagement/vehicleFuelManagement/index.vue | 765 +++----
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js | 139 +
src/api/qualityManagement/rawMaterialInspection.js | 12
src/views/equipmentManagement/calibration/index.vue | 67
src/views/qualityManagement/metricMaintenance/index.vue | 13
src/views/projectManagement/Management/components/ContractListDialog.vue | 104 +
src/views/equipmentManagement/ledger/index.vue | 228 ++
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/components/DetailPanel.vue | 2
src/views/inventoryManagement/vehicleManagement/index.vue | 889 ++++----
src/views/equipmentManagement/upkeep/index.vue | 36
src/views/qualityManagement/processInspection/components/formDia.vue | 17
src/views/salesManagement/deliveryLedger/index.vue | 203 +
src/views/equipmentManagement/operationManagement/index.vue | 22
src/views/equipmentManagement/repair/Modal/MaintainModal.vue | 10
src/views/equipmentManagement/repair/Modal/RepairModal.vue | 77
src/views/inventoryManagement/issueManagement/index.vue | 4
src/views/salesManagement/salesQuotation/index.vue | 45
src/views/projectManagement/Management/components/ArchiveDialog.vue | 175 +
src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js | 26
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue | 21
src/views/salesManagement/lawsuitManagement/index.vue | 16
src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue | 7
src/api/device/statusChangeRecord.js | 9
.gitignore | 3
src/api/inventoryManagement/vehicle.js | 144 +
src/views/equipmentManagement/upkeep/Form/PlanModal.vue | 27
src/views/salesManagement/receiptPaymentLedger/index.vue | 836 +++++--
src/views/qualityManagement/metricMaintenance/ParamFormDialog.vue | 56
src/views/salesManagement/salesLedger/index.vue | 439 +++
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue | 68
src/api/system/user.js | 5
src/views/salesManagement/biddingManagement/index.vue | 8
src/views/qualityManagement/finalInspection/components/formDia.vue | 214 +
src/views/equipmentManagement/repair/index.vue | 11
src/components/PIMTable/PIMTable.vue | 29
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue | 119 +
src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/costReimburseUtils.js | 2
src/views/equipmentManagement/upkeep/Form/formDia.vue | 41
src/views/projectManagement/Management/index.vue | 26
49 files changed, 4,456 insertions(+), 1,494 deletions(-)
diff --git a/.gitignore b/.gitignore
index f979342..dfa1a4e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,6 @@
package-lock.json
yarn.lock
+
+/graphify-out
+build_graph.py
diff --git a/src/api/device/statusChangeRecord.js b/src/api/device/statusChangeRecord.js
new file mode 100644
index 0000000..a4a5367
--- /dev/null
+++ b/src/api/device/statusChangeRecord.js
@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function listStatusChangeRecord(query) {
+ return request({
+ url: '/device/statusChangeRecord/list',
+ method: 'get',
+ params: query
+ })
+}
diff --git a/src/api/inventoryManagement/vehicle.js b/src/api/inventoryManagement/vehicle.js
new file mode 100644
index 0000000..7df1888
--- /dev/null
+++ b/src/api/inventoryManagement/vehicle.js
@@ -0,0 +1,144 @@
+import request from '@/utils/request'
+
+// 杞﹁締淇℃伅
+export function listVehicleInfo(query) {
+ return request({
+ url: '/vehicle/info/listPage',
+ method: 'get',
+ params: query
+ })
+}
+export function getWarningList() {
+ return request({
+ url: '/vehicle/info/warningList',
+ method: 'get'
+ })
+}
+export function addVehicleInfo(data) {
+ return request({
+ url: '/vehicle/info',
+ method: 'post',
+ data: data
+ })
+}
+export function updateVehicleInfo(data) {
+ return request({
+ url: '/vehicle/info',
+ method: 'put',
+ data: data
+ })
+}
+export function delVehicleInfo(id) {
+ return request({
+ url: '/vehicle/info/' + id,
+ method: 'delete'
+ })
+}
+
+// 鍔犳补鍗�
+export function listFuelCardPage(query) {
+ return request({
+ url: '/vehicle/fuelCard/listPage',
+ method: 'get',
+ params: query
+ })
+}
+export function listAllFuelCard() {
+ return request({
+ url: '/vehicle/fuelCard/listAll',
+ method: 'get'
+ })
+}
+export function addFuelCard(data) {
+ return request({
+ url: '/vehicle/fuelCard',
+ method: 'post',
+ data: data
+ })
+}
+export function updateFuelCard(data) {
+ return request({
+ url: '/vehicle/fuelCard',
+ method: 'put',
+ data: data
+ })
+}
+export function delFuelCard(id) {
+ return request({
+ url: '/vehicle/fuelCard/' + id,
+ method: 'delete'
+ })
+}
+
+// 鍔犳补璁板綍
+export function listFuelRecord(query) {
+ return request({
+ url: '/vehicle/fuelRecord/listPage',
+ method: 'get',
+ params: query
+ })
+}
+export function addFuelRecord(data) {
+ return request({
+ url: '/vehicle/fuelRecord',
+ method: 'post',
+ data: data
+ })
+}
+export function updateFuelRecord(data) {
+ return request({
+ url: '/vehicle/fuelRecord',
+ method: 'put',
+ data: data
+ })
+}
+export function delFuelRecord(id) {
+ return request({
+ url: '/vehicle/fuelRecord/' + id,
+ method: 'delete'
+ })
+}
+
+
+// 鍒嗛〉鏌ヨ鍔犳补鍗″厖鍊艰褰�
+export function listRecharge(query) {
+ return request({
+ url: '/vehicle/fuelCardRecharge/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 鏂板鍔犳补鍗″厖鍊�
+export function addRecharge(data) {
+ return request({
+ url: '/vehicle/fuelCardRecharge/add',
+ method: 'post',
+ data: data
+ })
+}
+
+// 鍒犻櫎鍔犳补鍗″厖鍊�
+export function delRecharge(id) {
+ return request({
+ url: '/vehicle/fuelCardRecharge/' + id,
+ method: 'delete'
+ })
+}
+
+
+// 鎻愪氦鍔犳补璁板綍
+export function submitFuelRecord(id) {
+ return request({
+ url: '/vehicle/fuelRecord/submit/' + id,
+ method: 'post'
+ })
+}
+
+// 鎾ゅ洖鍔犳补璁板綍
+export function revokeFuelRecord(id) {
+ return request({
+ url: '/vehicle/fuelRecord/revoke/' + id,
+ method: 'post'
+ })
+}
diff --git a/src/api/projectManagement/config.js b/src/api/projectManagement/config.js
new file mode 100644
index 0000000..3d77e4c
--- /dev/null
+++ b/src/api/projectManagement/config.js
@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 鏌ヨ椤圭洰绠$悊鍏ㄥ眬閰嶇疆
+export function getConfig() {
+ return request({
+ url: '/projectManagement/config',
+ method: 'get'
+ })
+}
+
+// 鏇存柊椤圭洰绠$悊鍏ㄥ眬閰嶇疆
+export function updateConfig(data) {
+ return request({
+ url: '/projectManagement/config',
+ method: 'put',
+ data: data
+ })
+}
diff --git a/src/api/projectManagement/project.js b/src/api/projectManagement/project.js
index 2d6eff1..c373542 100644
--- a/src/api/projectManagement/project.js
+++ b/src/api/projectManagement/project.js
@@ -116,3 +116,11 @@
method: 'post'
})
}
+
+// 鑾峰彇椤圭洰褰掓。闄勪欢
+export function getProjectAttachments(id) {
+ return request({
+ url: `/projectManagement/info/attachments/${id}`,
+ method: 'get'
+ })
+}
diff --git a/src/api/qualityManagement/rawMaterialInspection.js b/src/api/qualityManagement/rawMaterialInspection.js
index dcb3869..7727ef3 100644
--- a/src/api/qualityManagement/rawMaterialInspection.js
+++ b/src/api/qualityManagement/rawMaterialInspection.js
@@ -45,7 +45,7 @@
})
}
-// 鎻愪氦鍘熸潗鏂欐楠�
+// 涓嬭浇鍘熸潗鏂欐楠學ord
export function downloadQualityInspect(data) {
return request({
url: '/quality/qualityInspect/down',
@@ -55,3 +55,13 @@
})
}
+// 涓嬭浇鍘熸潗鏂欐楠孭DF
+export function downloadQualityInspectPdf(data) {
+ return request({
+ url: '/quality/qualityInspect/downPdf',
+ method: 'post',
+ data: data,
+ responseType: "blob",
+ })
+}
+
diff --git a/src/api/salesManagement/indicatorStats.js b/src/api/salesManagement/indicatorStats.js
index c29eac3..4c19f29 100644
--- a/src/api/salesManagement/indicatorStats.js
+++ b/src/api/salesManagement/indicatorStats.js
@@ -19,6 +19,15 @@
});
}
+// 椤圭洰寰�鏉ユ眹鎬伙紙椤跺眰锛�
+export function projectTransactions(query) {
+ return request({
+ url: "/metricStatistics/projectTransactions",
+ method: "get",
+ params: query,
+ });
+}
+
// 瀹㈡埛寰�鏉ュ垪琛�
export function customewTransactions(query) {
return request({
@@ -36,3 +45,11 @@
params: query,
});
}
+
+// 澶х洏鎬昏
+export function transactionsSummary() {
+ return request({
+ url: "/metricStatistics/transactionsSummary",
+ method: "get"
+ });
+}
diff --git a/src/api/system/user.js b/src/api/system/user.js
index 431f6b0..541cba7 100644
--- a/src/api/system/user.js
+++ b/src/api/system/user.js
@@ -135,10 +135,11 @@
})
}
// 鏌ヨ鐢ㄦ埛鍒楄〃
-export function userListNoPageByTenantId() {
+export function userListNoPageByTenantId(params) {
return request({
url: '/system/user/userListNoPageByTenantId',
- method: 'get'
+ method: 'get',
+ params
})
}
diff --git a/src/components/PIMTable/PIMTable.vue b/src/components/PIMTable/PIMTable.vue
index 74615af..b8c6792 100644
--- a/src/components/PIMTable/PIMTable.vue
+++ b/src/components/PIMTable/PIMTable.vue
@@ -117,7 +117,33 @@
@click.stop>
<template v-for="(o, key) in item.operation"
:key="key">
- <el-button v-show="o.type != 'upload'"
+ <el-dropdown v-if="o.type == 'dropdown'"
+ v-show="o.showHide ? o.showHide(scope.row) : true"
+ trigger="click"
+ @command="(command) => o.commandFun(command, scope.row)"
+ style="margin-left: 12px; margin-right: 12px; vertical-align: middle;">
+ <el-button :disabled="isOperationDisabled(o, scope.row)"
+ :plain="o.plain"
+ type="primary"
+ :style="{
+ color: getOperationColor(o, scope.row),
+ fontWeight: 'bold',
+ }"
+ link>
+ {{ o.name }}<el-icon class="el-icon--right"><ArrowDown /></el-icon>
+ </el-button>
+ <template #dropdown>
+ <el-dropdown-menu>
+ <el-dropdown-item v-for="(dItem, dIdx) in (typeof o.dropdownItems === 'function' ? o.dropdownItems(scope.row) : o.dropdownItems)"
+ :key="dIdx"
+ :command="dItem.command"
+ :disabled="dItem.disabled ? dItem.disabled(scope.row) : false">
+ {{ dItem.label }}
+ </el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+ <el-button v-show="o.type != 'upload' && o.type != 'dropdown'"
v-if="o.showHide ? o.showHide(scope.row) : true"
:disabled="isOperationDisabled(o, scope.row)"
:plain="o.plain"
@@ -204,6 +230,7 @@
import pagination from "./Pagination.vue";
import { computed, ref, inject, getCurrentInstance } from "vue";
import { ElMessage } from "element-plus";
+ import { ArrowDown } from "@element-plus/icons-vue";
// 鑾峰彇鍏ㄥ眬鐨� uploadHeader
const { proxy } = getCurrentInstance();
diff --git a/src/views/equipmentManagement/calibration/index.vue b/src/views/equipmentManagement/calibration/index.vue
index b04da2e..0d5b9cf 100644
--- a/src/views/equipmentManagement/calibration/index.vue
+++ b/src/views/equipmentManagement/calibration/index.vue
@@ -50,7 +50,13 @@
@selection-change="handleSelectionChange"
:tableLoading="tableLoading"
@pagination="pagination"
- ></PIMTable>
+ >
+ <template #validUntilSlot="{ row }">
+ <span>{{ row.validUntil ? row.validUntil.split(' ')[0] : '' }}</span>
+ <el-tag v-if="isWarning(row.validUntil)" type="warning" class="ml10">鍗冲皢鍒版湡</el-tag>
+ <el-tag v-else-if="isOverdue(row.validUntil)" type="danger" class="ml10">宸查�炬湡</el-tag>
+ </template>
+ </PIMTable>
</div>
<calibration-dia ref="calibrationDia" @close="handleQuery"></calibration-dia>
</div>
@@ -59,6 +65,7 @@
<script setup>
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";
+import dayjs from "dayjs";
import useUserStore from "@/store/modules/user.js";
import CalibrationDia from "@/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue";
import {ledgerRecordListPage, ledgerRecordDelete} from "@/api/equipmentManagement/calibration.js";
@@ -119,9 +126,46 @@
width:200
},
{
- label: "鏈夋晥鏈�",
+ label: "鏈夋晥鏈�(澶�)",
prop: "valid",
width: 100,
+ },
+ {
+ label: "鏈夋晥鏈熻嚦",
+ prop: "validUntil",
+ dataType: "slot",
+ slot: "validUntilSlot",
+ width: 160,
+ },
+ {
+ label: "婧簮/鏍告煡绫诲瀷",
+ prop: "traceabilityType",
+ width: 150,
+ },
+ {
+ label: "瀹炴柦鏈烘瀯",
+ prop: "implementingAgency",
+ width: 150,
+ },
+ {
+ label: "璇佷功缂栧彿",
+ prop: "certificateNumber",
+ width: 150,
+ },
+ {
+ label: "鏍稿績缁撴灉",
+ prop: "coreResult",
+ width: 150,
+ },
+ {
+ label: "缁撴灉纭",
+ prop: "resultConfirmation",
+ width: 150,
+ },
+ {
+ label: "纭浜�",
+ prop: "confirmerName",
+ width: 120,
},
{
label: "褰曞叆浜�",
@@ -197,12 +241,27 @@
const getList = () => {
tableLoading.value = true;
ledgerRecordListPage({ ...searchForm.value, ...page }).then((res) => {
- tableLoading.value = false;
tableData.value = res.data.records;
page.total = res.data.total;
+ tableLoading.value = false;
}).catch((err) => {
tableLoading.value = false;
- })
+ });
+};
+
+const isWarning = (validUntil) => {
+ if (!validUntil) return false;
+ const now = dayjs().startOf('day');
+ const until = dayjs(validUntil).startOf('day');
+ const diffDays = until.diff(now, 'day');
+ return diffDays >= 0 && diffDays <= 30;
+};
+
+const isOverdue = (validUntil) => {
+ if (!validUntil) return false;
+ const now = dayjs().startOf('day');
+ const until = dayjs(validUntil).startOf('day');
+ return until.isBefore(now);
};
// 鎵撳紑妫�瀹氭牎鍑嗗脊妗�
diff --git a/src/views/equipmentManagement/ledger/Form.vue b/src/views/equipmentManagement/ledger/Form.vue
index f1d21f2..4405f90 100644
--- a/src/views/equipmentManagement/ledger/Form.vue
+++ b/src/views/equipmentManagement/ledger/Form.vue
@@ -1,6 +1,24 @@
<template>
<el-form :model="form" label-width="120px" :rules="formRules" ref="formRef">
+ <el-divider content-position="left">鍩虹淇℃伅</el-divider>
<el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="璁惧缂栧彿" prop="uniqueCode">
+ <div style="display: flex; gap: 10px; width: 100%;">
+ <el-checkbox
+ v-model="form.autoGenerateUniqueCode"
+ v-if="operationType === 'add'"
+ >鑷姩鐢熸垚
+ </el-checkbox>
+ <el-input
+ v-model="form.uniqueCode"
+ :placeholder="form.autoGenerateUniqueCode ? '淇濆瓨鍚庤嚜鍔ㄧ敓鎴�' : '璇疯緭鍏ヨ澶囩紪鍙�'"
+ :disabled="form.autoGenerateUniqueCode || operationType === 'view'"
+ clearable
+ />
+ </div>
+ </el-form-item>
+ </el-col>
<el-col :span="12">
<el-form-item label="璁惧鍚嶇О" prop="deviceName">
<el-input v-model="form.deviceName" placeholder="璇疯緭鍏ヨ澶囧悕绉�" />
@@ -38,8 +56,8 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="渚涘簲鍟�" prop="supplierName">
- <el-input v-model="form.supplierName" placeholder="璇疯緭鍏ヤ緵搴斿晢" />
+ <el-form-item label="鍑哄巶缂栧彿" prop="factoryNumber">
+ <el-input v-model="form.factoryNumber" placeholder="璇疯緭鍏ュ嚭鍘傜紪鍙�" />
</el-form-item>
</el-col>
<el-col :span="12">
@@ -48,31 +66,68 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="鍗曚綅" prop="unit">
- <el-input v-model="form.unit" placeholder="璇疯緭鍏ュ崟浣�" />
+ <el-form-item label="璐d换浜�" prop="responsiblePerson">
+ <el-input v-model="form.responsiblePerson" placeholder="璇疯緭鍏ヨ矗浠讳汉" />
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="鍚敤鎶樻棫" prop="isDepr">
- <el-switch v-model="form.isDepr" :active-value="1" :inactive-value="2" />
+ <el-form-item label="寤烘。鏃ユ湡" prop="filingDate">
+ <el-date-picker
+ style="width: 100%"
+ v-model="form.filingDate"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨寤烘。鏃ユ湡"
+ clearable
+ />
</el-form-item>
</el-col>
- <el-col :span="12" v-if="form.isDepr === 1">
- <el-form-item label="姣忓勾鎶樻棫閲戦" prop="annualDepreciationAmount">
- <el-input-number
- :step="0.01"
- :min="0"
+ </el-row>
+
+ <el-divider content-position="left">閲囪喘淇℃伅</el-divider>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="渚涘簲鍟�" prop="supplierName">
+ <el-input v-model="form.supplierName" placeholder="璇疯緭鍏ヤ緵搴斿晢" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍒堕�犲巶鍟�" prop="manufacturer">
+ <el-input v-model="form.manufacturer" placeholder="璇疯緭鍏ュ埗閫犲巶鍟�" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璐疆鏃ユ湡" prop="purchaseDate">
+ <el-date-picker
style="width: 100%"
- v-model="form.annualDepreciationAmount"
- placeholder="璇疯緭鍏ユ瘡骞存姌鏃ч噾棰�"
+ v-model="form.purchaseDate"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨璐疆鏃ユ湡"
+ clearable
/>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璧勪骇鍘熷��" prop="originalAssetValue">
+ <el-input-number :step="0.01" :min="0" style="width: 100%"
+ v-model="form.originalAssetValue"
+ placeholder="璇疯緭鍏ヨ祫浜у師鍊�"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍗曚綅" prop="unit">
+ <el-input v-model="form.unit" placeholder="璇疯緭鍏ュ崟浣�" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="鏁伴噺" prop="number">
<el-input-number :min="1" style="width: 100%"
v-model="form.number"
- disabled
+ disabled
placeholder="璇疯緭鍏ユ暟閲�"
@change="mathNum"
/>
@@ -125,37 +180,80 @@
/>
</el-form-item>
</el-col>
- <!-- <el-col :span="12">
- <el-form-item label="褰曞叆浜�" prop="createUser">
- <el-input v-model="form.createUser" placeholder="璇疯緭鍏ュ綍鍏ヤ汉" />
+ </el-row>
+
+ <el-divider content-position="left">鎶�鏈弬鏁�</el-divider>
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="涓昏鎶�鏈弬鏁�" prop="technicalParameters">
+ <el-input type="textarea" :rows="3" v-model="form.technicalParameters" placeholder="璇疯緭鍏ヤ富瑕佹妧鏈弬鏁�" />
</el-form-item>
- </el-col> -->
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="涓昏鐢ㄩ��" prop="mainPurpose">
+ <el-input type="textarea" :rows="3" v-model="form.mainPurpose" placeholder="璇疯緭鍏ヤ富瑕佺敤閫�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-divider content-position="left">杩愯绠$悊</el-divider>
+ <el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="褰曞叆鏃ユ湡" prop="createTime">
+ <el-form-item label="璁惧鐘舵��" prop="status">
+ <el-select
+ v-model="form.status"
+ placeholder="璇烽�夋嫨"
+ :disabled="operationType === 'edit'"
+ style="width: 100%"
+ >
+ <el-option label="鍋滅敤" :value="0" />
+ <el-option label="鍚敤" :value="1" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍚敤鏃ユ湡" prop="activationDate">
<el-date-picker
style="width: 100%"
- v-model="form.createTime"
+ v-model="form.activationDate"
format="YYYY-MM-DD"
- value-format="YYYY-MM-DD HH:mm:ss"
+ value-format="YYYY-MM-DD"
type="date"
- placeholder="璇烽�夋嫨褰曞叆鏃ユ湡"
+ placeholder="璇烽�夋嫨鍚敤鏃ユ湡"
clearable
/>
</el-form-item>
</el-col>
- <el-col :span="12">
- <el-form-item label="棰勮杩愯鏃堕棿" prop="planRuntimeTime">
- <el-date-picker
- style="width: 100%"
- v-model="form.planRuntimeTime"
- format="YYYY-MM-DD"
- value-format="YYYY-MM-DD"
- type="date"
- placeholder="璇烽�夋嫨褰曞叆鏃ユ湡"
- clearable
- />
- </el-form-item>
- </el-col>
+ <el-col :span="12">
+ <el-form-item label="棰勮杩愯鏃堕棿" prop="planRuntimeTime">
+ <el-date-picker
+ style="width: 100%"
+ v-model="form.planRuntimeTime"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨棰勮杩愯鏃堕棿"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍚敤鎶樻棫" prop="isDepr">
+ <el-switch v-model="form.isDepr" :active-value="1" :inactive-value="2" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12" v-if="form.isDepr === 1">
+ <el-form-item label="姣忓勾鎶樻棫閲戦" prop="annualDepreciationAmount">
+ <el-input-number
+ :step="0.01"
+ :min="0"
+ style="width: 100%"
+ v-model="form.annualDepreciationAmount"
+ placeholder="璇疯緭鍏ユ瘡骞存姌鏃ч噾棰�"
+ />
+ </el-form-item>
+ </el-col>
+
<el-col :span="24">
<el-form-item label="璁惧鍥剧墖" prop="storageBlobDTOs">
<AttachmentUploadImage
@@ -172,7 +270,6 @@
<script setup>
import useFormData from "@/hooks/useFormData";
-// import useUserStore from "@/store/modules/user";
import { getLedgerById } from "@/api/equipmentManagement/ledger";
import dayjs from "dayjs";
import {
@@ -190,7 +287,7 @@
name: "璁惧鍙拌处琛ㄥ崟",
});
const formRef = ref(null);
-const operationType = ref('');
+const operationType = ref('add');
// 璁惧绫诲瀷鍥哄畾閫夐」
const deviceTypeOptions = ref([
'鐢熶骇璁惧',
@@ -200,47 +297,77 @@
'鍏朵粬璁惧'
]);
const formRules = {
- deviceName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
- deviceModel: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
- type: [{ required: true, trigger: "change", message: "璇烽�夋嫨鎴栬緭鍏ヨ澶囩被鍨�" }],
- supplierName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
- unit: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
- number: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
- taxIncludingPriceUnit: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
- taxRate: [{ required: true, trigger: "change", message: "璇疯緭鍏�" }],
- planRuntimeTime: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }],
- annualDepreciationAmount: [
- {
- validator: (rule, value, callback) => {
- if (form.isDepr === 1 && (value === undefined || value === null || value === '')) {
- callback(new Error('鍚敤鎶樻棫鏃讹紝璇疯緭鍏ユ瘡骞存姌鏃ч噾棰�'));
- } else {
- callback();
- }
- },
- trigger: "blur"
- }
- ],
+ uniqueCode: [
+ {
+ validator: (rule, value, callback) => {
+ if (!form.autoGenerateUniqueCode && (value === undefined || value === null || value === '')) {
+ callback(new Error('璇疯緭鍏ヨ澶囩紪鍙�'));
+ } else {
+ callback();
+ }
+ },
+ trigger: "blur"
+ }
+ ],
+ deviceName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+ deviceModel: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+ type: [{ required: true, trigger: "change", message: "璇烽�夋嫨鎴栬緭鍏ヨ澶囩被鍨�" }],
+ supplierName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+ unit: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+ number: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+ taxIncludingPriceUnit: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+ taxRate: [{ required: true, trigger: "change", message: "璇疯緭鍏�" }],
+ planRuntimeTime: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }],
+ status: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }],
+ annualDepreciationAmount: [
+ {
+ validator: (rule, value, callback) => {
+ if (form.isDepr === 1 && (value === undefined || value === null || value === '')) {
+ callback(new Error('鍚敤鎶樻棫鏃讹紝璇疯緭鍏ユ瘡骞存姌鏃ч噾棰�'));
+ } else {
+ callback();
+ }
+ },
+ trigger: "blur"
+ }
+ ],
}
const { form, resetForm } = useFormData({
+ // Basic Info
+ autoGenerateUniqueCode: true,
+ uniqueCode: undefined, // 鍞竴鎬х紪鍙�
deviceName: undefined, // 璁惧鍚嶇О
deviceModel: undefined, // 瑙勬牸鍨嬪彿
deviceBrand: undefined, // 璁惧鍝佺墝
type: undefined, // 璁惧绫诲瀷
- supplierName: undefined, // 渚涘簲鍟�
+ factoryNumber: undefined, // 鍑哄巶缂栧彿
storageLocation: undefined, // 瀛樻斁浣嶇疆
- isDepr: 2, // 鏄惁鍚敤鎶樻棫 1-鏄� 2-鍚�
- annualDepreciationAmount: undefined, // 姣忓勾鎶樻棫閲戦
+ responsiblePerson: undefined, // 璐d换浜�
+ filingDate: dayjs().format("YYYY-MM-DD"), // 寤烘。鏃ユ湡
+
+ // Purchase Info
+ supplierName: undefined, // 渚涘簲鍟�
+ manufacturer: undefined, // 鍒堕�犲巶鍟�
+ purchaseDate: undefined, // 璐疆鏃ユ湡
+ originalAssetValue: undefined, // 璧勪骇鍘熷��
unit: undefined, // 鍗曚綅
number: 1, // 鏁伴噺
taxIncludingPriceUnit: undefined, // 鍚◣鍗曚环
taxIncludingPriceTotal: undefined, // 鍚◣鎬讳环
taxRate: undefined, // 绋庣巼
unTaxIncludingPriceTotal: undefined, // 涓嶅惈绋庢�讳环
- // createUser: useUserStore().nickName, // 褰曞叆浜�
- createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 褰曞叆鏃ユ湡
- planRuntimeTime: dayjs().format("YYYY-MM-DD"), // 褰曞叆鏃ユ湡
+
+ // Technical Specs
+ technicalParameters: undefined, // 涓昏鎶�鏈弬鏁�
+ mainPurpose: undefined, // 涓昏鐢ㄩ��
+
+ // Operation Info
+ status: 1, // 璁惧鐘舵�� (0鍋滅敤 1鍚敤)
+ activationDate: undefined, // 鍚敤鏃ユ湡
+ planRuntimeTime: dayjs().format("YYYY-MM-DD"), // 棰勮杩愯鏃堕棿
+ isDepr: 2, // 鏄惁鍚敤鎶樻棫 1-鏄� 2-鍚�
+ annualDepreciationAmount: undefined, // 姣忓勾鎶樻棫閲戦
storageBlobDTOs: undefined, // 璁惧鍥剧墖鎻愪氦
storageBlobVOs: undefined, // 璁惧鍥剧墖灞曠ず
});
@@ -256,26 +383,47 @@
});
const loadForm = async (id) => {
- if (id) {
- operationType.value = 'edit'
- }
+ if (id) {
+ operationType.value = 'edit';
+ } else {
+ operationType.value = 'add';
+ }
const { code, data } = await getLedgerById(id);
if (code == 200) {
+ form.autoGenerateUniqueCode = false;
+ form.uniqueCode = data.uniqueCode;
form.deviceName = data.deviceName;
form.deviceModel = data.deviceModel;
form.deviceBrand = data.deviceBrand;
form.type = data.type;
- form.supplierName = data.supplierName;
+ form.factoryNumber = data.factoryNumber;
form.storageLocation = data.storageLocation;
- form.isDepr = data.isDepr;
- form.annualDepreciationAmount = data.annualDepreciationAmount;
+ form.responsiblePerson = data.responsiblePerson;
+ form.filingDate = data.filingDate;
+
+ form.supplierName = data.supplierName;
+ form.manufacturer = data.manufacturer;
+ if (data.purchaseDate) {
+ form.purchaseDate = dayjs(data.purchaseDate).format('YYYY-MM-DD');
+ }
+ form.originalAssetValue = data.originalAssetValue;
form.unit = data.unit;
form.number = 1;
form.taxIncludingPriceUnit = data.taxIncludingPriceUnit;
form.taxIncludingPriceTotal = data.taxIncludingPriceTotal;
form.taxRate = data.taxRate;
form.unTaxIncludingPriceTotal = data.unTaxIncludingPriceTotal;
- form.createTime = data.createTime;
+
+ form.technicalParameters = data.technicalParameters;
+ form.mainPurpose = data.mainPurpose;
+
+ form.status = data.status != null ? data.status : 1;
+ if (data.activationDate) {
+ form.activationDate = dayjs(data.activationDate).format('YYYY-MM-DD');
+ }
+ form.isDepr = data.isDepr;
+ form.annualDepreciationAmount = data.annualDepreciationAmount;
+
// 棰勮杩愯鏃堕棿锛氬悗绔繑鍥炲悗杞负 YYYY-MM-DD 浠ヤ究鏃ユ湡閫夋嫨鍣ㄦ纭睍绀�
if (data.planRuntimeTime) {
form.planRuntimeTime = dayjs(data.planRuntimeTime).format('YYYY-MM-DD');
@@ -319,6 +467,7 @@
// 閲嶇疆琛ㄥ崟鏁版嵁鍜屾牎楠岀姸鎬�
const resetFormAndValidate = () => {
resetForm();
+ operationType.value = 'add'; // 閲嶇疆涓烘柊澧�
clearValidate();
};
@@ -328,6 +477,6 @@
resetForm,
clearValidate,
resetFormAndValidate,
- formRef,
+ formRef,
});
</script>
diff --git a/src/views/equipmentManagement/ledger/index.vue b/src/views/equipmentManagement/ledger/index.vue
index af10532..fbc9869 100644
--- a/src/views/equipmentManagement/ledger/index.vue
+++ b/src/views/equipmentManagement/ledger/index.vue
@@ -1,6 +1,15 @@
<template>
<div class="app-container">
<el-form :model="filters" :inline="true">
+ <el-form-item label="璁惧缂栧彿">
+ <el-input
+ v-model="filters.uniqueCode"
+ style="width: 200px"
+ placeholder="璇疯緭鍏ヨ澶囩紪鍙�"
+ clearable
+ @change="getTableData"
+ />
+ </el-form-item>
<el-form-item label="璁惧鍚嶇О">
<el-input
v-model="filters.deviceName"
@@ -113,6 +122,7 @@
<!-- 璇︽儏瀵硅瘽妗� -->
<el-dialog v-model="detailDialogVisible" title="璁惧鍙拌处璇︽儏" width="60%" draggable>
<el-descriptions :column="2" border>
+ <el-descriptions-item label="璁惧缂栧彿">{{ detailData.uniqueCode }}</el-descriptions-item>
<el-descriptions-item label="璁惧鍚嶇О">{{ detailData.deviceName }}</el-descriptions-item>
<el-descriptions-item label="瑙勬牸鍨嬪彿">{{ detailData.deviceModel }}</el-descriptions-item>
<el-descriptions-item label="璁惧鍝佺墝">{{ detailData.deviceBrand }}</el-descriptions-item>
@@ -148,13 +158,88 @@
<el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
</template>
</el-dialog>
+
+ <!-- 鐘舵�佸彉鏇村璇濇 -->
+ <el-dialog v-model="statusDialog.visible" title="璁惧鐘舵�佸彉鏇寸敵璇�" width="500px" draggable>
+ <el-form label-width="100px">
+ <el-form-item label="褰撳墠鐘舵��">
+ <el-tag :type="statusDialog.row?.status == 1 ? 'success' : 'danger'">
+ {{ statusDialog.row?.status == 1 ? '鍚敤' : '鍋滅敤' }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鐩爣鐘舵��" required>
+ <el-radio-group v-model="statusDialog.targetStatus">
+ <el-radio :label="1">鍚敤</el-radio>
+ <el-radio :label="0">鍋滅敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="鍙樻洿鍘熷洜" required>
+ <el-input type="textarea" :rows="3" v-model="statusDialog.reason" placeholder="璇疯緭鍏ョ姸鎬佸彉鏇村師鍥�" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="statusDialog.visible = false">鍙栨秷</el-button>
+ <el-button type="primary" :loading="statusDialog.loading" @click="submitStatusChange">鎻愪氦瀹℃壒</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 鐘舵�佸彉鏇磋褰曞璇濇 -->
+ <el-dialog v-model="recordDialog.visible" title="璁惧鐘舵�佸彉鏇磋褰�" width="800px" draggable>
+ <el-table v-loading="recordDialog.loading" :data="recordDialog.list" border>
+ <el-table-column label="鍙樻洿鏃堕棿" prop="createTime" width="160" />
+ <el-table-column label="鍘熺姸鎬�" width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.originalStatus == 1 ? 'success' : 'danger'">
+ {{ scope.row.originalStatus == 1 ? '鍚敤' : '鍋滅敤' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鐩爣鐘舵��" width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.targetStatus == 1 ? 'success' : 'danger'">
+ {{ scope.row.targetStatus == 1 ? '鍚敤' : '鍋滅敤' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍙樻洿鍘熷洜" prop="reason" show-overflow-tooltip />
+ <el-table-column label="鐢宠浜�" prop="createUserName" width="100" />
+ <el-table-column label="瀹℃壒鐘舵��" width="100">
+ <template #default="scope">
+ <el-tag v-if="scope.row.approvalStatus === 'PENDING'" type="warning">瀹℃壒涓�</el-tag>
+ <el-tag v-else-if="scope.row.approvalStatus === 'APPROVED'" type="success">宸查�氳繃</el-tag>
+ <el-tag v-else-if="scope.row.approvalStatus === 'REJECTED'" type="danger">宸查┏鍥�</el-tag>
+ <span v-else>{{ scope.row.approvalStatus }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="椹冲洖鍘熷洜" prop="rejectReason" show-overflow-tooltip>
+ <template #default="scope">
+ <span v-if="scope.row.approvalStatus === 'REJECTED'">{{ scope.row.rejectReason || '--' }}</span>
+ <span v-else>--</span>
+ </template>
+ </el-table-column>
+ </el-table>
+ <div style="display: flex; justify-content: flex-end; margin-top: 15px;">
+ <el-pagination
+ v-model:current-page="recordDialog.queryParams.pageNum"
+ v-model:page-size="recordDialog.queryParams.pageSize"
+ :total="recordDialog.total"
+ layout="total, prev, pager, next"
+ @current-change="getRecordList"
+ />
+ </div>
+ <template #footer>
+ <el-button @click="recordDialog.visible = false">鍏抽棴</el-button>
+ </template>
+ </el-dialog>
</div>
</template>
<script setup>
import { usePaginationApi } from "@/hooks/usePaginationApi";
-// import { Search } from "@element-plus/icons-vue";
import { getLedgerPage, delLedger, getLedgerById } from "@/api/equipmentManagement/ledger";
+import { listStatusChangeRecord } from "@/api/device/statusChangeRecord";
+import { saveApprovalInstance } from "@/api/officeProcessAutomation/approvalInstance";
+import { listApprovalTemplate, getApprovalTemplateDetail, TEMPLATE_TYPE_CUSTOM } from "@/api/officeProcessAutomation/approvalTemplate";
import { onMounted, getCurrentInstance, ref, reactive } from "vue";
import Modal from "./Modal.vue";
import { ElMessageBox, ElMessage } from "element-plus";
@@ -204,6 +289,7 @@
} = usePaginationApi(
getLedgerPage,
{
+ uniqueCode: undefined,
deviceName: undefined,
deviceModel: undefined,
supplierName: undefined,
@@ -211,6 +297,10 @@
entryDateEnd: undefined,
},
[
+ {
+ label: "璁惧缂栧彿",
+ prop: "uniqueCode",
+ },
{
label: "璁惧鍚嶇О",
prop: "deviceName",
@@ -240,18 +330,34 @@
prop: "number",
},
{
+ label: "璁惧鐘舵��",
+ prop: "status",
+ width: 100,
+ dataType: "tag",
+ formatData: (v) => v == 1 ? '鍚敤' : '鍋滅敤',
+ formatType: (v) => v == 1 ? 'success' : 'danger',
+ },
+ {
+ label: "鍚敤鏃ユ湡",
+ prop: "activationDate",
+ width: 120,
+ formatData: (v) => {
+ if (!v) return '';
+ if (v.includes(' ')) return v.split(' ')[0];
+ return v;
+ },
+ },
+ {
label: "褰曞叆浜�",
prop: "createUser",
},
{
- label: "褰曞叆鏃ユ湡",
- prop: "createTime",
+ label: "寤烘。鏃ユ湡",
+ prop: "filingDate",
+ width: 120,
formatData: (v) => {
if (!v) return '';
- // 濡傛灉鍖呭惈鏃跺垎绉掞紝鍙彇鏃ユ湡閮ㄥ垎
- if (v.includes(' ')) {
- return v.split(' ')[0];
- }
+ if (v.includes(' ')) return v.split(' ')[0];
return v;
},
},
@@ -266,6 +372,18 @@
name: "璇︽儏",
clickFun: (row) => {
handleDetail(row);
+ },
+ },
+ {
+ name: "鐘舵�佸彉鏇�",
+ clickFun: (row) => {
+ openStatusChange(row);
+ },
+ },
+ {
+ name: "鍙樻洿璁板綍",
+ clickFun: (row) => {
+ openRecordDialog(row);
},
},
{
@@ -303,6 +421,102 @@
detailDialogVisible.value = true;
}
};
+
+const statusDialog = reactive({ visible: false, row: null, targetStatus: 1, reason: '', loading: false });
+
+const openStatusChange = (row) => {
+ statusDialog.row = row;
+ statusDialog.targetStatus = row.status == 1 ? 0 : 1;
+ statusDialog.reason = '';
+ statusDialog.visible = true;
+};
+
+const submitStatusChange = async () => {
+ if (!statusDialog.reason.trim()) {
+ ElMessage.error("璇峰~鍐欑姸鎬佸彉鏇村師鍥�");
+ return;
+ }
+ if (statusDialog.targetStatus === statusDialog.row.status) {
+ ElMessage.warning("鐩爣鐘舵�佷笌褰撳墠鐘舵�佷竴鑷�");
+ return;
+ }
+ statusDialog.loading = true;
+ try {
+ const listRes = await listApprovalTemplate(TEMPLATE_TYPE_CUSTOM);
+ const templates = listRes.data?.records || listRes.data || [];
+ const tpl = templates.find(t => t.businessType === 19 && (t.enabled === 1 || t.enabled === true));
+ if (!tpl) {
+ ElMessage.error("鏈壘鍒板惎鐢ㄧ殑璁惧鐘舵�佸彉鏇村鎵规ā鏉�");
+ return;
+ }
+ const detailRes = await getApprovalTemplateDetail(tpl.id);
+ const detail = detailRes.data;
+
+ const payload = {
+ templateId: detail.id,
+ templateName: detail.templateName,
+ businessType: 19,
+ templateSnapshot: JSON.stringify(detail),
+ title: `璁惧鐘舵�佸彉鏇村鎵� - ${statusDialog.row.deviceName}`,
+ reason: statusDialog.reason,
+ businessId: statusDialog.row.id,
+ formConfig: JSON.stringify({
+ targetStatus: statusDialog.targetStatus,
+ targetStatusName: statusDialog.targetStatus == 1 ? '鍚敤' : '鍋滅敤',
+ deviceId: statusDialog.row.id,
+ deviceName: statusDialog.row.deviceName,
+ reason: statusDialog.reason
+ }),
+ flowNodes: detail.flowNodes || []
+ };
+
+ const res = await saveApprovalInstance(payload);
+ if (res.code === 200) {
+ ElMessage.success("宸插彂璧风姸鎬佸彉鏇村鎵�");
+ statusDialog.visible = false;
+ getTableData();
+ }
+ } catch (error) {
+ console.error(error);
+ } finally {
+ statusDialog.loading = false;
+ }
+};
+
+const recordDialog = reactive({
+ visible: false,
+ loading: false,
+ list: [],
+ total: 0,
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ deviceId: null
+ }
+});
+
+const openRecordDialog = (row) => {
+ recordDialog.queryParams.deviceId = row.id;
+ recordDialog.queryParams.pageNum = 1;
+ recordDialog.visible = true;
+ getRecordList();
+};
+
+const getRecordList = async () => {
+ recordDialog.loading = true;
+ try {
+ const res = await listStatusChangeRecord(recordDialog.queryParams);
+ if (res.code === 200) {
+ recordDialog.list = res.data.records;
+ recordDialog.total = res.data.total;
+ }
+ } catch (error) {
+ console.error(error);
+ } finally {
+ recordDialog.loading = false;
+ }
+};
+
const changePage = ({ page, limit }) => {
pagination.currentPage = page;
pagination.pageSize = limit;
diff --git a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue b/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
index 923dd2c..298ec84 100644
--- a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
+++ b/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -47,6 +47,7 @@
type="date"
placeholder="璇烽�夋嫨"
clearable
+ @change="calculateValidUntil"
/>
</el-form-item>
</el-col>
@@ -67,6 +68,85 @@
</el-row>
<el-row :gutter="30">
<el-col :span="12">
+ <el-form-item label="鏈夋晥鏈熻嚦锛�" prop="validUntil">
+ <el-input
+ v-model="form.validUntil"
+ placeholder="鑷姩璁$畻"
+ disabled
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="婧簮/鏍告煡绫诲瀷锛�" prop="traceabilityType">
+ <el-input
+ v-model="form.traceabilityType"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="瀹炴柦鏈烘瀯锛�" prop="implementingAgency">
+ <el-input
+ v-model="form.implementingAgency"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璇佷功缂栧彿锛�" prop="certificateNumber">
+ <el-input
+ v-model="form.certificateNumber"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鏍稿績缁撴灉锛�" prop="coreResult">
+ <el-input
+ v-model="form.coreResult"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="缁撴灉纭锛�" prop="resultConfirmation">
+ <el-input
+ v-model="form.resultConfirmation"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="纭浜猴細" prop="confirmerId">
+ <el-select
+ v-model="form.confirmerId"
+ placeholder="璇烽�夋嫨"
+ filterable
+ default-first-option
+ :reserve-keyword="false"
+ clearable
+ >
+ <el-option
+ v-for="item in userList"
+ :key="item.userId"
+ :label="item.nickName"
+ :value="item.userId"
+ ></el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
<el-form-item label="褰曞叆浜猴細" prop="userId">
<el-select
v-model="form.userId"
@@ -85,6 +165,8 @@
</el-select>
</el-form-item>
</el-col>
+ </el-row>
+ <el-row :gutter="30">
<el-col :span="12">
<el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
<el-date-picker
@@ -128,6 +210,7 @@
</template>
<script setup>
+import dayjs from "dayjs";
import {ref, reactive, toRefs, getCurrentInstance} from "vue";
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
@@ -150,7 +233,14 @@
recordDate: "",
userId: "",
entryDate: "",
- tempFileIds: []
+ tempFileIds: [],
+ traceabilityType: "",
+ implementingAgency: "",
+ certificateNumber: "",
+ coreResult: "",
+ resultConfirmation: "",
+ validUntil: "",
+ confirmerId: ""
},
rules: {
code: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
@@ -211,6 +301,13 @@
if (type === "edit") {
form.value.valid = row.valid;
form.value.recordDate = row.recordDate;
+ form.value.traceabilityType = row.traceabilityType;
+ form.value.implementingAgency = row.implementingAgency;
+ form.value.certificateNumber = row.certificateNumber;
+ form.value.coreResult = row.coreResult;
+ form.value.resultConfirmation = row.resultConfirmation;
+ form.value.validUntil = row.validUntil;
+ form.value.confirmerId = row.confirmerId;
fileList.value = row.commonFiles;
}
if(type === "add"){
@@ -219,6 +316,14 @@
if(type === "verifying"){
form.value.valid = row.valid;
form.value.recordDate = row.mostDate;
+ // Initialize empty for new verifying record
+ form.value.traceabilityType = "";
+ form.value.implementingAgency = "";
+ form.value.certificateNumber = "";
+ form.value.coreResult = "";
+ form.value.resultConfirmation = "";
+ form.value.confirmerId = "";
+ calculateValidUntil();
}
form.value.id = row.id;
@@ -232,12 +337,14 @@
const handleValidInput = (value) => {
if (value === '' || value === null || value === undefined) {
form.value.valid = '';
+ calculateValidUntil();
return;
}
// 杞崲涓哄瓧绗︿覆骞剁Щ闄ゆ墍鏈夐潪鏁板瓧瀛楃锛堝寘鎷礋鍙枫�佸皬鏁扮偣绛夛級
const numStr = String(value).replace(/[^0-9]/g, '');
if (numStr === '') {
form.value.valid = '';
+ calculateValidUntil();
return;
}
const numValue = parseInt(numStr, 10);
@@ -247,6 +354,16 @@
} else {
form.value.valid = '';
}
+ calculateValidUntil();
+}
+
+// 鑷姩璁$畻鏈夋晥鏈熻嚦
+const calculateValidUntil = () => {
+ if (form.value.recordDate && form.value.valid) {
+ form.value.validUntil = dayjs(form.value.recordDate).add(form.value.valid, 'day').format('YYYY-MM-DD');
+ } else {
+ form.value.validUntil = '';
+ }
}
const submitForm = () => {
diff --git a/src/views/equipmentManagement/operationManagement/index.vue b/src/views/equipmentManagement/operationManagement/index.vue
index 99c3bd1..f920bd4 100644
--- a/src/views/equipmentManagement/operationManagement/index.vue
+++ b/src/views/equipmentManagement/operationManagement/index.vue
@@ -61,9 +61,9 @@
:type="getDeviceStatusType(scope.row.status)"
size="small"
>
- <el-icon v-if="scope.row.status === '杩愯涓�'"><VideoPlay /></el-icon>
+ <el-icon v-if="String(scope.row.status) === '1'"><VideoPlay /></el-icon>
<el-icon v-else><VideoPause /></el-icon>
- {{ scope.row.status || '鏈煡' }}
+ {{ String(scope.row.status) === '1' ? '杩愯涓�' : '鍋滄杩愯' }}
</el-tag>
</template>
</el-table-column>
@@ -126,7 +126,7 @@
<!-- 姝e父鐘舵�佹椂鏄剧ず瀵瑰簲鐨勬搷浣滄寜閽� -->
<template v-else>
<el-button
- v-if="scope.row.status === '杩愯涓�'"
+ v-if="String(scope.row.status) === '1'"
type="danger"
size="small"
@click="changeDeviceStatus(scope.row, '鍋滄杩愯')"
@@ -186,9 +186,9 @@
// 鏍规嵁璁惧鐘舵�佺瓫閫�
if (deviceFilter.value !== 'all') {
if (deviceFilter.value === 'start') {
- filtered = filtered.filter(device => device.status === '杩愯涓�')
+ filtered = filtered.filter(device => String(device.status) === '1')
} else if (deviceFilter.value === 'stop') {
- filtered = filtered.filter(device => device.status === '鍋滄杩愯')
+ filtered = filtered.filter(device => String(device.status) === '0')
}
}
@@ -264,7 +264,7 @@
}
const statusStr = String(row?.status ?? '').trim()
- const isRunning = statusStr === '杩愯涓�' || statusStr === '1'
+ const isRunning = statusStr === '1'
const endRaw = pickEndTime(row)
const hasEnd = hasMeaningfulEnd(endRaw)
@@ -289,7 +289,7 @@
// 妫�鏌ヨ澶囨槸鍚﹁秴鏃舵湭鍚姩
const isOverdue = (device) => {
- if (!device.planRuntimeTime || device.status === '杩愯涓�' || device.startRuntimeTime) {
+ if (!device.planRuntimeTime || String(device.status) === '1' || device.startRuntimeTime) {
return false
}
@@ -330,12 +330,12 @@
// 鏇存柊璁惧鐘舵�佸拰鐩稿叧鏃堕棿瀛楁
if (status === '鍚姩杩愯') {
- device.status = '杩愯涓�'
+ device.status = 1
device.startRuntimeTime = currentTime
device.endRuntimeTime = null // 娓呯┖缁撴潫鏃堕棿
device.runtimeDuration = null // 娓呯┖杩愯鏃堕暱
} else {
- device.status = '鍋滄杩愯'
+ device.status = 0
device.endRuntimeTime = currentTime
// 璁$畻杩愯鏃堕暱
if (device.startRuntimeTime) {
@@ -370,9 +370,9 @@
}
const getDeviceStatusType = (status) => {
- if (status === '杩愯涓�') {
+ if (String(status) === '1') {
return 'success'
- } else if (status === '鍋滄杩愯') {
+ } else if (String(status) === '0') {
return 'danger'
} else {
return 'info'
diff --git a/src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue b/src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue
index 6d61a9f..c8b2a22 100644
--- a/src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/AcceptanceModal.vue
@@ -8,6 +8,14 @@
@close="handleCancel"
>
<el-form :model="form" :rules="rules" label-width="100px">
+ <!-- 閰嶄欢浣跨敤鎯呭喌灞曠ず -->
+ <el-form-item label="閰嶄欢浣跨敤鏄庣粏" v-if="sparePartsDetails.length > 0">
+ <el-table :data="sparePartsDetails" size="small" border style="width: 100%">
+ <el-table-column prop="sparePartName" label="閰嶄欢鍚嶇О" />
+ <el-table-column prop="quantity" label="浣跨敤鏁伴噺" width="100" />
+ </el-table>
+ </el-form-item>
+
<el-form-item label="楠屾敹浜�" prop="acceptanceName">
<el-select
v-model="form.acceptanceName"
@@ -50,7 +58,7 @@
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
import { userListNoPageByTenantId } from "@/api/system/user.js";
-import { repairAcceptance } from "@/api/equipmentManagement/repair";
+import { repairAcceptance, getRepairById } from "@/api/equipmentManagement/repair";
import dayjs from "dayjs";
defineOptions({
@@ -63,6 +71,7 @@
const loading = ref(false);
const repairId = ref(null);
const userList = ref([]);
+const sparePartsDetails = ref([]);
const form = reactive({
acceptanceName: undefined,
@@ -92,10 +101,16 @@
const open = async (row) => {
repairId.value = row.id;
visible.value = true;
- // 閲嶇疆琛ㄥ崟
form.acceptanceName = undefined;
form.acceptanceTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
form.acceptanceRemark = undefined;
+ sparePartsDetails.value = [];
+ try {
+ const { data } = await getRepairById(row.id);
+ if (data && data.sparePartsUseListDetails) {
+ sparePartsDetails.value = data.sparePartsUseListDetails;
+ }
+ } catch(e) {}
await loadUserList();
};
diff --git a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
index b0b09f0..4408c31 100644
--- a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -14,6 +14,12 @@
<el-form-item label="缁翠慨缁撴灉">
<el-input v-model="form.maintenanceResult" placeholder="璇疯緭鍏ョ淮淇粨鏋�" />
</el-form-item>
+ <el-form-item label="鍘熷洜鍒嗘瀽">
+ <el-input v-model="form.reasonAnalysis" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ師鍥犲垎鏋�" />
+ </el-form-item>
+ <el-form-item label="澶勭悊鎺柦">
+ <el-input v-model="form.handlingMeasures" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ鐞嗘帾鏂�" />
+ </el-form-item>
<el-form-item label="缁翠慨鐘舵��">
<el-select v-model="form.status">
<el-option label="寰呮姤淇�" :value="0"></el-option>
@@ -97,6 +103,8 @@
const { form, resetForm } = useFormData({
maintenanceName: undefined, // 缁翠慨鍚嶇О
maintenanceResult: undefined, // 缁翠慨缁撴灉
+ reasonAnalysis: undefined, // 鍘熷洜鍒嗘瀽
+ handlingMeasures: undefined, // 澶勭悊鎺柦
maintenanceTime: undefined, // 缁翠慨鏃ユ湡
status: 0,
sparePartsIds: [],
@@ -114,6 +122,8 @@
const setForm = (data) => {
form.maintenanceName = data.maintenanceName ?? userStore.nickName;
form.maintenanceResult = data.maintenanceResult;
+ form.reasonAnalysis = data.reasonAnalysis;
+ form.handlingMeasures = data.handlingMeasures;
form.maintenanceTime =
data.maintenanceTime
? dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss")
diff --git a/src/views/equipmentManagement/repair/Modal/RepairModal.vue b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
index 4a10071..6a934c9 100644
--- a/src/views/equipmentManagement/repair/Modal/RepairModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -58,6 +58,18 @@
</el-row>
<el-row>
<el-col :span="12">
+ <el-form-item label="鎶ヤ慨绫诲瀷">
+ <el-select v-model="form.repairType"
+ placeholder="璇烽�夋嫨鎶ヤ慨绫诲瀷"
+ :disabled="operationType === 'view'">
+ <el-option v-for="dict in device_repair_type"
+ :key="dict.value"
+ :label="dict.label"
+ :value="Number(dict.value)"></el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
<el-form-item label="缁翠慨浜�">
<el-input v-model="form.maintenanceName"
placeholder="璇疯緭鍏ョ淮淇汉濮撳悕"
@@ -76,7 +88,26 @@
:value="1"></el-option>
<el-option label="澶辫触"
:value="2"></el-option>
+ <el-option label="寰呴獙鏀�"
+ :value="3"></el-option>
</el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12" v-if="form.status !== 0">
+ <el-form-item label="缁翠慨鏃堕棿">
+ <el-input v-model="form.maintenanceTime"
+ disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row v-if="form.status !== 0">
+ <el-col :span="24">
+ <el-form-item label="缁翠慨缁撴灉">
+ <el-input v-model="form.maintenanceResult"
+ :rows="2"
+ type="textarea"
+ placeholder="鏆傛棤缁翠慨缁撴灉"
+ :disabled="operationType === 'view'" />
</el-form-item>
</el-col>
</el-row>
@@ -114,6 +145,37 @@
</el-form-item>
</el-col>
</el-row>
+ <el-row v-if="form.status !== 0">
+ <el-col :span="24">
+ <el-form-item label="鍘熷洜鍒嗘瀽">
+ <el-input v-model="form.reasonAnalysis"
+ :rows="2"
+ type="textarea"
+ placeholder="璇疯緭鍏ュ師鍥犲垎鏋�"
+ :disabled="operationType === 'view'" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="澶勭悊鎺柦">
+ <el-input v-model="form.handlingMeasures"
+ :rows="2"
+ type="textarea"
+ placeholder="璇疯緭鍏ュ鐞嗘帾鏂�"
+ :disabled="operationType === 'view'" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 閰嶄欢浣跨敤鏄庣粏灞曠ず -->
+ <el-row v-if="sparePartsDetails.length > 0">
+ <el-col :span="24">
+ <el-form-item label="閰嶄欢浣跨敤鏄庣粏">
+ <el-table :data="sparePartsDetails" size="small" border style="width: 100%">
+ <el-table-column prop="sparePartName" label="閰嶄欢鍚嶇О" />
+ <el-table-column prop="quantity" label="浣跨敤鏁伴噺" width="100" />
+ </el-table>
+ </el-form-item>
+ </el-col>
+ </el-row>
<el-row v-if="operationType !== 'view'"
:gutter="30">
<el-col :span="24">
@@ -147,6 +209,8 @@
});
const emits = defineEmits(["ok"]);
+ const { proxy } = getCurrentInstance();
+ const { device_repair_type } = proxy.useDict("device_repair_type");
const id = ref();
const visible = ref(false);
@@ -163,6 +227,7 @@
const userStore = useUserStore();
const deviceOptions = ref([]);
const fileList = ref([]);
+ const sparePartsDetails = ref([]);
const loadDeviceName = async () => {
const { data } = await getDeviceLedger();
@@ -174,12 +239,17 @@
deviceName: undefined, // 璁惧鍚嶇О
deviceModel: undefined, // 瑙勬牸鍨嬪彿
repairTime: dayjs().format("YYYY-MM-DD"), // 鎶ヤ慨鏃ユ湡锛岄粯璁ゅ綋澶�
+ repairType: undefined, // 鎶ヤ慨绫诲瀷
repairName: userStore.nickName, // 鎶ヤ慨浜�
remark: undefined, // 鏁呴殰鐜拌薄
+ reasonAnalysis: undefined, // 鍘熷洜鍒嗘瀽
+ handlingMeasures: undefined, // 澶勭悊鎺柦
status: 0, // 鎶ヤ慨鐘舵��
machineryCategory: undefined,
storageBlobDTOs: [],
maintenanceName: undefined, // 缁翠慨浜�
+ maintenanceTime: undefined,
+ maintenanceResult: undefined,
});
const setDeviceModel = deviceId => {
@@ -192,15 +262,21 @@
form.deviceName = data.deviceName;
form.deviceModel = data.deviceModel;
form.repairTime = data.repairTime;
+ form.repairType = data.repairType;
form.repairName = data.repairName;
form.remark = data.remark;
+ form.reasonAnalysis = data.reasonAnalysis;
+ form.handlingMeasures = data.handlingMeasures;
form.status = data.status;
form.machineryCategory = data.machineryCategory;
form.storageBlobDTOs = data.storageBlobVOs || [];
form.maintenanceName = data.maintenanceName;
+ form.maintenanceTime = data.maintenanceTime;
+ form.maintenanceResult = data.maintenanceResult;
form.acceptanceName = data.acceptanceName;
form.acceptanceTime = data.acceptanceTime;
form.acceptanceRemark = data.acceptanceRemark;
+ sparePartsDetails.value = data.sparePartsUseListDetails || [];
};
const sendForm = async () => {
@@ -234,6 +310,7 @@
operationType.value = "add";
visible.value = true;
fileList.value = [];
+ sparePartsDetails.value = [];
await nextTick();
await loadDeviceName();
};
diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index f1573cb..b93d750 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -96,6 +96,9 @@
<el-tag v-if="row.status === 0"
type="warning">寰呯淮淇�</el-tag>
</template>
+ <template #repairTypeRef="{ row }">
+ <dict-tag :options="device_repair_type" :value="row.repairType" />
+ </template>
<template #operation="{ row }">
<el-button type="primary"
link
@@ -171,6 +174,7 @@
});
const { proxy } = getCurrentInstance();
+ const { device_repair_type } = proxy.useDict("device_repair_type");
// 妯℃�佹瀹炰緥
const repairModalRef = ref();
@@ -222,6 +226,13 @@
prop: "repairName",
},
{
+ label: "鎶ヤ慨绫诲瀷",
+ align: "center",
+ prop: "repairType",
+ dataType: "slot",
+ slot: "repairTypeRef",
+ },
+ {
label: "鐘舵��",
align: "center",
prop: "status",
diff --git a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
index 0fcccb2..5619d67 100644
--- a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
@@ -35,6 +35,11 @@
placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
type="text" />
</el-form-item>
+ <el-form-item label="杩愯鐘跺喌">
+ <el-input v-model="form.runningStatus"
+ placeholder="璇疯緭鍏ヨ繍琛岀姸鍐�"
+ type="text" />
+ </el-form-item>
<el-form-item label="璁惧澶囦欢">
<el-select v-model="form.sparePartsIds"
:loading="loadingSparePartOptions"
@@ -104,6 +109,7 @@
maintenanceActuallyName: undefined, // 瀹為檯淇濆吇浜�
maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
maintenanceResult: undefined, // 淇濆吇缁撴灉
+ runningStatus: undefined, // 杩愯鐘跺喌
status: 0, // 淇濆吇鐘舵��
sparePartsIds: [],
storageBlobDTOs: [],
@@ -126,6 +132,7 @@
? dayjs(data.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss")
: dayjs().format("YYYY-MM-DD HH:mm:ss");
form.maintenanceResult = data.maintenanceResult;
+ form.runningStatus = data.runningStatus;
form.status = 1; // 榛樿鐘舵�佷负瀹岀粨
// multiple 閫夋嫨鍣ㄨ姹傛暟缁勶紱鍚庣甯歌繑鍥� "1,2,3"
if (Array.isArray(data?.sparePartsIds)) {
diff --git a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
index 8a9cd98..f4608fd 100644
--- a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
@@ -57,9 +57,30 @@
</el-form-item>
<el-form-item v-if="id" label="淇濅慨鐘舵��">
<el-select v-model="form.status">
- <el-option label="寰呬繚淇�" :value="0"></el-option>
+ <el-option label="寰呬繚鍏�" :value="0"></el-option>
<el-option label="瀹岀粨" :value="1"></el-option>
<el-option label="澶辫触" :value="2"></el-option>
+ </el-select>
+ </el-form-item>
+ <el-form-item label="淇濆吇绫诲瀷" prop="maintenanceType">
+ <el-select v-model="form.maintenanceType" placeholder="璇烽�夋嫨淇濆吇绫诲瀷" clearable>
+ <el-option label="鏃ュ父浣跨敤" value="1"/>
+ <el-option label="瀹氭湡淇濆吇" value="2"/>
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鎵ц浜�" prop="executorId">
+ <el-select
+ v-model="form.executorId"
+ filterable
+ placeholder="璇烽�夋嫨鎵ц浜�"
+ clearable
+ >
+ <el-option
+ v-for="item in userList"
+ :label="item.nickName"
+ :value="item.userId"
+ :key="item.userId"
+ />
</el-select>
</el-form-item>
<el-form-item label="淇濆吇浜�">
@@ -132,6 +153,8 @@
machineryCategory: undefined,
storageBlobDTOs: [],
maintenancePerson: undefined, // 淇濆吇浜�
+ maintenanceType: undefined,
+ executorId: undefined
});
const setDeviceModel = (deviceId) => {
@@ -151,6 +174,8 @@
form.status = data.status;
form.machineryCategory = data.machineryCategory;
form.maintenancePerson = data.maintenancePerson;
+ form.maintenanceType = data.maintenanceType;
+ form.executorId = data.executorId;
if (data.maintenancePlanTime) {
form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
"YYYY-MM-DD HH:mm:ss"
diff --git a/src/views/equipmentManagement/upkeep/Form/formDia.vue b/src/views/equipmentManagement/upkeep/Form/formDia.vue
index 6856ae1..08c0a83 100644
--- a/src/views/equipmentManagement/upkeep/Form/formDia.vue
+++ b/src/views/equipmentManagement/upkeep/Form/formDia.vue
@@ -70,7 +70,7 @@
<el-form-item label="淇濆吇椤圭洰" prop="machineryCategory">
<el-input
v-model.trim="form.machineryCategory"
- placeholder="璇疯緭鍏ヤ繚鍏婚」鐩�"
+ placeholder="璇疯緭鍏ュ伐浣滃唴瀹规弿杩�(淇濆吇椤圭洰)"
maxlength="100"
clearable
/>
@@ -84,6 +84,33 @@
maxlength="100"
clearable
/>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="淇濆吇绫诲瀷" prop="maintenanceType">
+ <el-select v-model="form.maintenanceType" 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="executorId">
+ <el-select
+ v-model="form.executorId"
+ filterable
+ placeholder="璇烽�夋嫨鎵ц浜�"
+ clearable
+ >
+ <el-option
+ v-for="item in userList"
+ :label="item.nickName"
+ :value="item.userId"
+ :key="item.userId"
+ />
+ </el-select>
</el-form-item>
</el-col>
</el-row>
@@ -184,13 +211,17 @@
time: '',
deviceModel: undefined, // 瑙勬牸鍨嬪彿
registrationDate: '',
- maintenancePerson: '' // 淇濆吇浜�
+ maintenancePerson: '', // 淇濆吇浜�
+ maintenanceType: undefined,
+ executorId: undefined
},
rules: {
taskId: [{ required: true, message: "璇烽�夋嫨璁惧", trigger: "change" },],
inspector: [{ required: true, message: "璇烽�夋嫨褰曞叆浜�", trigger: "blur" },],
registrationDate: [{ required: true, message: "璇烽�夋嫨鐧昏鏃堕棿", trigger: "change" }],
- machineryCategory: [{ required: true, message: "璇疯緭鍏ヤ繚鍏婚」鐩�", trigger: "blur" }]
+ machineryCategory: [{ required: true, message: "璇疯緭鍏ヤ繚鍏婚」鐩�", trigger: "blur" }],
+ maintenanceType: [{ required: true, message: "璇烽�夋嫨淇濆吇绫诲瀷", trigger: "change" }],
+ executorId: [{ required: true, message: "璇烽�夋嫨鎵ц浜�", trigger: "change" }]
}
})
const { form, rules } = toRefs(data)
@@ -271,7 +302,9 @@
time: '',
deviceModel: undefined,
registrationDate: '',
- maintenancePerson: ''
+ maintenancePerson: '',
+ maintenanceType: undefined,
+ executorId: undefined
}
}
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 245a162..4e629a1 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -241,6 +241,7 @@
deviceMaintenanceTaskList,
deviceMaintenanceTaskDel,
} from "@/api/equipmentManagement/upkeep";
+ import { userListNoPageByTenantId } from "@/api/system/user.js";
import dayjs from "dayjs";
const { proxy } = getCurrentInstance();
@@ -261,6 +262,13 @@
const fileListDialogRef = ref(null);
const fileDialogVisible = ref(false);
const currentMaintenanceTaskId = ref(null);
+
+ const userList = ref([]);
+ const getUserName = (userId) => {
+ if (!userId) return "--";
+ const user = userList.value.find(u => String(u.userId) === String(userId));
+ return user ? user.nickName : userId;
+ };
// 淇濆吇璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
const filters = reactive({
@@ -304,6 +312,18 @@
prop: "machineryCategory",
minWidth: 120,
formatData: cell => cell || "--",
+ },
+ {
+ label: "淇濆吇绫诲瀷",
+ prop: "maintenanceType",
+ minWidth: 100,
+ formatData: cell => (cell === "1" ? "鏃ュ父浣跨敤" : cell === "2" ? "瀹氭湡淇濆吇" : "--")
+ },
+ {
+ prop: "executorId",
+ label: "鎵ц浜�",
+ minWidth: 100,
+ formatData: cell => getUserName(cell)
},
{
prop: "frequencyType",
@@ -391,6 +411,18 @@
align: "center",
prop: "machineryCategory",
formatData: cell => cell || "--",
+ },
+ {
+ label: "淇濆吇绫诲瀷",
+ align: "center",
+ prop: "maintenanceType",
+ formatData: cell => (cell === "1" ? "鏃ュ父浣跨敤" : cell === "2" ? "瀹氭湡淇濆吇" : "--")
+ },
+ {
+ label: "鎵ц浜�",
+ align: "center",
+ prop: "executorId",
+ formatData: cell => getUserName(cell)
},
// {
// label: "褰曞叆鏃ユ湡",
@@ -628,7 +660,9 @@
fileDialogVisible.value = true;
};
- onMounted(() => {
+ onMounted(async () => {
+ const { data } = await userListNoPageByTenantId();
+ userList.value = data || [];
// 鏍规嵁榛樿婵�娲荤殑 Tab 璋冪敤瀵瑰簲鐨勬煡璇㈡帴鍙�
if (activeTab.value === "scheduled") {
getScheduledTableData();
diff --git a/src/views/inventoryManagement/fuelCardManagement/index.vue b/src/views/inventoryManagement/fuelCardManagement/index.vue
new file mode 100644
index 0000000..f6a6d30
--- /dev/null
+++ b/src/views/inventoryManagement/fuelCardManagement/index.vue
@@ -0,0 +1,430 @@
+<template>
+ <div class="app-container">
+
+
+ <!-- 鏌ヨ鏉′欢 -->
+ <div class="search_form">
+ <div style="flex: 1; padding-right: 20px;">
+ <el-row :gutter="16">
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鍔犳补鍗″彿锛�</span>
+ <el-input v-model="queryParams.cardNumber" placeholder="璇疯緭鍏ュ姞娌瑰崱鍙�" clearable style="flex: 1;" @keyup.enter.native="handleQuery" />
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鎵�灞為儴闂細</span>
+ <el-tree-select v-model="queryParams.deptId" :data="deptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="璇烽�夋嫨閮ㄩ棬" check-strictly clearable filterable style="flex: 1;" />
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-col>
+ </el-row>
+ </div>
+ <div>
+ <el-button type="primary" icon="Plus" @click="handleAdd">鏂板鍔犳补鍗�</el-button>
+ </div>
+ </div>
+ <!-- 琛ㄦ牸 -->
+ <div class="table_list">
+ <el-table
+ v-loading="loading"
+ :data="tableData"
+ border
+ style="width: 100%"
+ height="calc(100vh - 22em)"
+ :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+ >
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="cardNumber" label="鍔犳补鍗″彿" min-width="150" />
+ <el-table-column prop="deptName" label="鎵�灞為儴闂�" min-width="150" show-overflow-tooltip />
+ <el-table-column prop="issuer" label="鍙戝崱鍗曚綅" min-width="120" />
+ <el-table-column prop="initialBalance" label="鏈熷垵浣欓" min-width="100" align="right" />
+ <el-table-column prop="currentBalance" label="褰撳墠浣欓" min-width="100" align="right" />
+ <el-table-column prop="status" label="鐘舵��" min-width="100" align="center">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === 'ACTIVE' ? 'success' : 'danger'">
+ {{ scope.row.status === 'ACTIVE' ? '姝e父' : '娉ㄩ攢' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="remark" label="澶囨敞" min-width="150" show-overflow-tooltip />
+ <el-table-column label="鎿嶄綔" fixed="right" width="200" align="center">
+ <template #default="scope">
+ <el-button type="success" link size="small" @click="handleRecharge(scope.row)">鍏呭�肩鐞�</el-button>
+ <el-button type="primary" link size="small" @click="handleUpdate(scope.row)">缂栬緫</el-button>
+ <el-button v-if="scope.row.status !== 'CANCELLED'" type="warning" link size="small" @click="handleCancel(scope.row)">娉ㄩ攢</el-button>
+ <el-button type="danger" link size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </div>
+<!-- 鏂板/缂栬緫寮圭獥 -->
+ <el-dialog :title="title" v-model="open" width="500px" append-to-body>
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+ <el-form-item label="鍔犳补鍗″彿" prop="cardNumber">
+ <el-input v-model="form.cardNumber" placeholder="璇疯緭鍏ュ姞娌瑰崱鍙�" />
+ </el-form-item>
+ <el-form-item label="鎵�灞為儴闂�" prop="deptId">
+ <el-tree-select
+ v-model="form.deptId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ value-key="id"
+ placeholder="璇烽�夋嫨閮ㄩ棬"
+ check-strictly
+ clearable
+ filterable
+ style="width: 100%"
+ />
+ </el-form-item>
+ <el-form-item label="鍙戝崱鍗曚綅" prop="issuer">
+ <el-input v-model="form.issuer" placeholder="璇疯緭鍏ュ彂鍗″崟浣�" />
+ </el-form-item>
+ <el-form-item label="鏈熷垵浣欓" prop="initialBalance" v-if="!form.id">
+ <el-input-number v-model="form.initialBalance" :precision="2" :min="0" :step="100" style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="鐘舵��" prop="status">
+ <el-radio-group v-model="form.status">
+ <el-radio label="ACTIVE">姝e父</el-radio>
+ <el-radio label="CANCELLED">娉ㄩ攢</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+ <el-button @click="cancel">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 鍏呭�肩鐞嗘娊灞� -->
+ <el-drawer v-model="rechargeDrawer.visible" :title="'鍏呭�肩鐞� - ' + rechargeDrawer.cardNumber" size="50%">
+ <div style="padding: 0 20px;">
+ <el-row style="margin-bottom: 15px;">
+ <el-button type="primary" icon="Plus" @click="handleAddRecharge">鏂板鍏呭��</el-button>
+ </el-row>
+ <el-table v-loading="rechargeDrawer.loading" :data="rechargeDrawer.list" border>
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="rechargeDate" label="鍏呭�兼椂闂�" width="160" />
+ <el-table-column prop="rechargeAmount" label="鍏呭�奸噾棰�(鍏�)" width="120" align="right">
+ <template #default="scope">
+ <span style="color: #67C23A; font-weight: bold;">+{{ scope.row.rechargeAmount }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="createUserName" label="鎿嶄綔浜�" width="100" />
+ <el-table-column prop="remark" label="澶囨敞" min-width="150" show-overflow-tooltip />
+ <el-table-column label="鎿嶄綔" width="100" align="center">
+ <template #default="scope">
+ <el-button type="danger" link size="small" @click="handleDeleteRecharge(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ v-show="rechargeDrawer.total > 0"
+ :total="rechargeDrawer.total"
+ v-model:page="rechargeDrawer.queryParams.current"
+ v-model:limit="rechargeDrawer.queryParams.size"
+ @pagination="getRechargeList"
+ />
+ </div>
+ </el-drawer>
+
+ <!-- 鏂板鍏呭�煎脊绐� -->
+ <el-dialog :title="rechargeDialog.title" v-model="rechargeDialog.visible" width="500px" append-to-body>
+ <el-form ref="rechargeFormRef" :model="rechargeDialog.form" :rules="rechargeDialog.rules" label-width="100px">
+ <el-form-item label="鍏呭�奸噾棰�" prop="rechargeAmount">
+ <el-input-number v-model="rechargeDialog.form.rechargeAmount" :min="0.01" :precision="2" :step="100" style="width: 100%;" placeholder="璇疯緭鍏ュ厖鍊奸噾棰�" />
+ </el-form-item>
+ <el-form-item label="鍏呭�兼棩鏈�" prop="rechargeDate">
+ <el-date-picker v-model="rechargeDialog.form.rechargeDate" type="datetime" placeholder="閫夋嫨鍏呭�兼椂闂�" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%;" />
+ </el-form-item>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="rechargeDialog.form.remark" type="textarea" placeholder="璇疯緭鍏ュ娉ㄥ唴瀹�" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitRechargeForm">纭� 瀹�</el-button>
+ <el-button @click="rechargeDialog.visible = false">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { listFuelCardPage, addFuelCard, updateFuelCard, delFuelCard } from '@/api/inventoryManagement/vehicle';
+import request from '@/utils/request';
+
+import { listAllFuelCard, listRecharge, addRecharge, delRecharge } from '@/api/inventoryManagement/vehicle';
+const statTotal = ref(0);
+const statActive = ref(0);
+const statCancelled = ref(0);
+const statBalance = ref(0);
+
+function getStats() {
+ listAllFuelCard().then(res => {
+ const arr = res.data || [];
+ statTotal.value = arr.length;
+ statActive.value = arr.filter(i => i.status === 'ACTIVE').length;
+ statCancelled.value = arr.filter(i => i.status === 'CANCELLED').length;
+ statBalance.value = arr.reduce((sum, item) => sum + (item.currentBalance || 0), 0);
+ });
+}
+
+
+const loading = ref(true);
+const tableData = ref([]);
+const total = ref(0);
+const open = ref(false);
+const title = ref("");
+const formRef = ref(null);
+
+const deptOptions = ref([]);
+
+const queryParams = reactive({
+ current: 1,
+ size: 10,
+ cardNumber: undefined,
+ deptId: undefined
+});
+
+const form = reactive({
+ id: undefined,
+ cardNumber: undefined,
+ deptId: undefined,
+ issuer: undefined,
+ initialBalance: 0,
+ status: 'ACTIVE',
+ remark: undefined
+});
+
+const rules = {
+ cardNumber: [{ required: true, message: "鍔犳补鍗″彿涓嶈兘涓虹┖", trigger: "blur" }],
+ deptId: [{ required: true, message: "鎵�灞為儴闂ㄤ笉鑳戒负绌�", trigger: "change" }],
+ issuer: [{ required: true, message: "鍙戝崱鍗曚綅涓嶈兘涓虹┖", trigger: "blur" }]
+};
+
+function getDicts() {
+ request({url: '/system/user/deptTree', method: 'get'}).then(res => {
+ deptOptions.value = res.data || [];
+ });
+}
+
+function getList() {
+ loading.value = true;
+ listFuelCardPage(queryParams).then(response => {
+ tableData.value = response.data.records;
+ total.value = response.data.total;
+ loading.value = false;
+ }).catch(() => {
+ loading.value = false;
+ });
+}
+
+function handleQuery() {
+ queryParams.current = 1;
+ getList();
+ getStats();
+}
+
+function resetQuery() {
+ queryParams.cardNumber = undefined;
+ queryParams.deptId = undefined;
+ handleQuery();
+}
+
+function reset() {
+ Object.assign(form, {
+ id: undefined,
+ cardNumber: undefined,
+ deptId: undefined,
+ issuer: undefined,
+ initialBalance: 0,
+ status: 'ACTIVE',
+ remark: undefined
+ });
+ if (formRef.value) formRef.value.resetFields();
+}
+
+function handleAdd() {
+ reset();
+ open.value = true;
+ title.value = "鏂板鍔犳补鍗�";
+}
+
+function handleUpdate(row) {
+ reset();
+ Object.assign(form, row);
+ open.value = true;
+ title.value = "淇敼鍔犳补鍗�";
+}
+
+function submitForm() {
+ formRef.value.validate(valid => {
+ if (valid) {
+ if (form.id != null) {
+ updateFuelCard(form).then(response => {
+ ElMessage.success("淇敼鎴愬姛");
+ open.value = false;
+ getList();
+ getStats();
+ });
+ } else {
+ addFuelCard(form).then(response => {
+ ElMessage.success("鏂板鎴愬姛");
+ open.value = false;
+ getList();
+ getStats();
+ });
+ }
+ }
+ });
+}
+
+function handleCancel(row) {
+ ElMessageBox.confirm('鏄惁纭娉ㄩ攢鍔犳补鍗″彿涓�"' + row.cardNumber + '"鐨勫崱鐗囷紵娉ㄩ攢鍚庤鍗′笉鑳界敤浜庢柊澧炲姞娌硅褰曪紝浣嗕綑棰濆皢淇濈暀銆�', "鎻愮ず", {
+ confirmButtonText: "纭畾娉ㄩ攢",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return updateFuelCard({ id: row.id, status: 'CANCELLED' });
+ }).then(() => {
+ getList();
+ getStats();
+ ElMessage.success("娉ㄩ攢鎴愬姛");
+ });
+}
+
+function handleDelete(row) {
+ ElMessageBox.confirm('鏄惁纭鍒犻櫎鍔犳补鍗″彿涓�"' + row.cardNumber + '"鐨勬暟鎹」锛�', "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return delFuelCard(row.id);
+ }).then(() => {
+ getList();
+ getStats();
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ });
+}
+
+function cancel() {
+ open.value = false;
+ reset();
+}
+
+onMounted(() => {
+ getDicts();
+ getList();
+ getStats();
+});
+
+// --- 鍏呭�肩鐞嗛�昏緫 ---
+const rechargeDrawer = reactive({
+ visible: false,
+ loading: false,
+ cardNumber: '',
+ cardId: null,
+ list: [],
+ total: 0,
+ queryParams: {
+ current: 1,
+ size: 10,
+ cardId: null
+ }
+});
+
+const rechargeDialog = reactive({
+ visible: false,
+ title: '鏂板鍏呭��',
+ form: {
+ cardId: null,
+ rechargeAmount: null,
+ rechargeDate: '',
+ remark: ''
+ },
+ rules: {
+ rechargeAmount: [{ required: true, message: '璇疯緭鍏ュ厖鍊奸噾棰�', trigger: 'blur' }],
+ rechargeDate: [{ required: true, message: '璇烽�夋嫨鍏呭�兼棩鏈�', trigger: 'change' }]
+ }
+});
+
+const rechargeFormRef = ref(null);
+
+function handleRecharge(row) {
+ rechargeDrawer.cardId = row.id;
+ rechargeDrawer.cardNumber = row.cardNumber;
+ rechargeDrawer.queryParams.cardId = row.id;
+ rechargeDrawer.visible = true;
+ getRechargeList();
+}
+
+function getRechargeList() {
+ rechargeDrawer.loading = true;
+ listRecharge(rechargeDrawer.queryParams).then(res => {
+ rechargeDrawer.list = res.data.records;
+ rechargeDrawer.total = res.data.total;
+ rechargeDrawer.loading = false;
+ }).catch(() => {
+ rechargeDrawer.loading = false;
+ });
+}
+
+function handleAddRecharge() {
+ rechargeDialog.form = {
+ cardId: rechargeDrawer.cardId,
+ rechargeAmount: undefined,
+ rechargeDate: new Date().toISOString().slice(0,19).replace('T', ' '),
+ remark: ''
+ };
+ rechargeDialog.visible = true;
+ if (rechargeFormRef.value) {
+ rechargeFormRef.value.clearValidate();
+ }
+}
+
+function submitRechargeForm() {
+ rechargeFormRef.value.validate(valid => {
+ if (valid) {
+ addRecharge(rechargeDialog.form).then(res => {
+ ElMessage.success('鍏呭�兼垚鍔�');
+ rechargeDialog.visible = false;
+ getRechargeList();
+ getList(); // 鏇存柊涓诲垪琛ㄩ噾棰�
+ getStats();
+ });
+ }
+ });
+}
+
+function handleDeleteRecharge(row) {
+ ElMessageBox.confirm('纭鍒犻櫎璇ョ瑪鍏呭�艰褰曞悧锛燂紙鍒犻櫎鍚庝綑棰濆皢鑷姩鎵e洖锛�', '鎻愮ず', { type: 'warning' }).then(() => {
+ return delRecharge(row.id);
+ }).then(() => {
+ ElMessage.success('鍒犻櫎鎴愬姛');
+ getRechargeList();
+ getList();
+ getStats();
+ });
+}
+// --- 鍏呭�肩鐞嗛�昏緫缁撴潫 ---
+
+</script>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/issueManagement/index.vue b/src/views/inventoryManagement/issueManagement/index.vue
index 2bba7d0..45bffc1 100644
--- a/src/views/inventoryManagement/issueManagement/index.vue
+++ b/src/views/inventoryManagement/issueManagement/index.vue
@@ -22,8 +22,8 @@
<el-button @click="handleOut">瀵煎嚭</el-button>
<!-- <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button> -->
</div>
- </div>
- <div class="table_list">
+ </div> <div class="table_list">
+
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
:expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
:summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
diff --git a/src/views/inventoryManagement/vehicleFuelManagement/index.vue b/src/views/inventoryManagement/vehicleFuelManagement/index.vue
index eaf543c..8ce40c3 100644
--- a/src/views/inventoryManagement/vehicleFuelManagement/index.vue
+++ b/src/views/inventoryManagement/vehicleFuelManagement/index.vue
@@ -1,281 +1,179 @@
<template>
<div class="app-container">
+
+
<!-- 鏌ヨ鏉′欢 -->
- <div class="search_form">
- <div>
- <span class="search_title">杞﹁締缂栧彿锛�</span>
- <el-input
- v-model="searchForm.vehicleCode"
- style="width: 200px"
- placeholder="璇疯緭鍏ヨ溅杈嗙紪鍙�"
- clearable
- @keyup.enter.native="handleQuery"
- />
-
- <span class="search_title ml10">鍔犳补鏃堕棿锛�</span>
- <el-date-picker
- v-model="searchForm.dateRange"
- type="daterange"
- range-separator="鑷�"
- start-placeholder="寮�濮嬫棩鏈�"
- end-placeholder="缁撴潫鏃ユ湡"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- clearable
- @change="handleQuery"
- />
-
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
- 鎼滅储
- </el-button>
- <el-button @click="resetSearch">閲嶇疆</el-button>
+ <div class="search_form">
+ <div style="flex: 1; padding-right: 20px;">
+ <el-row :gutter="16" style="margin-bottom: 16px;">
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鎵�灞為儴闂細</span>
+ <el-tree-select v-model="queryParams.deptId" :data="deptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="璇烽�夋嫨閮ㄩ棬" check-strictly clearable filterable style="flex: 1;" />
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鍔犳补杞﹁締锛�</span>
+ <el-select v-model="queryParams.vehicleId" placeholder="鎸夎溅杈嗙瓫閫�" clearable filterable style="flex: 1;">
+ <el-option v-for="item in vehicleList" :key="item.id" :label="item.plateNumber" :value="item.id" />
+ </el-select>
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鍔犳补鍗★細</span>
+ <el-select v-model="queryParams.fuelCardId" placeholder="璇烽�夋嫨鍔犳补鍗�" clearable style="flex: 1;">
+ <el-option v-for="item in fuelCardList" :key="item.id" :label="item.cardNumber + ' - ' + item.issuer" :value="item.id" />
+ </el-select>
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鐘舵�侊細</span>
+ <el-select v-model="queryParams.status" placeholder="鐘舵�佺瓫閫�" clearable style="flex: 1;">
+ <el-option label="鑽夌" value="0" />
+ <el-option label="宸叉彁浜�" value="1" />
+ </el-select>
+ </el-col>
+ </el-row>
+ <el-row :gutter="16">
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鍔犳补鏃堕棿锛�</span>
+ <el-date-picker v-model="dateRange" type="daterange" range-separator="鑷�" start-placeholder="寮�濮�" end-placeholder="缁撴潫" value-format="YYYY-MM-DD" clearable style="flex: 1; width: 100%;" />
+ </el-col>
+ <el-col :span="18" style="display: flex; align-items: center;">
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-col>
+ </el-row>
</div>
- <div>
- <el-button type="primary" icon="Plus" @click="openAdd">
- 鏂板鍔犳补璁板綍
- </el-button>
+ <div style="display: flex; align-items: flex-start; height: 100%;">
+ <el-button type="primary" icon="Plus" @click="handleAdd">鏂板鍔犳补璁板綍</el-button>
</div>
</div>
-
<!-- 琛ㄦ牸 -->
<div class="table_list">
<el-table
+ v-loading="loading"
:data="tableData"
border
style="width: 100%"
- height="calc(100vh - 18.5em)"
+ height="calc(100vh - 22em)"
:header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
- :row-class-name="tableRowClassName"
>
<el-table-column type="index" label="搴忓彿" width="60" align="center" />
- <el-table-column
- prop="vehicleCode"
- label="杞﹁締缂栧彿"
- width="130"
- show-overflow-tooltip
- />
- <el-table-column
- prop="plateNumber"
- label="杞︾墝鍙风爜"
- width="120"
- show-overflow-tooltip
- />
- <el-table-column
- prop="fuelDate"
- label="鍔犳补鏃ユ湡"
- width="120"
- show-overflow-tooltip
- />
- <el-table-column
- prop="gunNo"
- label="娌规灙鍙�"
- width="90"
- align="center"
- />
- <el-table-column
- prop="amount"
- label="閲戦(鍏�)"
- width="100"
- align="right"
- >
+ <el-table-column prop="fuelDate" label="鍔犳补鏃ユ湡" width="120" />
+ <el-table-column prop="deptName" label="鎵�灞為儴闂�" min-width="150" show-overflow-tooltip />
+ <el-table-column prop="plateNumber" label="鍔犳补杞﹁締(鐗岀収)" width="140" />
+ <el-table-column prop="cardNumber" label="鍔犳补鍗″彿" min-width="150" />
+ <el-table-column prop="amount" label="閲戦(鍏�)" width="100" align="right" />
+ <el-table-column prop="liters" label="鍗囨暟(L)" width="100" align="right" />
+ <el-table-column prop="startMileage" label="璧峰閲岀▼(km)" width="120" align="right" />
+ <el-table-column prop="endMileage" label="缁撴潫閲岀▼(km)" width="120" align="right" />
+ <el-table-column prop="invoiceNo" label="鍙戠エ鍙风爜" width="140" />
+ <el-table-column prop="status" label="鐘舵��" width="100" align="center">
<template #default="scope">
- <span>{{ scope.row.amount?.toFixed(2) }}</span>
+ <el-tag v-if="scope.row.status === '0'" type="info">鑽夌</el-tag>
+ <el-tag v-else-if="scope.row.status === '1'" type="success">宸叉彁浜�</el-tag>
+ <el-tag v-else type="info">鑽夌</el-tag>
</template>
</el-table-column>
- <el-table-column
- prop="liters"
- label="鍗囨暟(L)"
- width="90"
- align="right"
- >
+ <el-table-column prop="remark" label="澶囨敞" min-width="150" show-overflow-tooltip />
+ <el-table-column label="鎿嶄綔" fixed="right" width="200" align="center">
<template #default="scope">
- <span>{{ scope.row.liters?.toFixed(2) }}</span>
- </template>
- </el-table-column>
- <el-table-column
- prop="startMileage"
- label="璧峰閲岀▼(km)"
- width="120"
- align="right"
- />
- <el-table-column
- prop="endMileage"
- label="缁撴潫閲岀▼(km)"
- width="120"
- align="right"
- />
- <el-table-column
- prop="distance"
- label="琛岄┒閲岀▼(km)"
- width="120"
- align="right"
- />
- <el-table-column
- prop="fuelConsumption"
- label="娌硅��(L/100km)"
- width="130"
- align="center"
- >
- <template #default="scope">
- <span
- :style="scope.row.isAbnormal ? 'color:#F56C6C;font-weight:600;' : ''"
- >
- {{ scope.row.fuelConsumption != null ? scope.row.fuelConsumption.toFixed(2) : '-' }}
- </span>
- </template>
- </el-table-column>
- <el-table-column
- prop="avgConsumption"
- label="杞﹁締骞冲潎娌硅��"
- width="130"
- align="center"
- >
- <template #default="scope">
- <span>
- {{ scope.row.avgConsumption != null ? scope.row.avgConsumption.toFixed(2) : '-' }}
- </span>
- </template>
- </el-table-column>
- <el-table-column label="寮傚父棰勮" width="100" align="center">
- <template #default="scope">
- <el-tag v-if="scope.row.isAbnormal" type="danger" size="small">
- 寮傚父
- </el-tag>
- <span v-else>-</span>
- </template>
- </el-table-column>
- <el-table-column label="鎿嶄綔" fixed="right" width="140" align="center">
- <template #default="scope">
- <el-button
- type="primary"
- link
- size="small"
- @click="openEdit(scope.row)"
- >
- 缂栬緫
- </el-button>
- <el-button
- type="danger"
- link
- size="small"
- @click="removeRow(scope.row)"
- >
- 鍒犻櫎
- </el-button>
+ <el-button v-if="scope.row.status === '0' || !scope.row.status" type="success" link size="small" @click="handleSubmitRow(scope.row)">鎻愪氦</el-button>
+ <el-button v-if="scope.row.status === '0' || !scope.row.status" type="primary" link size="small" @click="handleUpdate(scope.row)">缂栬緫</el-button>
+ <el-button v-if="scope.row.status === '0' || !scope.row.status" type="danger" link size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ <el-button v-if="scope.row.status === '1'" type="warning" link size="small" @click="handleRevokeRow(scope.row)">鎾ゅ洖</el-button>
</template>
</el-table-column>
</el-table>
- </div>
- <!-- 鏂板/缂栬緫寮圭獥 -->
- <el-dialog
- v-model="dialogVisible"
- :title="dialogTitle"
- width="640px"
- destroy-on-close
- >
- <el-form
- ref="formRef"
- :model="form"
- :rules="rules"
- label-width="110px"
- label-position="right"
- >
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </div>
+<!-- 寮圭獥 -->
+ <el-dialog :title="title" v-model="open" width="640px" append-to-body>
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="杞﹁締缂栧彿锛�" prop="vehicleCode">
- <el-input
- v-model="form.vehicleCode"
- placeholder="璇疯緭鍏ヨ溅杈嗙紪鍙�"
- />
+ <el-form-item label="鍔犳补杞﹁締" prop="vehicleId">
+ <el-select v-model="form.vehicleId" placeholder="璇烽�夋嫨杞﹁締(鐗岀収)" style="width: 100%" filterable>
+ <el-option
+ v-for="item in vehicleList"
+ :key="item.id"
+ :label="item.plateNumber"
+ :value="item.id"
+ />
+ </el-select>
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="杞︾墝鍙风爜锛�" prop="plateNumber">
- <el-input
- v-model="form.plateNumber"
- placeholder="璇疯緭鍏ヨ溅鐗屽彿鐮�"
- />
+ <el-form-item label="鍏宠仈鍔犳补鍗�" prop="fuelCardId">
+ <el-select v-model="form.fuelCardId" placeholder="璇烽�夋嫨鍔犳补鍗�" style="width: 100%" filterable>
+ <el-option
+ v-for="item in availableFuelCardList"
+ :key="item.id"
+ :label="item.cardNumber + ' - ' + item.issuer"
+ :value="item.id"
+ />
+ </el-select>
+ <div v-if="form.fuelCardId" style="font-size: 12px; color: #999; margin-top: 5px;">
+ 褰撳墠浣欓: {{ getFuelCardBalance(form.fuelCardId) }} 鍏�
+ </div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="鍔犳补鏃ユ湡锛�" prop="fuelDate">
+ <el-form-item label="鍔犳补鏃ユ湡" prop="fuelDate">
<el-date-picker
v-model="form.fuelDate"
type="date"
value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- placeholder="璇烽�夋嫨鍔犳补鏃ユ湡"
+ placeholder="璇烽�夋嫨鏃ユ湡"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="娌规灙鍙凤細" prop="gunNo">
- <el-input
- v-model="form.gunNo"
- placeholder="璇疯緭鍏ユ补鏋彿"
- />
+ <el-form-item label="鍙戠エ鍙风爜" prop="invoiceNo">
+ <el-input v-model="form.invoiceNo" placeholder="璇疯緭鍏ュ彂绁ㄥ彿鐮�" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="閲戦(鍏�)锛�" prop="amount">
- <el-input-number
- v-model="form.amount"
- :min="0"
- :step="0.01"
- :precision="2"
- placeholder="璇疯緭鍏ラ噾棰�"
- style="width: 100%"
- />
+ <el-form-item label="閲戦(鍏�)" prop="amount">
+ <el-input-number v-model="form.amount" :min="0" :precision="2" :step="100" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="鍗囨暟(L)锛�" prop="liters">
- <el-input-number
- v-model="form.liters"
- :min="0"
- :step="0.01"
- :precision="2"
- placeholder="璇疯緭鍏ュ崌鏁�"
- style="width: 100%"
- />
+ <el-form-item label="鍗囨暟(L)" prop="liters">
+ <el-input-number v-model="form.liters" :min="0" :precision="2" :step="10" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="璧峰閲岀▼(km)锛�" prop="startMileage">
- <el-input-number
- v-model="form.startMileage"
- :min="0"
- :step="1"
- placeholder="璇疯緭鍏ヨ捣濮嬮噷绋�"
- style="width: 100%"
- />
+ <el-form-item label="璧峰閲岀▼(km)" prop="startMileage">
+ <el-input-number v-model="form.startMileage" :min="0" :precision="2" :step="100" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="缁撴潫閲岀▼(km)锛�" prop="endMileage">
- <el-input-number
- v-model="form.endMileage"
- :min="0"
- :step="1"
- placeholder="璇疯緭鍏ョ粨鏉熼噷绋�"
- style="width: 100%"
- />
+ <el-form-item label="缁撴潫閲岀▼(km)" prop="endMileage">
+ <el-input-number v-model="form.endMileage" :min="0" :precision="2" :step="100" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ娉ㄥ唴瀹�" />
+ </el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
- <el-button @click="handleCancel">鍙� 娑�</el-button>
- <el-button type="primary" @click="handleSubmit">淇� 瀛�</el-button>
+ <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+ <el-button @click="cancel">鍙� 娑�</el-button>
</div>
</template>
</el-dialog>
@@ -283,274 +181,229 @@
</template>
<script setup>
-import { ref, reactive, onMounted } from "vue";
-import { ElMessage, ElMessageBox } from "element-plus";
+import { ref, reactive, onMounted, computed } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import request from '@/utils/request';
+import {
+ listFuelRecord,
+ addFuelRecord,
+ updateFuelRecord,
+ delFuelRecord,
+ listVehicleInfo,
+ listAllFuelCard,
+ submitFuelRecord,
+ revokeFuelRecord
+} from '@/api/inventoryManagement/vehicle';
-// 妯℃嫙鍔犳补璁板綍鏁版嵁
-const rawRecords = ref([
- {
- id: 1,
- vehicleCode: "CL-202401",
- plateNumber: "绮12345",
- fuelDate: "2024-12-01",
- gunNo: "01",
- amount: 500,
- liters: 70,
- startMileage: 12000,
- endMileage: 12600,
- },
- {
- id: 2,
- vehicleCode: "CL-202401",
- plateNumber: "绮12345",
- fuelDate: "2024-12-15",
- gunNo: "02",
- amount: 520,
- liters: 72,
- startMileage: 12600,
- endMileage: 13250,
- },
- {
- id: 3,
- vehicleCode: "CL-202402",
- plateNumber: "绮67890",
- fuelDate: "2024-12-05",
- gunNo: "03",
- amount: 430,
- liters: 60,
- startMileage: 8000,
- endMileage: 8520,
- },
- {
- id: 4,
- vehicleCode: "CL-202402",
- plateNumber: "绮67890",
- fuelDate: "2024-12-20",
- gunNo: "01",
- amount: 450,
- liters: 63,
- startMileage: 8520,
- endMileage: 9000,
- },
- {
- id: 5,
- vehicleCode: "CL-202401",
- plateNumber: "绮12345",
- fuelDate: "2025-01-05",
- gunNo: "01",
- amount: 700,
- liters: 90,
- startMileage: 13250,
- endMileage: 13600, // 鏄庢樉寮傚父娌硅��
- },
-]);
+const loading = ref(true);
+const tableData = ref([]);
+const total = ref(0);
+const open = ref(false);
+const title = ref("");
+const formRef = ref(null);
-// 鏌ヨ琛ㄥ崟
-const searchForm = reactive({
- vehicleCode: "",
- dateRange: [],
+const vehicleList = ref([]);
+const fuelCardList = ref([]);
+const deptOptions = ref([]);
+const dateRange = ref([]);
+
+const availableFuelCardList = computed(() => {
+ return fuelCardList.value.filter(item => item.status !== 'CANCELLED');
});
-// 琛ㄦ牸鏁版嵁锛堝寘鍚绠楀瓧娈碉級
-const tableData = ref([]);
+const queryParams = reactive({
+ current: 1,
+ size: 10,
+ deptId: undefined,
+ vehicleId: undefined,
+ fuelCardId: undefined,
+ beginDate: undefined,
+ endDate: undefined
+});
-// 寮圭獥 & 琛ㄥ崟
-const dialogVisible = ref(false);
-const dialogTitle = ref("鏂板鍔犳补璁板綍");
-const isEdit = ref(false);
-const formRef = ref(null);
const form = reactive({
- id: null,
- vehicleCode: "",
- plateNumber: "",
- fuelDate: "",
- gunNo: "",
- amount: null,
- liters: null,
- startMileage: null,
- endMileage: null,
+ id: undefined,
+ vehicleId: undefined,
+ fuelCardId: undefined,
+ fuelDate: undefined,
+ amount: undefined,
+ liters: undefined,
+ startMileage: undefined,
+ endMileage: undefined,
+ invoiceNo: undefined,
+ remark: undefined
});
const rules = {
- vehicleCode: [{ required: true, message: "璇疯緭鍏ヨ溅杈嗙紪鍙�", trigger: "blur" }],
- plateNumber: [{ required: true, message: "璇疯緭鍏ヨ溅鐗屽彿鐮�", trigger: "blur" }],
- fuelDate: [{ required: true, message: "璇烽�夋嫨鍔犳补鏃ユ湡", trigger: "change" }],
- gunNo: [{ required: true, message: "璇疯緭鍏ユ补鏋彿", trigger: "blur" }],
- amount: [{ required: true, message: "璇疯緭鍏ラ噾棰�", trigger: "blur" }],
- liters: [{ required: true, message: "璇疯緭鍏ュ崌鏁�", trigger: "blur" }],
- startMileage: [{ required: true, message: "璇疯緭鍏ヨ捣濮嬮噷绋�", trigger: "blur" }],
- endMileage: [{ required: true, message: "璇疯緭鍏ョ粨鏉熼噷绋�", trigger: "blur" }],
+ vehicleId: [{ required: true, message: "鍔犳补杞﹁締涓嶈兘涓虹┖", trigger: "change" }],
+ fuelCardId: [{ required: true, message: "鍔犳补鍗′笉鑳戒负绌�", trigger: "change" }],
+ fuelDate: [{ required: true, message: "鍔犳补鏃ユ湡涓嶈兘涓虹┖", trigger: "change" }],
+ amount: [{ required: true, message: "閲戦涓嶈兘涓虹┖", trigger: "blur" }]
};
-// 閲嶆柊璁$畻娌硅�椼�佸钩鍧囨补鑰楀拰寮傚父棰勮
-const recomputeTable = () => {
- const records = rawRecords.value;
-
- // 1. 鍏堟寜杞﹁締缁熻骞冲潎娌硅��
- const stats = {};
- records.forEach((r) => {
- const distance = r.endMileage - r.startMileage;
- if (distance <= 0 || !r.liters) return;
- const cons = (r.liters / distance) * 100; // L/100km
- if (!stats[r.vehicleCode]) {
- stats[r.vehicleCode] = { totalCons: 0, count: 0 };
- }
- stats[r.vehicleCode].totalCons += cons;
- stats[r.vehicleCode].count += 1;
+function getDictData() {
+ request({url: '/system/user/deptTree', method: 'get'}).then(res => {
+ deptOptions.value = res.data || [];
});
-
- const avgMap = {};
- Object.keys(stats).forEach((key) => {
- avgMap[key] = stats[key].totalCons / stats[key].count;
+ listVehicleInfo({current: 1, size: 1000}).then(res => {
+ vehicleList.value = res.data.records;
});
+ listAllFuelCard().then(res => {
+ fuelCardList.value = res.data;
+ });
+}
- // 2. 鎸夌瓫閫夋潯浠惰繃婊ゅ苟琛ュ厖璁$畻瀛楁
- const filtered = records
- .filter((r) => {
- if (
- searchForm.vehicleCode &&
- !r.vehicleCode.includes(searchForm.vehicleCode.trim())
- ) {
- return false;
- }
- if (Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
- const [start, end] = searchForm.dateRange;
- if (r.fuelDate < start || r.fuelDate > end) {
- return false;
+function getFuelCardBalance(id) {
+ const c = fuelCardList.value.find(item => item.id === id);
+ return c ? c.currentBalance : 0;
+}
+
+
+const statTotalAmount = ref(0);
+const statTotalLiters = ref(0);
+
+function getStats() {
+ const query = { ...queryParams, current: 1, size: 10000 };
+ listFuelRecord(query).then(res => {
+ const arr = res.data.records || [];
+ statTotalAmount.value = arr.reduce((sum, item) => sum + (item.amount || 0), 0);
+ statTotalLiters.value = arr.reduce((sum, item) => sum + (item.liters || 0), 0);
+ });
+}
+
+function getList() {
+ loading.value = true;
+ if (dateRange.value && dateRange.value.length === 2) {
+ queryParams.beginDate = dateRange.value[0];
+ queryParams.endDate = dateRange.value[1];
+ } else {
+ queryParams.beginDate = undefined;
+ queryParams.endDate = undefined;
+ }
+ listFuelRecord(queryParams).then(response => {
+ tableData.value = response.data.records;
+ total.value = response.data.total;
+ loading.value = false;
+ getStats();
+ }).catch(() => {
+ loading.value = false;
+ getStats();
+ });
+}
+
+function handleQuery() {
+ queryParams.current = 1;
+ getList();
+}
+
+function resetQuery() {
+ queryParams.deptId = undefined;
+ queryParams.vehicleId = undefined;
+ queryParams.fuelCardId = undefined;
+ dateRange.value = [];
+ handleQuery();
+}
+
+function reset() {
+ Object.assign(form, {
+ id: undefined,
+ vehicleId: undefined,
+ fuelCardId: undefined,
+ fuelDate: undefined,
+ amount: undefined,
+ liters: undefined,
+ startMileage: undefined,
+ endMileage: undefined,
+ invoiceNo: undefined,
+ remark: undefined
+ });
+ if (formRef.value) formRef.value.resetFields();
+}
+
+function handleAdd() {
+ reset();
+ open.value = true;
+ title.value = "鏂板鍔犳补璁板綍";
+}
+
+function handleUpdate(row) {
+ reset();
+ Object.assign(form, row);
+ open.value = true;
+ title.value = "淇敼鍔犳补璁板綍";
+}
+
+function submitForm() {
+ formRef.value.validate(valid => {
+ if (valid) {
+ if (!form.id) {
+ const bal = getFuelCardBalance(form.fuelCardId);
+ if (bal < form.amount) {
+ ElMessage.warning("璇ュ姞娌瑰崱浣欓涓嶈冻锛佸綋鍓嶄綑棰�: " + bal);
}
}
- return true;
- })
- .map((r) => {
- const distance = r.endMileage - r.startMileage;
- const fuelConsumption =
- distance > 0 && r.liters
- ? (r.liters / distance) * 100
- : null;
- const avgConsumption =
- avgMap[r.vehicleCode] != null ? avgMap[r.vehicleCode] : null;
- const isAbnormal =
- avgConsumption != null &&
- fuelConsumption != null &&
- fuelConsumption > avgConsumption * 1.2;
-
- return {
- ...r,
- distance,
- fuelConsumption,
- avgConsumption,
- isAbnormal,
- };
- });
-
- tableData.value = filtered;
-};
-
-// 鏌ヨ
-const handleQuery = () => {
- recomputeTable();
-};
-
-const resetSearch = () => {
- searchForm.vehicleCode = "";
- searchForm.dateRange = [];
- recomputeTable();
-};
-
-// 琛屾牱寮忥紙寮傚父楂樹寒锛�
-const tableRowClassName = ({ row }) => {
- if (row.isAbnormal) {
- return "row-abnormal";
- }
- return "";
-};
-
-// 鏂板
-const openAdd = () => {
- dialogTitle.value = "鏂板鍔犳补璁板綍";
- isEdit.value = false;
- Object.assign(form, {
- id: null,
- vehicleCode: "",
- plateNumber: "",
- fuelDate: "",
- gunNo: "",
- amount: null,
- liters: null,
- startMileage: null,
- endMileage: null,
- });
- dialogVisible.value = true;
-};
-
-// 缂栬緫
-const openEdit = (row) => {
- dialogTitle.value = "缂栬緫鍔犳补璁板綍";
- isEdit.value = true;
- Object.assign(form, row);
- dialogVisible.value = true;
-};
-
-// 淇濆瓨
-const handleSubmit = () => {
- if (!formRef.value) return;
- formRef.value.validate((valid) => {
- if (!valid) return;
-
- if (form.endMileage <= form.startMileage) {
- ElMessage.warning("缁撴潫閲岀▼蹇呴』澶т簬璧峰閲岀▼");
- return;
- }
-
- if (isEdit.value) {
- const index = rawRecords.value.findIndex((r) => r.id === form.id);
- if (index !== -1) {
- rawRecords.value[index] = { ...form };
+
+ if (form.id != null) {
+ updateFuelRecord(form).then(response => {
+ ElMessage.success("淇敼鎴愬姛");
+ open.value = false;
+ getList();
+ getDictData();
+ });
+ } else {
+ addFuelRecord(form).then(response => {
+ ElMessage.success("鏂板鎴愬姛");
+ open.value = false;
+ getList();
+ getDictData();
+ });
}
- ElMessage.success("鍔犳补璁板綍宸叉洿鏂�");
- } else {
- const newId = rawRecords.value.length
- ? Math.max(...rawRecords.value.map((r) => r.id)) + 1
- : 1;
- rawRecords.value.push({ ...form, id: newId });
- ElMessage.success("鍔犳补璁板綍宸叉柊澧�");
}
- dialogVisible.value = false;
- recomputeTable();
});
-};
+}
-const handleCancel = () => {
- dialogVisible.value = false;
-};
-
-// 鍒犻櫎
-const removeRow = (row) => {
- ElMessageBox.confirm("鏄惁纭鍒犻櫎璇ュ姞娌硅褰曪紵", "鍒犻櫎鎻愮ず", {
- confirmButtonText: "纭",
+function handleDelete(row) {
+ ElMessageBox.confirm('鏄惁纭鍒犻櫎璇ヨ崏绋胯褰曪紵', "璀﹀憡", {
+ confirmButtonText: "纭畾",
cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- rawRecords.value = rawRecords.value.filter((r) => r.id !== row.id);
- recomputeTable();
- ElMessage.success("鍒犻櫎鎴愬姛");
- })
- .catch(() => {});
-};
+ type: "warning"
+ }).then(function() {
+ return delFuelRecord(row.id);
+ }).then(() => {
+ getList();
+ getDictData();
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ });
+}
+
+function cancel() {
+ open.value = false;
+ reset();
+}
onMounted(() => {
- recomputeTable();
+ getDictData();
+ getList();
});
-</script>
-<style scoped lang="scss">
-.dialog-footer {
- text-align: right;
+function handleSubmitRow(row) {
+ ElMessageBox.confirm('纭鎻愪氦璇ョ瑪鍔犳补璁板綍鍚楋紵鎻愪氦鍚庡皢浠庡姞娌瑰崱涓墸闄や綑棰�', '鎻愮ず', { type: 'warning' }).then(() => {
+ return submitFuelRecord(row.id);
+ }).then(() => {
+ ElMessage.success('鎻愪氦鎴愬姛');
+ getList();
+ getStats();
+ }).catch(() => {});
}
-:deep(.row-abnormal) {
- background-color: #fff5f5;
+function handleRevokeRow(row) {
+ ElMessageBox.confirm('纭鎾ゅ洖璇ョ瑪鍔犳补璁板綍鍚楋紵鎾ゅ洖鍚庡皢閫�杩樺姞娌瑰崱浣欓', '鎻愮ず', { type: 'warning' }).then(() => {
+ return revokeFuelRecord(row.id);
+ }).then(() => {
+ ElMessage.success('鎾ゅ洖鎴愬姛');
+ getList();
+ getStats();
+ }).catch(() => {});
}
-</style>
+</script>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/vehicleManagement/index.vue b/src/views/inventoryManagement/vehicleManagement/index.vue
index 1e383c6..1d0203a 100644
--- a/src/views/inventoryManagement/vehicleManagement/index.vue
+++ b/src/views/inventoryManagement/vehicleManagement/index.vue
@@ -1,268 +1,130 @@
<template>
<div class="app-container">
+
<!-- 鏌ヨ鏉′欢 -->
- <div class="search_form">
- <div>
- <span class="search_title">杞︾墝鍙风爜锛�</span>
- <el-input
- v-model="searchForm.plateNumber"
- style="width: 180px"
- placeholder="璇疯緭鍏ヨ溅鐗屽彿鐮�"
- clearable
- @keyup.enter.native="handleQuery"
- />
-
- <span class="search_title ml10">杞﹁締绫诲瀷锛�</span>
- <el-select
- v-model="searchForm.vehicleType"
- style="width: 160px"
- placeholder="璇烽�夋嫨杞﹁締绫诲瀷"
- clearable
- >
- <el-option
- v-for="item in vehicleTypeOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
-
- <span class="search_title ml10">鎵�灞為儴闂細</span>
- <el-select
- v-model="searchForm.department"
- style="width: 160px"
- placeholder="璇烽�夋嫨鎵�灞為儴闂�"
- clearable
- >
- <el-option
- v-for="item in departmentOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
-
- <span class="search_title ml10">鐘舵�侊細</span>
- <el-select
- v-model="searchForm.status"
- style="width: 140px"
- placeholder="璇烽�夋嫨鐘舵��"
- clearable
- >
- <el-option
- v-for="item in statusOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
-
- <span class="search_title ml10">褰掓。鐘舵�侊細</span>
- <el-select
- v-model="searchForm.archived"
- style="width: 140px"
- placeholder="璇烽�夋嫨褰掓。鐘舵��"
- clearable
- >
- <el-option label="鏈綊妗�" value="false" />
- <el-option label="宸插綊妗�" value="true" />
- </el-select>
-
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
- 鎼滅储
- </el-button>
- <el-button @click="resetSearch">閲嶇疆</el-button>
+ <div class="search_form" >
+ <div style="flex: 1; padding-right: 20px;">
+ <el-row :gutter="16">
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">杞︾墝鍙风爜锛�</span>
+ <el-input v-model="queryParams.plateNumber" placeholder="璇疯緭鍏ヨ溅鐗屽彿鐮�" clearable style="flex: 1;" @keyup.enter.native="handleQuery" />
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鎵�灞為儴闂細</span>
+ <el-tree-select v-model="queryParams.deptId" :data="deptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="璇烽�夋嫨閮ㄩ棬" check-strictly clearable filterable style="flex: 1;" />
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <span class="search_title" style="white-space: nowrap;">鐘舵�侊細</span>
+ <el-select v-model="queryParams.vehicleStatus" placeholder="璇烽�夋嫨鐘舵��" clearable style="flex: 1;">
+ <el-option label="鍦ㄧ敤" value="NORMAL" />
+ <el-option label="闂茬疆" value="IDLE" />
+ <el-option label="缁翠慨" value="REPAIR" />
+ </el-select>
+ </el-col>
+ <el-col :span="6" style="display: flex; align-items: center;">
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-col>
+ </el-row>
</div>
<div>
- <el-button type="primary" icon="Plus" @click="openAdd">鏂板杞﹁締</el-button>
+ <el-button type="primary" icon="Plus" @click="handleAdd">鏂板杞﹁締</el-button>
</div>
</div>
-
<!-- 琛ㄦ牸 -->
<div class="table_list">
<el-table
+ v-loading="loading"
:data="tableData"
border
style="width: 100%"
- height="calc(100vh - 18.5em)"
+ height="calc(100vh - 22em)"
:header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
>
<el-table-column type="index" label="搴忓彿" width="60" align="center" />
- <el-table-column
- prop="vehicleCode"
- label="杞﹁締缂栧彿"
- width="140"
- show-overflow-tooltip
- />
- <el-table-column
- prop="plateNumber"
- label="杞︾墝鍙风爜"
- width="120"
- show-overflow-tooltip
- />
- <el-table-column
- prop="vehicleType"
- label="杞﹁締绫诲瀷"
- width="120"
- show-overflow-tooltip
- />
- <el-table-column
- prop="department"
- label="鎵�灞為儴闂�"
- width="140"
- show-overflow-tooltip
- />
- <el-table-column
- prop="purchaseDate"
- label="璐疆鏃ユ湡"
- width="120"
- show-overflow-tooltip
- />
- <el-table-column
- prop="licenseNumber"
- label="琛岄┒璇佺紪鍙�"
- width="160"
- show-overflow-tooltip
- />
- <el-table-column
- prop="licenseIssueDate"
- label="鍙戣瘉鏃ユ湡"
- width="120"
- show-overflow-tooltip
- />
- <el-table-column
- prop="licenseExpireDate"
- label="鍒版湡鏃ユ湡"
- width="120"
- show-overflow-tooltip
- />
- <el-table-column label="鐘舵��" width="100" align="center">
+ <el-table-column prop="plateNumber" label="杞︾墝鍙风爜" min-width="120" show-overflow-tooltip />
+ <el-table-column prop="deptName" label="鎵�灞為儴闂�" min-width="150" show-overflow-tooltip />
+ <el-table-column prop="userName" label="璐d换浜�" min-width="120" />
+ <el-table-column prop="propertyOwnership" label="浜ф潈鎵�鏈�" min-width="120" show-overflow-tooltip />
+ <el-table-column label="骞存鍒版湡鏃ユ湡" min-width="140">
<template #default="scope">
- <el-tag :type="statusTagType(scope.row.status)">
- {{ scope.row.status }}
+ <span>{{ scope.row.inspectionExpireDate }}</span>
+ <el-tag v-if="isExpired(scope.row.inspectionExpireDate)" type="danger" size="small" style="margin-left:5px;">宸茶秴鏈�</el-tag>
+ <el-tag v-else-if="isExpiringSoon(scope.row.inspectionExpireDate)" type="warning" size="small" style="margin-left:5px;">鍗冲皢鍒版湡</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="淇濋櫓鍒版湡鏃ユ湡" min-width="140">
+ <template #default="scope">
+ <span>{{ scope.row.insuranceExpireDate }}</span>
+ <el-tag v-if="isExpired(scope.row.insuranceExpireDate)" type="danger" size="small" style="margin-left:5px;">宸茶秴鏈�</el-tag>
+ <el-tag v-else-if="isExpiringSoon(scope.row.insuranceExpireDate)" type="warning" size="small" style="margin-left:5px;">鍗冲皢鍒版湡</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鐘舵��" min-width="100" align="center">
+ <template #default="scope">
+ <el-tag :type="statusTagType(scope.row.vehicleStatus)">
+ {{ statusLabel(scope.row.vehicleStatus) }}
</el-tag>
</template>
</el-table-column>
- <el-table-column label="褰掓。鐘舵��" width="100" align="center">
- <template #default="scope">
- <el-tag :type="scope.row.archived ? 'info' : 'success'">
- {{ scope.row.archived ? '宸插綊妗�' : '鏈綊妗�' }}
- </el-tag>
- </template>
- </el-table-column>
+ <el-table-column prop="remark" label="澶囨敞" min-width="150" show-overflow-tooltip />
<el-table-column label="鎿嶄綔" fixed="right" width="220" align="center">
<template #default="scope">
- <el-button
- type="primary"
- link
- size="small"
- @click="openEdit(scope.row)"
- >
- 缂栬緫
- </el-button>
- <el-button
- type="warning"
- link
- size="small"
- :disabled="scope.row.archived"
- @click="archiveRow(scope.row)"
- >
- 褰掓。
- </el-button>
- <el-button
- type="danger"
- link
- size="small"
- @click="removeRow(scope.row)"
- >
- 鍒犻櫎
- </el-button>
+ <el-button type="success" link size="small" @click="handleCost(scope.row)">璐圭敤绠$悊</el-button>
+ <el-button type="primary" link size="small" @click="handleUpdate(scope.row)">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
</template>
</el-table-column>
</el-table>
- </div>
- <!-- 鏂板/缂栬緫寮圭獥 -->
- <el-dialog
- v-model="dialogVisible"
- :title="dialogTitle"
- width="600px"
- destroy-on-close
- >
- <el-form
- ref="formRef"
- :model="form"
- :rules="rules"
- label-width="100px"
- label-position="right"
- >
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </div>
+<!-- 鏂板/缂栬緫杞﹁締寮圭獥 -->
+ <el-dialog :title="title" v-model="open" width="600px" append-to-body>
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="杞﹁締缂栧彿锛�" prop="vehicleCode">
- <el-input v-model="form.vehicleCode" placeholder="璇疯緭鍏ヨ溅杈嗙紪鍙�" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="杞︾墝鍙风爜锛�" prop="plateNumber">
+ <el-form-item label="杞︾墝鍙风爜" prop="plateNumber">
<el-input v-model="form.plateNumber" placeholder="璇疯緭鍏ヨ溅鐗屽彿鐮�" />
</el-form-item>
</el-col>
- </el-row>
- <el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="杞﹁締绫诲瀷锛�" prop="vehicleType">
- <el-select
- v-model="form.vehicleType"
- placeholder="璇烽�夋嫨杞﹁締绫诲瀷"
- clearable
- >
- <el-option
- v-for="item in vehicleTypeOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鎵�灞為儴闂細" prop="department">
- <el-select
- v-model="form.department"
- placeholder="璇烽�夋嫨鎵�灞為儴闂�"
- clearable
- >
- <el-option
- v-for="item in departmentOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
+ <el-form-item label="浜ф潈鎵�鏈�" prop="propertyOwnership">
+ <el-input v-model="form.propertyOwnership" placeholder="濡�:鑷湁,绉熻祦绛�" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="璐疆鏃ユ湡锛�" prop="purchaseDate">
- <el-date-picker
- v-model="form.purchaseDate"
- type="date"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- placeholder="璇烽�夋嫨璐疆鏃ユ湡"
+ <el-form-item label="鎵�灞為儴闂�" prop="deptId">
+ <el-tree-select
+ v-model="form.deptId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ value-key="id"
+ placeholder="璇烽�夋嫨閮ㄩ棬"
+ check-strictly
+ clearable
+ filterable
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="鐘舵�侊細" prop="status">
- <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
+ <el-form-item label="璐d换浜�" prop="userId">
+ <el-select v-model="form.userId" placeholder="璇烽�夋嫨璐d换浜�" style="width: 100%" filterable>
<el-option
- v-for="item in statusOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
+ v-for="item in userList"
+ :key="item.userId"
+ :label="item.userId + ' - ' + (item.nickName || item.userName)"
+ :value="item.userId"
/>
</el-select>
</el-form-item>
@@ -270,312 +132,389 @@
</el-row>
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="琛岄┒璇佺紪鍙凤細" prop="licenseNumber">
- <el-input
- v-model="form.licenseNumber"
- placeholder="璇疯緭鍏ヨ椹惰瘉缂栧彿"
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鍙戣瘉鏃ユ湡锛�" prop="licenseIssueDate">
- <el-date-picker
- v-model="form.licenseIssueDate"
- type="date"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- placeholder="璇烽�夋嫨鍙戣瘉鏃ユ湡"
- style="width: 100%"
- />
+ <el-form-item label="鐘舵��" prop="vehicleStatus">
+ <el-select v-model="form.vehicleStatus" placeholder="璇烽�夋嫨鐘舵��" style="width: 100%">
+ <el-option label="鍦ㄧ敤" value="NORMAL" />
+ <el-option label="闂茬疆" value="IDLE" />
+ <el-option label="缁翠慨" value="REPAIR" />
+ </el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
- <el-form-item label="鍒版湡鏃ユ湡锛�" prop="licenseExpireDate">
+ <el-form-item label="骞存鍒版湡" prop="inspectionExpireDate">
<el-date-picker
- v-model="form.licenseExpireDate"
+ v-model="form.inspectionExpireDate"
type="date"
value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
+ placeholder="璇烽�夋嫨鍒版湡鏃ユ湡"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="淇濋櫓鍒版湡" prop="insuranceExpireDate">
+ <el-date-picker
+ v-model="form.insuranceExpireDate"
+ type="date"
+ value-format="YYYY-MM-DD"
placeholder="璇烽�夋嫨鍒版湡鏃ユ湡"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+ </el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
- <el-button @click="handleCancel">鍙� 娑�</el-button>
- <el-button type="primary" @click="handleSubmit">淇� 瀛�</el-button>
+ <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+ <el-button @click="cancel">鍙� 娑�</el-button>
</div>
+ </template>
+ </el-dialog>
+
+ <!-- 璐圭敤绠$悊鎶藉眽 -->
+ <el-drawer v-model="costDrawer" :title="'璐圭敤绠$悊 - ' + currentVehiclePlate" size="50%">
+ <div style="padding: 20px;">
+ <div style="margin-bottom: 20px;">
+ <el-button type="primary" @click="handleAddCost">鏂板璁板綍</el-button>
+ </div>
+ <el-table v-loading="costLoading" :data="costList" border>
+ <el-table-column label="璐圭敤绫诲瀷" prop="costType">
+ <template #default="scope">
+ {{ costTypeLabel(scope.row.costType) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="閲戦(鍏�)" prop="amount" align="right" />
+ <el-table-column label="鍙戠敓鏃ユ湡" prop="costDate" />
+ <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
+ <el-table-column label="鎿嶄綔" width="100" align="center">
+ <template #default="scope">
+ <el-button type="danger" link size="small" @click="handleDeleteCost(scope.row)">
+ 鍒犻櫎
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ v-show="costTotal > 0"
+ :total="costTotal"
+ v-model:page="costQuery.current"
+ v-model:limit="costQuery.size"
+ @pagination="getCostList"
+ />
+ </div>
+ </el-drawer>
+
+ <!-- 鏂板璐圭敤寮圭獥 -->
+ <el-dialog title="鏂板璐圭敤" v-model="costOpen" width="400px" append-to-body>
+ <el-form ref="costFormRef" :model="costForm" :rules="costRules" label-width="100px">
+ <el-form-item label="璐圭敤绫诲瀷" prop="costType">
+ <el-select v-model="costForm.costType" placeholder="璇烽�夋嫨" style="width:100%">
+ <el-option label="淇濆吇" value="MAINTENANCE" />
+ <el-option label="骞存" value="INSPECTION" />
+ <el-option label="淇濋櫓" value="INSURANCE" />
+ <el-option label="缁翠慨" value="REPAIR" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="閲戦(鍏�)" prop="amount">
+ <el-input-number v-model="costForm.amount" :precision="2" :min="0" style="width:100%" />
+ </el-form-item>
+ <el-form-item label="鍙戠敓鏃ユ湡" prop="costDate">
+ <el-date-picker v-model="costForm.costDate" type="date" value-format="YYYY-MM-DD" style="width:100%" />
+ </el-form-item>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="costForm.remark" type="textarea" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button type="primary" @click="submitCostForm">纭� 瀹�</el-button>
+ <el-button @click="costOpen = false">鍙� 娑�</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
-import { ref, reactive } from "vue";
-import { ElMessage, ElMessageBox } from "element-plus";
+import { ref, reactive, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { listVehicleInfo, getWarningList, addVehicleInfo, updateVehicleInfo, delVehicleInfo } from '@/api/inventoryManagement/vehicle';
+import request from '@/utils/request';
-// 妯℃嫙杞﹁締鍩虹鏁版嵁
-const allVehicles = ref([
- {
- id: 1,
- vehicleCode: "CL-202401",
- plateNumber: "绮12345",
- vehicleType: "鍘㈠紡璐ц溅",
- department: "鐗╂祦涓�閮�",
- purchaseDate: "2022-03-15",
- licenseNumber: "4401-2022-0001",
- licenseIssueDate: "2022-03-10",
- licenseExpireDate: "2026-03-10",
- status: "鍦ㄧ敤",
- archived: false,
- },
- {
- id: 2,
- vehicleCode: "CL-202402",
- plateNumber: "绮67890",
- vehicleType: "鍐疯棌杞�",
- department: "鐗╂祦浜岄儴",
- purchaseDate: "2021-08-01",
- licenseNumber: "4401-2021-0123",
- licenseIssueDate: "2021-07-28",
- licenseExpireDate: "2025-07-28",
- status: "缁翠慨",
- archived: false,
- },
- {
- id: 3,
- vehicleCode: "CL-202403",
- plateNumber: "绮11223",
- vehicleType: "鐗靛紩杞�",
- department: "椤圭洰杩愯緭閮�",
- purchaseDate: "2020-05-20",
- licenseNumber: "4401-2020-0456",
- licenseIssueDate: "2020-05-18",
- licenseExpireDate: "2024-05-18",
- status: "闂茬疆",
- archived: false,
- },
- {
- id: 4,
- vehicleCode: "CL-202404",
- plateNumber: "绮33445",
- vehicleType: "鍘㈠紡璐ц溅",
- department: "璧勪骇绠$悊閮�",
- purchaseDate: "2019-11-11",
- licenseNumber: "4401-2019-0789",
- licenseIssueDate: "2019-11-08",
- licenseExpireDate: "2023-11-08",
- status: "鍦ㄧ敤",
- archived: true,
- },
-]);
+const loading = ref(true);
+const tableData = ref([]);
+const total = ref(0);
+const open = ref(false);
+const title = ref("");
+const formRef = ref(null);
-// 涓嬫媺鏋氫妇
-const vehicleTypeOptions = [
- { label: "鍘㈠紡璐ц溅", value: "鍘㈠紡璐ц溅" },
- { label: "鍐疯棌杞�", value: "鍐疯棌杞�" },
- { label: "鐗靛紩杞�", value: "鐗靛紩杞�" },
- { label: "鍏朵粬", value: "鍏朵粬" },
-];
+const deptOptions = ref([]);
+const userList = ref([]);
-const departmentOptions = [
- { label: "鐗╂祦涓�閮�", value: "鐗╂祦涓�閮�" },
- { label: "鐗╂祦浜岄儴", value: "鐗╂祦浜岄儴" },
- { label: "椤圭洰杩愯緭閮�", value: "椤圭洰杩愯緭閮�" },
- { label: "璧勪骇绠$悊閮�", value: "璧勪骇绠$悊閮�" },
-];
-
-const statusOptions = [
- { label: "鍦ㄧ敤", value: "鍦ㄧ敤" },
- { label: "闂茬疆", value: "闂茬疆" },
- { label: "缁翠慨", value: "缁翠慨" },
-];
-
-// 鏌ヨ琛ㄥ崟
-const searchForm = reactive({
- plateNumber: "",
- vehicleType: "",
- department: "",
- status: "",
- archived: "",
+const queryParams = reactive({
+ current: 1,
+ size: 10,
+ plateNumber: undefined,
+ deptId: undefined,
+ vehicleStatus: undefined
});
-// 琛ㄦ牸鏁版嵁
-const tableData = ref([...allVehicles.value]);
-
-// 寮圭獥 & 琛ㄥ崟
-const dialogVisible = ref(false);
-const dialogTitle = ref("鏂板杞﹁締");
-const isEdit = ref(false);
-const formRef = ref(null);
const form = reactive({
- id: null,
- vehicleCode: "",
- plateNumber: "",
- vehicleType: "",
- department: "",
- purchaseDate: "",
- licenseNumber: "",
- licenseIssueDate: "",
- licenseExpireDate: "",
- status: "鍦ㄧ敤",
- archived: false,
+ id: undefined,
+ plateNumber: undefined,
+ propertyOwnership: undefined,
+ deptId: undefined,
+ userId: undefined,
+ vehicleStatus: 'NORMAL',
+ inspectionExpireDate: undefined,
+ insuranceExpireDate: undefined,
+ remark: undefined
});
const rules = {
- vehicleCode: [{ required: true, message: "璇疯緭鍏ヨ溅杈嗙紪鍙�", trigger: "blur" }],
- plateNumber: [{ required: true, message: "璇疯緭鍏ヨ溅鐗屽彿鐮�", trigger: "blur" }],
- vehicleType: [{ required: true, message: "璇烽�夋嫨杞﹁締绫诲瀷", trigger: "change" }],
- department: [{ required: true, message: "璇烽�夋嫨鎵�灞為儴闂�", trigger: "change" }],
- purchaseDate: [{ required: true, message: "璇烽�夋嫨璐疆鏃ユ湡", trigger: "change" }],
- status: [{ required: true, message: "璇烽�夋嫨鐘舵��", trigger: "change" }],
- licenseNumber: [{ required: true, message: "璇疯緭鍏ヨ椹惰瘉缂栧彿", trigger: "blur" }],
- licenseIssueDate: [{ required: true, message: "璇烽�夋嫨鍙戣瘉鏃ユ湡", trigger: "change" }],
+ plateNumber: [{ required: true, message: "杞︾墝鍙风爜涓嶈兘涓虹┖", trigger: "blur" }],
+ deptId: [{ required: true, message: "鎵�灞為儴闂ㄤ笉鑳戒负绌�", trigger: "change" }],
+ vehicleStatus: [{ required: true, message: "鐘舵�佷笉鑳戒负绌�", trigger: "change" }]
};
-// 鏌ヨ
-const handleQuery = () => {
- tableData.value = allVehicles.value.filter((item) => {
- if (
- searchForm.plateNumber &&
- !item.plateNumber.includes(searchForm.plateNumber.trim())
- ) {
- return false;
- }
- if (searchForm.vehicleType && item.vehicleType !== searchForm.vehicleType) {
- return false;
- }
- if (searchForm.department && item.department !== searchForm.department) {
- return false;
- }
- if (searchForm.status && item.status !== searchForm.status) {
- return false;
- }
- if (searchForm.archived !== "") {
- const targetArchived = searchForm.archived === "true";
- if (item.archived !== targetArchived) return false;
- }
- return true;
+// 瀛楀吀鑾峰彇
+function getDicts() {
+ request({url: '/system/user/deptTree', method: 'get'}).then(res => {
+ deptOptions.value = res.data || [];
});
-};
+ request({url: '/system/user/list', method: 'get'}).then(res => {
+ userList.value = res.rows || res.data || [];
+ });
+}
-const resetSearch = () => {
- searchForm.plateNumber = "";
- searchForm.vehicleType = "";
- searchForm.department = "";
- searchForm.status = "";
- searchForm.archived = "";
+
+const statTotal = ref(0);
+const statNormal = ref(0);
+const statIdle = ref(0);
+const statRepair = ref(0);
+
+function getStats() {
+ listVehicleInfo({ current: 1, size: 10000 }).then(res => {
+ const arr = res.data.records || [];
+ statTotal.value = arr.length;
+ statNormal.value = arr.filter(i => i.vehicleStatus === 'NORMAL').length;
+ statIdle.value = arr.filter(i => i.vehicleStatus === 'IDLE').length;
+ statRepair.value = arr.filter(i => i.vehicleStatus === 'REPAIR').length;
+ });
+}
+
+function getList() {
+ loading.value = true;
+ listVehicleInfo(queryParams).then(response => {
+ tableData.value = response.data.records;
+ total.value = response.data.total;
+ loading.value = false;
+ }).catch(() => {
+ loading.value = false;
+ });
+}
+
+function handleWarningList() {
+ loading.value = true;
+ getWarningList().then(response => {
+ tableData.value = response.data;
+ total.value = response.data.length;
+ loading.value = false;
+ ElMessage.info("宸插睍绀�30澶╁唴鍒版湡鐨勯璀﹁溅杈�");
+ }).catch(() => {
+ loading.value = false;
+ });
+}
+
+function handleQuery() {
+ queryParams.current = 1;
+ getList();
+ getStats();
+}
+
+function resetQuery() {
+ queryParams.plateNumber = undefined;
+ queryParams.deptId = undefined;
+ queryParams.vehicleStatus = undefined;
handleQuery();
-};
+}
-// 鏂板
-const openAdd = () => {
- dialogTitle.value = "鏂板杞﹁締";
- isEdit.value = false;
+function reset() {
Object.assign(form, {
- id: null,
- vehicleCode: "",
- plateNumber: "",
- vehicleType: "",
- department: "",
- purchaseDate: "",
- licenseInfo: "",
- status: "鍦ㄧ敤",
- archived: false,
+ id: undefined,
+ plateNumber: undefined,
+ propertyOwnership: undefined,
+ deptId: undefined,
+ userId: undefined,
+ vehicleStatus: 'NORMAL',
+ inspectionExpireDate: undefined,
+ insuranceExpireDate: undefined,
+ remark: undefined
});
- dialogVisible.value = true;
-};
+ if (formRef.value) formRef.value.resetFields();
+}
-// 缂栬緫
-const openEdit = (row) => {
- dialogTitle.value = "缂栬緫杞﹁締";
- isEdit.value = true;
+function handleAdd() {
+ reset();
+ open.value = true;
+ title.value = "鏂板杞﹁締";
+}
+
+function handleUpdate(row) {
+ reset();
Object.assign(form, row);
- dialogVisible.value = true;
-};
+ open.value = true;
+ title.value = "淇敼杞﹁締";
+}
-// 淇濆瓨
-const handleSubmit = () => {
- if (!formRef.value) return;
- formRef.value.validate((valid) => {
- if (!valid) return;
- if (isEdit.value) {
- const index = allVehicles.value.findIndex((v) => v.id === form.id);
- if (index !== -1) {
- allVehicles.value[index] = { ...form };
+function submitForm() {
+ formRef.value.validate(valid => {
+ if (valid) {
+ if (form.id != null) {
+ updateVehicleInfo(form).then(response => {
+ ElMessage.success("淇敼鎴愬姛");
+ open.value = false;
+ getList();
+ getStats();
+ });
+ } else {
+ addVehicleInfo(form).then(response => {
+ ElMessage.success("鏂板鎴愬姛");
+ open.value = false;
+ getList();
+ getStats();
+ });
}
- ElMessage.success("杞﹁締淇℃伅宸叉洿鏂�");
- } else {
- const newId = allVehicles.value.length
- ? Math.max(...allVehicles.value.map((v) => v.id)) + 1
- : 1;
- allVehicles.value.push({ ...form, id: newId });
- ElMessage.success("杞﹁締淇℃伅宸叉柊澧�");
}
- dialogVisible.value = false;
- handleQuery();
});
-};
+}
-const handleCancel = () => {
- dialogVisible.value = false;
-};
-
-// 褰掓。
-const archiveRow = (row) => {
- if (row.archived) return;
- ElMessageBox.confirm(
- "鏄惁纭灏嗚杞﹁締褰掓。锛熷綊妗e悗浠呬繚鐣欐煡璇紝涓嶅啀鍙備笌杩愯緭浠诲姟鍒嗛厤銆�",
- "褰掓。鎻愮ず",
- {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- }
- )
- .then(() => {
- row.archived = true;
- if (row.status === "鍦ㄧ敤") {
- row.status = "闂茬疆";
- }
- ElMessage.success("杞﹁締宸插綊妗�");
- handleQuery();
- })
- .catch(() => {});
-};
-
-// 鍒犻櫎
-const removeRow = (row) => {
- ElMessageBox.confirm("鏄惁纭鍒犻櫎璇ヨ溅杈嗗熀纭�淇℃伅锛�", "鍒犻櫎鎻愮ず", {
- confirmButtonText: "纭",
+function handleDelete(row) {
+ ElMessageBox.confirm('鏄惁纭鍒犻櫎璇ヨ溅杈嗘暟鎹紵', "璀﹀憡", {
+ confirmButtonText: "纭畾",
cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- allVehicles.value = allVehicles.value.filter((v) => v.id !== row.id);
- handleQuery();
- ElMessage.success("鍒犻櫎鎴愬姛");
- })
- .catch(() => {});
-};
+ type: "warning"
+ }).then(function() {
+ return delVehicleInfo(row.id);
+ }).then(() => {
+ getList();
+ getStats();
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ });
+}
-// 鐘舵�佹牱寮�
+function cancel() {
+ open.value = false;
+ reset();
+}
+
+
+function isExpired(dateStr) {
+ if (!dateStr) return false;
+ const targetDate = new Date(dateStr).getTime();
+ const now = new Date().getTime();
+ return targetDate < now;
+}
+
+function isExpiringSoon(dateStr) {
+ if (!dateStr) return false;
+ const targetDate = new Date(dateStr).getTime();
+ const now = new Date().getTime();
+ const diffDays = (targetDate - now) / (1000 * 3600 * 24);
+ return diffDays >= 0 && diffDays <= 30;
+}
const statusTagType = (status) => {
- if (status === "鍦ㄧ敤") return "success";
- if (status === "闂茬疆") return "info";
- if (status === "缁翠慨") return "warning";
+ if (status === "NORMAL") return "success";
+ if (status === "IDLE") return "info";
+ if (status === "REPAIR") return "warning";
return "default";
};
-</script>
-<style scoped lang="scss">
-.dialog-footer {
- text-align: right;
+const statusLabel = (status) => {
+ if (status === "NORMAL") return "鍦ㄧ敤";
+ if (status === "IDLE") return "闂茬疆";
+ if (status === "REPAIR") return "缁翠慨";
+ return status;
+};
+
+// 璐圭敤绠$悊閫昏緫
+const costDrawer = ref(false);
+const currentVehicleId = ref(null);
+const currentVehiclePlate = ref("");
+const costLoading = ref(false);
+const costList = ref([]);
+const costTotal = ref(0);
+const costQuery = reactive({ current: 1, size: 10, vehicleId: null });
+
+function costTypeLabel(type) {
+ const map = { MAINTENANCE: '淇濆吇', INSPECTION: '骞存', INSURANCE: '淇濋櫓', REPAIR: '缁翠慨' };
+ return map[type] || type;
}
-</style>
+function handleCost(row) {
+ currentVehicleId.value = row.id;
+ currentVehiclePlate.value = row.plateNumber;
+ costQuery.vehicleId = row.id;
+ costDrawer.value = true;
+ getCostList();
+}
+
+function getCostList() {
+ costLoading.value = true;
+ request({ url: '/vehicle/cost/listPage', method: 'get', params: costQuery }).then(res => {
+ costList.value = res.data.records;
+ costTotal.value = res.data.total;
+ costLoading.value = false;
+ });
+}
+
+const costOpen = ref(false);
+const costFormRef = ref(null);
+const costForm = reactive({ vehicleId: null, costType: undefined, amount: undefined, costDate: undefined, remark: undefined });
+const costRules = {
+ costType: [{ required: true, message: "璐圭敤绫诲瀷涓嶈兘涓虹┖", trigger: "change" }],
+ amount: [{ required: true, message: "閲戦涓嶈兘涓虹┖", trigger: "blur" }],
+ costDate: [{ required: true, message: "鏃ユ湡涓嶈兘涓虹┖", trigger: "change" }]
+};
+
+function handleAddCost() {
+ costForm.vehicleId = currentVehicleId.value;
+ costForm.costType = undefined;
+ costForm.amount = undefined;
+ costForm.costDate = undefined;
+ costForm.remark = undefined;
+ if(costFormRef.value) costFormRef.value.resetFields();
+ costOpen.value = true;
+}
+
+function submitCostForm() {
+ costFormRef.value.validate(valid => {
+ if (valid) {
+ request({ url: '/vehicle/cost', method: 'post', data: costForm }).then(res => {
+ ElMessage.success("娣诲姞鎴愬姛");
+ costOpen.value = false;
+ getCostList();
+ });
+ }
+ });
+}
+
+function handleDeleteCost(row) {
+ ElMessageBox.confirm('纭畾鍒犻櫎璇ヨ垂鐢ㄨ褰曪紵', '鎻愮ず', { type: 'warning' }).then(() => {
+ request({ url: '/vehicle/cost/' + row.id, method: 'delete' }).then(() => {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ getCostList();
+ });
+ });
+}
+
+onMounted(() => {
+ getDicts();
+ getList();
+ getStats();
+});
+</script>
\ No newline at end of file
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue
index d057f16..e6512e6 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue
@@ -145,6 +145,22 @@
</div>
</div>
</template>
+
+ <!-- 鐘舵�佸彉鏇村鎵硅鎯� -->
+ <template v-else-if="row.businessType === 19">
+ <div class="device-status-detail">
+ <el-divider content-position="left">璁惧鐘舵�佸彉鏇磋鎯�</el-divider>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="璁惧鍚嶇О">{{ detailData.deviceName || "--" }}</el-descriptions-item>
+ <el-descriptions-item label="鐩爣鐘舵��">
+ <el-tag :type="detailData.targetStatus === 1 ? 'success' : 'danger'">
+ {{ detailData.targetStatusName || "--" }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鍙樻洿鍘熷洜" :span="2">{{ detailData.reason || "--" }}</el-descriptions-item>
+ </el-descriptions>
+ </div>
+ </template>
</div>
<div v-if="attachmentList.length"
class="detail-block">
@@ -199,9 +215,9 @@
const formResolved = computed(() => resolveInstanceFormFields(props.row));
- // 鏄惁涓虹壒娈婂鎵圭被鍨嬶紙閲囪喘銆佸彂璐с�佹姤浠凤級
+ // 鏄惁涓虹壒娈婂鎵圭被鍨嬶紙閲囪喘銆佸彂璐с�佹姤浠枫�佽澶囩姸鎬佸彉鏇达級
const isSpecialApprovalType = computed(() => {
- return [5, 7, 6].includes(props.row.businessType);
+ return [5, 7, 6, 19].includes(props.row.businessType);
});
// 璇︽儏鏁版嵁锛堢洿鎺ヤ娇鐢ㄤ紶鍏ョ殑 detail-data 鍙傛暟锛�
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
index 737e2de..742fc5c 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
@@ -327,6 +327,19 @@
}
}
+ // 璁惧鐘舵�佸彉鏇村鎵�
+ else if (row.businessType === 19) {
+ if (row.formConfig) {
+ try {
+ detailData.value = JSON.parse(row.formConfig);
+ } catch(e) {
+ detailData.value = {};
+ }
+ } else {
+ detailData.value = {};
+ }
+ }
+
// 鍏朵粬瀹℃壒绫诲瀷
else {
detailData.value = {};
@@ -375,6 +388,19 @@
}
}
+ // 璁惧鐘舵�佸彉鏇村鎵�
+ else if (row.businessType === 19) {
+ if (row.formConfig) {
+ try {
+ detailData.value = JSON.parse(row.formConfig);
+ } catch(e) {
+ detailData.value = {};
+ }
+ } else {
+ detailData.value = {};
+ }
+ }
+
// 鍏朵粬瀹℃壒绫诲瀷
else {
detailData.value = {};
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/components/DetailPanel.vue b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/components/DetailPanel.vue
index 4db16a7..dcd0082 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/components/DetailPanel.vue
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/components/DetailPanel.vue
@@ -11,6 +11,8 @@
<el-descriptions-item label="鍛樺伐濮撳悕">{{ row.employeeName || row.applicantName || "鈥�" }}</el-descriptions-item>
<el-descriptions-item label="鎶ラ攢鍘熷洜" :span="2">{{ row.reimburseReason || "鈥�" }}</el-descriptions-item>
<el-descriptions-item label="鎶ラ攢閲戦">{{ row.applyAmount != null ? `${row.applyAmount} 鍏僠 : "鈥�" }}</el-descriptions-item>
+ <el-descriptions-item label="閮ㄩ棬">{{ row.applicantDeptName || row.deptName || "鈥�" }}</el-descriptions-item>
+ <el-descriptions-item label="浠樻鏂�">{{ row.payer || "鈥�" }}</el-descriptions-item>
<el-descriptions-item label="鏀舵浜�">{{ row.payee || "鈥�" }}</el-descriptions-item>
<el-descriptions-item label="鏀舵璐﹀彿">{{ row.payeeAccount || "鈥�" }}</el-descriptions-item>
<el-descriptions-item label="寮�鎴锋敮琛�">{{ row.bankBranch || "鈥�" }}</el-descriptions-item>
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/costReimburseUtils.js b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/costReimburseUtils.js
index 1736b3e..f42834f 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/costReimburseUtils.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/costReimburseUtils.js
@@ -201,6 +201,7 @@
reimburseReason: "",
applyAmount: undefined,
payee: "",
+ payer: "",
payeeAccount: "",
bankBranch: "",
expenseDetails: [],
@@ -295,6 +296,7 @@
reimburseReason: raw.reimburseReason ?? "",
applyAmount,
payee: raw.payee ?? "",
+ payer: raw.payer ?? "",
payeeAccount: raw.payeeAccount ?? "",
bankBranch: raw.bankBranch ?? "",
expenseDetails,
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
index 661c990..a74d541 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/index.vue
@@ -12,12 +12,43 @@
:prefix-icon="Search"
@keyup.enter="handleQuery"
/>
+ <span class="search_title" style="margin-left: 10px;">閮ㄩ棬锛�</span>
+ <el-tree-select
+ v-model="searchForm.deptId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ value-key="id"
+ placeholder="閫夋嫨閮ㄩ棬"
+ check-strictly
+ clearable
+ style="width: 180px"
+ />
+ <span class="search_title" style="margin-left: 10px;">椤圭洰锛�</span>
+ <el-select v-model="searchForm.expenseCategory" clearable placeholder="閫夋嫨椤圭洰" style="width: 150px">
+ <el-option
+ v-for="opt in EXPENSE_CATEGORY_OPTIONS"
+ :key="opt.value"
+ :label="opt.label"
+ :value="opt.value"
+ />
+ </el-select>
+ <span class="search_title" style="margin-left: 10px;">鐢宠鏃堕棿锛�</span>
+ <el-date-picker
+ v-model="searchForm.applyTimeRange"
+ type="daterange"
+ range-separator="-"
+ start-placeholder="寮�濮�"
+ end-placeholder="缁撴潫"
+ value-format="YYYY-MM-DD"
+ clearable
+ style="width: 240px"
+ />
<el-button type="primary" style="margin-left: 10px" @click="handleQuery">鎼滅储</el-button>
<el-button @click="resetSearch">閲嶇疆</el-button>
</div>
<div class="search_actions">
<!-- <el-button type="success" plain @click="handleImportClick">瀵煎叆</el-button>-->
-<!-- <el-button type="warning" plain @click="handleExport">瀵煎嚭</el-button>-->
+ <el-button type="warning" plain @click="handleExport">瀵煎嚭</el-button>
<el-button type="primary" @click="openFormDialog('add')">鏂板璐圭敤鎶ラ攢</el-button>
</div>
</div>
@@ -79,12 +110,21 @@
<el-card class="form-section" shadow="never">
<template #header><span class="card-header-title">鍩烘湰淇℃伅</span></template>
<el-row :gutter="20">
- <el-col :span="12">
- <el-form-item label="鍛樺伐缂栧彿">
- <el-input v-model="form.employeeNo" readonly placeholder="閫夋嫨鍛樺伐鍚庤嚜鍔ㄥ甫鍑�" />
+ <el-col :span="8">
+ <el-form-item label="褰掑睘閮ㄩ棬" prop="deptId">
+ <el-tree-select
+ v-model="form.deptId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ value-key="id"
+ placeholder="璇烽�夋嫨閮ㄩ棬"
+ check-strictly
+ style="width: 100%"
+ @change="onDeptChange"
+ />
</el-form-item>
</el-col>
- <el-col :span="12">
+ <el-col :span="8">
<el-form-item label="鍛樺伐濮撳悕" prop="applicantId">
<el-select
v-model="form.applicantId"
@@ -105,6 +145,11 @@
:value="u.userId"
/>
</el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鍛樺伐缂栧彿">
+ <el-input v-model="form.employeeNo" readonly placeholder="閫夋嫨鍛樺伐鍚庤嚜鍔ㄥ甫鍑�" />
</el-form-item>
</el-col>
</el-row>
@@ -248,17 +293,22 @@
<el-card class="form-section" shadow="never">
<template #header><span class="card-header-title">鏀舵淇℃伅</span></template>
<el-row :gutter="20">
- <el-col :span="8">
+ <el-col :span="6">
+ <el-form-item label="浠樻鏂�" prop="payer">
+ <el-input v-model="form.payer" placeholder="璇疯緭鍏ヤ粯娆炬柟" maxlength="50" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="6">
<el-form-item label="鏀舵浜�" prop="payee">
<el-input v-model="form.payee" placeholder="璇疯緭鍏ユ敹娆句汉" maxlength="50" />
</el-form-item>
</el-col>
- <el-col :span="8">
+ <el-col :span="6">
<el-form-item label="鏀舵璐﹀彿" prop="payeeAccount">
<el-input v-model="form.payeeAccount" placeholder="閾惰鍗″彿" maxlength="30" />
</el-form-item>
</el-col>
- <el-col :span="8">
+ <el-col :span="6">
<el-form-item label="寮�鎴锋敮琛�" prop="bankBranch">
<el-input v-model="form.bankBranch" placeholder="寮�鎴锋敮琛屽叏绉�" maxlength="100" />
</el-form-item>
@@ -404,6 +454,7 @@
approveOpinion,
applicantFormSearchLoading,
applicantFormOptions,
+ deptOptions,
flowUserOptions,
detailTotalAmount,
approvalRuleHint,
@@ -413,6 +464,7 @@
remoteSearchApplicantForm,
userSelectLabel,
onApplicantChange,
+ onDeptChange,
onExpenseCategoryChange,
applyTemplate,
onDetailAmountChange,
diff --git a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
index 3b90a3b..9d8b616 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/cost-reimburse/useCostReimburse.js
@@ -7,7 +7,7 @@
persistFinReimbursement,
} from "@/api/officeProcessAutomation/finReimbursement.js";
import { ElMessageBox } from "element-plus";
-import { userListNoPageByTenantId } from "@/api/system/user.js";
+import { userListNoPageByTenantId, deptTreeSelect } from "@/api/system/user.js";
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref } from "vue";
import {
buildCostReimbursementSaveDto,
@@ -72,6 +72,9 @@
const searchForm = reactive({
applicantKeyword: "",
+ deptId: undefined,
+ expenseCategory: "",
+ applyTimeRange: [],
});
const tableLoading = ref(false);
const page = reactive({ current: 1, size: 10, total: 0 });
@@ -79,6 +82,7 @@
const allUsersCache = ref([]);
const applicantFormSearchLoading = ref(false);
const applicantFormOptions = ref([]);
+ const deptOptions = ref([]);
const formRef = ref();
const form = reactive(createEmptyForm());
const formDialog = reactive({ visible: false, title: "", mode: "add", readonly: false });
@@ -151,6 +155,9 @@
{ label: "鎶ラ攢鍗曞彿", prop: "reimburseNo", width: 150 },
{ label: "鐢宠浜虹紪鍙�", prop: "applicantNo", width: 110 },
{ label: "鐢宠浜�", prop: "applicantName", minWidth: 90 },
+ { label: "閮ㄩ棬", prop: "applicantDeptName", minWidth: 100 },
+ { label: "璐圭敤绫诲瀷", prop: "expenseCategory", minWidth: 90, formatData: (v) => expenseCategoryLabel(v) },
+ { label: "浠樻鏂�", prop: "payer", minWidth: 100 },
{ label: "鎶ラ攢閲戦(鍏�)", prop: "applyAmount", width: 110 },
{ label: "鎶ラ攢鍘熷洜", prop: "reimburseReason", minWidth: 160, showOverflowTooltip: true },
{ label: "鐢宠鏃堕棿", prop: "applyTime", width: 165 },
@@ -194,6 +201,7 @@
]);
const formRules = {
+ deptId: [{ required: true, message: "璇烽�夋嫨褰掑睘閮ㄩ棬", trigger: "change" }],
applicantId: [{ required: true, message: "璇烽�夋嫨鍛樺伐", trigger: "change" }],
expenseCategory: [{ required: true, message: "璇烽�夋嫨璐圭敤绫诲瀷", trigger: "change" }],
reimburseReason: [{ required: true, message: "璇峰~鍐欐姤閿�鍘熷洜", trigger: "blur" }],
@@ -222,21 +230,39 @@
async function loadUserPool() {
try {
- allUsersCache.value = unwrapArray(await userListNoPageByTenantId());
+ allUsersCache.value = unwrapArray(await userListNoPageByTenantId({}));
} catch {
allUsersCache.value = [];
+ }
+ }
+
+ function formatDeptOptions(list) {
+ return (list || []).map((d) => ({
+ ...d,
+ rawLabel: d.label,
+ label: d.label, // 閮ㄩ棬涓嬫媺涓嶈鏄剧ずid
+ children: d.children && d.children.length ? formatDeptOptions(d.children) : undefined,
+ }));
+ }
+
+ async function loadDeptOptions() {
+ try {
+ const res = await deptTreeSelect();
+ deptOptions.value = formatDeptOptions(res.data || []);
+ } catch {
+ deptOptions.value = [];
}
}
function userSelectLabel(u) {
const nick = u.nickName || "";
const name = u.userName || "";
- if (nick && name && nick !== name) return `${nick}锛�${name}锛塦;
- return nick || name || `鐢ㄦ埛${u.userId ?? u.id ?? ""}`;
+ const empNo = employeeNoFromUser(u);
+ return `${nick || name || "鐢ㄦ埛"} - ${empNo}`;
}
function userById(id) {
- return allUsersCache.value.find((u) => String(u.userId ?? u.id) === String(id));
+ return applicantFormOptions.value.find((u) => String(u.userId ?? u.id) === String(id));
}
function employeeNoFromUser(u) {
@@ -244,22 +270,21 @@
return u.userName ?? u.userCode ?? u.jobNumber ?? u.workNo ?? (u.userId != null ? String(u.userId) : "");
}
- function filterUsersByQuery(query) {
- const list = allUsersCache.value.filter(isActiveUser);
- const q = (query || "").trim().toLowerCase();
- if (!q) return [...list];
- return list.filter((u) => {
- const nick = (u.nickName || "").toLowerCase();
- const uname = (u.userName || "").toLowerCase();
- return nick.includes(q) || uname.includes(q);
- });
- }
-
async function remoteSearchApplicantForm(query) {
applicantFormSearchLoading.value = true;
try {
- if (!allUsersCache.value.length) await loadUserPool();
- applicantFormOptions.value = filterUsersByQuery(query);
+ const q = (query || "").trim();
+ const params = {};
+ if (form.deptId) {
+ params.deptId = form.deptId;
+ }
+ if (q) {
+ params.nickName = q; // You can pass nickName to backend
+ }
+ const res = await userListNoPageByTenantId(params);
+ applicantFormOptions.value = unwrapArray(res).filter(isActiveUser);
+ } catch {
+ applicantFormOptions.value = [];
} finally {
applicantFormSearchLoading.value = false;
}
@@ -271,11 +296,46 @@
form.employeeName = u.nickName || u.userName || "";
form.employeeNo = employeeNoFromUser(u);
form.payee = form.payee || form.employeeName;
- form.deptId = String(u.deptId ?? u.sysDeptId ?? "");
- form.deptName = u.dept?.deptName ?? u.deptName ?? "";
+ const userDeptId = String(u.deptId ?? u.sysDeptId ?? "");
+ if (userDeptId && form.deptId !== userDeptId) {
+ form.deptId = userDeptId;
+ const name = findDeptLabel(deptOptions.value, userDeptId);
+ form.deptName = name || (u.dept?.deptName ?? u.deptName ?? "");
+ }
} else {
form.employeeName = "";
form.employeeNo = "";
+ }
+ }
+
+ function findDeptLabel(nodes, id) {
+ if (!nodes) return null;
+ for (const node of nodes) {
+ if (String(node.id) === String(id)) return node.rawLabel || node.label;
+ if (node.children) {
+ const hit = findDeptLabel(node.children, id);
+ if (hit) return hit;
+ }
+ }
+ return null;
+ }
+
+ function onDeptChange(deptId) {
+ if (deptId) {
+ const name = findDeptLabel(deptOptions.value, deptId);
+ if (name) form.deptName = name;
+
+ remoteSearchApplicantForm("");
+ if (form.applicantId) {
+ const u = userById(form.applicantId);
+ if (u && String(u.deptId ?? u.sysDeptId) !== String(deptId)) {
+ form.applicantId = "";
+ onApplicantChange("");
+ }
+ }
+ } else {
+ form.deptName = "";
+ remoteSearchApplicantForm("");
}
}
@@ -431,17 +491,13 @@
proxy?.$modal?.msgError?.("鍔犺浇鎶ラ攢璇︽儏澶辫触");
return;
}
- Object.assign(form, {
- ...JSON.parse(JSON.stringify(editRow)),
- reimbursementId: editRow.reimbursementId ?? editRow.id,
- attachmentList: JSON.parse(JSON.stringify(editRow.attachmentList || editRow.invoiceAttachments || [])),
- approvalFlowNodes: JSON.parse(JSON.stringify(editRow.approvalFlowNodes || [])),
- expenseDetails: JSON.parse(JSON.stringify(editRow.expenseDetails || [])),
- });
+ Object.assign(form, buildCostReimbursementSaveDto(editRow));
+ formDialog.title = "缂栬緫璐圭敤鎶ラ攢";
+ await remoteSearchApplicantForm("");
const u = userById(editRow.applicantId);
- applicantFormOptions.value = u
- ? [u]
- : [{ userId: editRow.applicantId, nickName: editRow.employeeName, userName: editRow.employeeNo }];
+ if (!u) {
+ applicantFormOptions.value.unshift({ userId: editRow.applicantId, nickName: editRow.employeeName, userName: editRow.applicantCode });
+ }
} else {
form.approvalFlowNodes = buildAutoApprovalFlow(0, "other");
remoteSearchApplicantForm("");
@@ -532,15 +588,17 @@
}
function handleExport() {
- const data = allRows.value;
- const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json;charset=utf-8" });
- const url = URL.createObjectURL(blob);
- const a = document.createElement("a");
- a.href = url;
- a.download = `璐圭敤鎶ラ攢瀵煎嚭_${dayjs().format("YYYYMMDDHHmmss")}.json`;
- a.click();
- URL.revokeObjectURL(url);
- proxy?.$modal?.msgSuccess?.(`宸插鍑� ${data.length} 鏉);
+ const params = buildFinReimbursementListParams({
+ page: { current: 1, size: 99999 }, // Just so the function doesn't crash, backend export ignores pagination anyway
+ searchForm,
+ reimbursementType: FIN_REIMBURSEMENT_TYPE.COST,
+ });
+ // Remove pagination parameters since export doesn't need them
+ delete params.current;
+ delete params.size;
+ delete params["page.current"];
+ delete params["page.size"];
+ proxy.download("/finReimbursement/export", params, `璐圭敤鎶ラ攢瀵煎嚭_${dayjs().format("YYYYMMDDHHmmss")}.xlsx`);
}
function handleImportClick() {
@@ -572,6 +630,7 @@
onMounted(async () => {
loadUserPool();
+ loadDeptOptions();
await fetchList();
const editPayload = consumeReimburseEditFromApprove();
if (editPayload?.reimbursementId != null) {
@@ -603,6 +662,7 @@
approveOpinion,
applicantFormSearchLoading,
applicantFormOptions,
+ deptOptions,
flowUserOptions,
detailTotalAmount,
approvalRuleHint,
@@ -612,6 +672,7 @@
remoteSearchApplicantForm,
userSelectLabel,
onApplicantChange,
+ onDeptChange,
onExpenseCategoryChange,
applyTemplate,
onDetailAmountChange,
diff --git a/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js b/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
index 2525f70..85029bc 100644
--- a/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
+++ b/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
@@ -173,29 +173,61 @@
return out;
}
-/** 鏄惁瀛樺湪鍒楄〃绛涢�夋潯浠讹紙浠呯敵璇蜂汉锛� */
-export function hasActiveReimbursementSearch(searchForm = {}) {
- return Boolean((searchForm?.applicantKeyword || "").trim());
+
+export function expenseTypeToCategory(typeLabel) {
+ const hit = EXPENSE_CATEGORY_OPTIONS.find((x) => x.label === typeLabel || x.value === typeLabel);
+ return hit?.value || typeLabel || "";
}
-/** 鏈嶅姟绔湭鐢熸晥鏃讹紝鎸夌敵璇蜂汉鍋氬墠绔厹搴曠瓫閫� */
+/** 鏄惁瀛樺湪鍒楄〃绛涢�夋潯浠� */
+export function hasActiveReimbursementSearch(searchForm = {}) {
+ return Boolean(
+ (searchForm?.applicantKeyword || "").trim() ||
+ searchForm?.deptId ||
+ searchForm?.expenseCategory ||
+ (searchForm?.applyTimeRange && searchForm.applyTimeRange.length === 2)
+ );
+}
+
+/** 鏈嶅姟绔湭鐢熸晥鏃讹紝鎸夋潯浠跺仛鍓嶇鍏滃簳绛涢�� */
export function filterReimbursementRowsBySearch(rows, searchForm = {}) {
const list = Array.isArray(rows) ? rows : [];
- const kw = (searchForm?.applicantKeyword || "").trim().toLowerCase();
- if (!kw) return list;
+ let result = list;
- return list.filter((row) => {
- const parts = [
- row.applicantName,
- row.employeeName,
- row.applicantNo,
- row.applicantCode,
- row.employeeNo,
- ]
- .filter((v) => v != null && v !== "")
- .map((v) => String(v).toLowerCase());
- return parts.some((p) => p.includes(kw));
- });
+ const kw = (searchForm?.applicantKeyword || "").trim().toLowerCase();
+ if (kw) {
+ result = result.filter((row) => {
+ const parts = [
+ row.applicantName,
+ row.employeeName,
+ row.applicantNo,
+ row.applicantCode,
+ row.employeeNo,
+ ]
+ .filter((v) => v != null && v !== "")
+ .map((v) => String(v).toLowerCase());
+ return parts.some((p) => p.includes(kw));
+ });
+ }
+
+ if (searchForm?.deptId) {
+ result = result.filter(row => String(row.deptId) === String(searchForm.deptId));
+ }
+
+ if (searchForm?.expenseCategory) {
+ result = result.filter(row => row.expenseCategory === searchForm.expenseCategory);
+ }
+
+ if (searchForm?.applyTimeRange && searchForm.applyTimeRange.length === 2) {
+ const start = searchForm.applyTimeRange[0];
+ const end = searchForm.applyTimeRange[1];
+ result = result.filter(row => {
+ const t = (row.applyTime || row.createTime || "").substring(0, 10);
+ return t >= start && t <= end;
+ });
+ }
+
+ return result;
}
/** 鎵佸钩鍖栦负 Spring GET 鍙粦瀹氱殑 query锛坒inReimbursementDto.xxx锛屽嬁鐢ㄦ柟鎷彿锛� */
@@ -216,6 +248,10 @@
}) {
const dto = {
reimbursementType,
+ deptId: searchForm.deptId || undefined,
+ expenseType: searchForm.expenseCategory ? expenseCategoryToType(searchForm.expenseCategory) : undefined,
+ createTimeStart: searchForm.applyTimeRange?.[0] || undefined,
+ createTimeEnd: searchForm.applyTimeRange?.[1] || undefined,
...pickApplicantQuery(searchForm),
...(extraDto && typeof extraDto === "object" ? extraDto : {}),
};
@@ -227,6 +263,12 @@
"page.size": page.size,
...dto,
};
+
+ if (searchForm.applyTimeRange && searchForm.applyTimeRange.length === 2) {
+ params["params[beginTime]"] = searchForm.applyTimeRange[0] + " 00:00:00";
+ params["params[endTime]"] = searchForm.applyTimeRange[1] + " 23:59:59";
+ }
+
appendDotNotationQuery(params, "finReimbursementDto", dto);
return params;
}
@@ -319,7 +361,8 @@
applicantName: row.applicantName || "",
employeeNo: row.applicantCode || "",
employeeName: row.applicantName || "",
- applicantDeptName: row.applicantDeptName || "",
+ applicantDeptName: row.deptName || "",
+ deptId: row.deptId,
reimburseReason: row.reason || "",
travelStartTime: formatReimbursementDateTime(travel.startTime),
travelEndTime: formatReimbursementDateTime(travel.endTime),
@@ -375,12 +418,14 @@
applicantName: row.applicantName || "",
employeeNo: row.applicantCode || "",
employeeName: row.applicantName || "",
- applicantDeptName: row.applicantDeptName || "",
+ applicantDeptName: row.deptName || "",
+ deptId: row.deptId,
reimburseReason: row.reason || "",
- expenseCategory: row.expenseType || "",
+ expenseCategory: expenseTypeToCategory(row.expenseType) || "",
applyAmount: row.applyAmount,
applyTime: formatReimbursementDateTime(row.createTime),
payee: row.payeeName || "",
+ payer: row.payer || "",
payeeAccount: row.payeeAccount || "",
bankBranch: row.payeeBank || "",
billStatus: row.billStatus,
@@ -846,6 +891,7 @@
applyAmount,
detailTotalAmount: detailTotal,
payeeName: form.payee || "",
+ payer: form.payer || "",
payeeAccount: form.payeeAccount || "",
payeeBank: form.bankBranch || form.payeeBank || "",
billStatus: "IN_APPROVAL",
diff --git a/src/views/projectManagement/Management/components/ArchiveDialog.vue b/src/views/projectManagement/Management/components/ArchiveDialog.vue
new file mode 100644
index 0000000..01f69cd
--- /dev/null
+++ b/src/views/projectManagement/Management/components/ArchiveDialog.vue
@@ -0,0 +1,175 @@
+<template>
+ <el-dialog
+ title="鏂囨。褰掓。"
+ v-model="visible"
+ width="650px"
+ destroy-on-close
+ append-to-body
+ >
+ <div class="archive-container" v-loading="loading">
+ <el-empty v-if="!loading && (!archiveGroups || archiveGroups.length === 0)" description="鏆傛棤褰掓。闄勪欢" />
+
+ <el-collapse v-else v-model="activeNames">
+ <el-collapse-item
+ v-for="(group, index) in archiveGroups"
+ :key="index"
+ :title="`${group.moduleName} (${group.files ? group.files.length : 0})`"
+ :name="index"
+ >
+ <div class="attachment-list">
+ <div
+ v-for="(file, fileIdx) in group.files"
+ :key="fileIdx"
+ class="attachment-item"
+ >
+ <el-icon class="file-icon"><Document /></el-icon>
+ <a
+ :href="getRealUrl(file.url)"
+ target="_blank"
+ class="file-link"
+ :title="file.name"
+ >
+ {{ file.name }}
+ </a>
+ <el-button link type="primary" class="download-btn" @click="handleDownload(file.url, file.name)">涓嬭浇</el-button>
+ </div>
+ </div>
+ </el-collapse-item>
+ </el-collapse>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="visible = false">鍏抽棴</el-button>
+ </div>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+import { getProjectAttachments } from '@/api/projectManagement/project'
+import { Document } from '@element-plus/icons-vue'
+
+const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ projectId: {
+ type: [Number, String],
+ default: null
+ }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const visible = ref(false)
+const loading = ref(false)
+const archiveGroups = ref([])
+const activeNames = ref([])
+
+watch(() => props.modelValue, (val) => {
+ visible.value = val
+ if (val && props.projectId) {
+ fetchAttachments()
+ } else {
+ archiveGroups.value = []
+ activeNames.value = []
+ }
+})
+
+watch(() => visible.value, (val) => {
+ emit('update:modelValue', val)
+})
+
+const fetchAttachments = async () => {
+ loading.value = true
+ try {
+ const res = await getProjectAttachments(props.projectId)
+ if (res.code === 200) {
+ archiveGroups.value = res.data || []
+ // 榛樿灞曞紑鎵�鏈夐潰鏉�
+ activeNames.value = archiveGroups.value.map((_, index) => index)
+ }
+ } catch (error) {
+ console.error('鑾峰彇闄勪欢澶辫触', error)
+ } finally {
+ loading.value = false
+ }
+}
+
+// 鑾峰彇鐪熷疄鍦板潃锛堝鐞嗗彲鑳界殑鐩稿璺緞鍓嶇紑锛�
+const getRealUrl = (url) => {
+ if (!url) return '#'
+ if (url.startsWith('http://') || url.startsWith('https://')) {
+ return url
+ }
+ return import.meta.env.VITE_APP_BASE_API + url
+}
+
+// 鎻愬彇鏂囦欢鍚�(鍏煎鏃ч�昏緫)
+const getFileName = (url) => {
+ if (!url) return '鏈煡鏂囦欢'
+ const parts = url.split('/')
+ const name = parts[parts.length - 1]
+ // 澶勭悊鍙兘鐨勬椂闂存埑鍓嶇紑
+ const nameParts = name.split('_')
+ return nameParts.length > 1 ? nameParts.slice(1).join('_') : name
+}
+
+const handleDownload = (url, name) => {
+ const realUrl = getRealUrl(url)
+ const a = document.createElement('a')
+ a.style.display = 'none'
+ a.href = realUrl
+ a.download = name || getFileName(url)
+ document.body.appendChild(a)
+ a.click()
+ document.body.removeChild(a)
+}
+</script>
+
+<style scoped>
+.archive-container {
+ min-height: 200px;
+}
+.attachment-list {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 5px 0;
+}
+.attachment-item {
+ display: flex;
+ align-items: center;
+ padding: 8px 12px;
+ background-color: #f8f9fa;
+ border-radius: 4px;
+ transition: background-color 0.3s;
+}
+.attachment-item:hover {
+ background-color: #f0f2f5;
+}
+.file-icon {
+ font-size: 16px;
+ color: #909399;
+ margin-right: 8px;
+}
+.file-link {
+ flex: 1;
+ color: #606266;
+ text-decoration: none;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ margin-right: 15px;
+}
+.file-link:hover {
+ color: #409EFF;
+ text-decoration: underline;
+}
+.download-btn {
+ margin-left: auto;
+}
+</style>
diff --git a/src/views/projectManagement/Management/components/ContractListDialog.vue b/src/views/projectManagement/Management/components/ContractListDialog.vue
new file mode 100644
index 0000000..561297d
--- /dev/null
+++ b/src/views/projectManagement/Management/components/ContractListDialog.vue
@@ -0,0 +1,104 @@
+<template>
+ <el-dialog
+ title="閿�鍞悎鍚屽垪琛�"
+ v-model="visible"
+ width="800px"
+ append-to-body
+ destroy-on-close
+ @open="handleOpen"
+ >
+ <div v-loading="loading">
+ <el-table :data="tableData" border style="width: 100%">
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="salesContractNo" label="閿�鍞悎鍚岀紪鍙�" align="center" show-overflow-tooltip />
+ <el-table-column prop="customerName" label="瀹㈡埛鍚嶇О" align="center" show-overflow-tooltip />
+ </el-table>
+
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="visible = false">鍏� 闂�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import { ref, watch, reactive } from 'vue'
+import { ledgerListPage } from '@/api/salesManagement/salesLedger'
+
+const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ projectId: {
+ type: [Number, String],
+ default: null
+ }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const visible = ref(false)
+const loading = ref(false)
+const tableData = ref([])
+const total = ref(0)
+
+const queryParams = reactive({
+ current: 1,
+ size: 10,
+ projectId: undefined
+})
+
+watch(() => props.modelValue, (val) => {
+ visible.value = val
+})
+
+watch(visible, (val) => {
+ emit('update:modelValue', val)
+})
+
+function handleOpen() {
+ queryParams.current = 1
+ queryParams.projectId = props.projectId
+ getList()
+}
+
+function getList() {
+ if (!queryParams.projectId) {
+ tableData.value = []
+ total.value = 0
+ return
+ }
+
+ loading.value = true
+ ledgerListPage(queryParams).then(res => {
+ const records = res?.data?.records || res?.records || res?.rows || []
+
+ // 濡傛灉涓�鏉¤褰曚篃娌℃湁涓斿湪绗竴椤典互鍚庯紝鍙兘闇�瑕侀噸缃垎椤�
+ // 鍓嶇瑕佹眰鏄剧ず鈥滈攢鍞悎鍚岀紪鍙封�濆拰鈥滃鎴峰悕绉扳�濓紝杩欎簺瀛楁鍦� salesLedgerListPage 杩斿洖鐨勬暟鎹腑瀛樺湪
+ tableData.value = records
+ total.value = res?.data?.total || res?.total || 0
+ }).catch(() => {
+ tableData.value = []
+ total.value = 0
+ }).finally(() => {
+ loading.value = false
+ })
+}
+</script>
+
+<style scoped>
+.dialog-footer {
+ text-align: right;
+}
+</style>
diff --git a/src/views/projectManagement/Management/index.vue b/src/views/projectManagement/Management/index.vue
index 0438aef..67e51c6 100644
--- a/src/views/projectManagement/Management/index.vue
+++ b/src/views/projectManagement/Management/index.vue
@@ -56,15 +56,21 @@
<template #projectStage="{ row }">
<dict-tag :options="plan_status" :value="row.projectStage" />
</template>
+ <template #salesContractCount="{ row }">
+ <el-button link type="primary" @click="handleOpenContractDialog(row)">{{ row.salesContractCount || 0 }}</el-button>
+ </template>
<template #action="{ row }">
<el-button link type="primary" @click="handleEdit(row)">缂栬緫</el-button>
<el-button link type="primary" :loading="progressBtnLoadingId===row.id" @click="handleProgressReport(row)">杩涘害姹囨姤</el-button>
+ <el-button link type="primary" @click="handleArchive(row)">鏂囨。褰掓。</el-button>
<el-button link type="primary" @click="handleDetail(row)">璇︽儏</el-button>
</template>
</PIMTable>
</div>
<FormDia ref="formDiaRef" @completed="getList" />
+ <ArchiveDialog v-model="archiveVisible" :project-id="archiveProjectId" />
+ <ContractListDialog v-model="contractVisible" :project-id="contractProjectId" />
<ProgressReportDialog
v-model="progressReportVisible"
:project-id="progressProjectId"
@@ -82,6 +88,8 @@
import SearchPanel from '@/components/SearchPanel/index.vue'
import PIMTable from '@/components/PIMTable/PIMTable.vue'
import FormDia from './components/formDia.vue'
+import ArchiveDialog from './components/ArchiveDialog.vue'
+import ContractListDialog from './components/ContractListDialog.vue'
import ProgressReportDialog from '@/components/ProjectManagement/ProgressReportDialog.vue'
import {
listProject,
@@ -111,6 +119,10 @@
const progressPlanNodes = ref([])
const progressDefaultPlanNodeId = ref(undefined)
const progressBtnLoadingId = ref(null)
+const archiveVisible = ref(false)
+const archiveProjectId = ref(null)
+const contractVisible = ref(false)
+const contractProjectId = ref(null)
const submitLoading = ref(false)
const auditLoading = ref(false)
const reverseAuditLoading = ref(false)
@@ -154,6 +166,7 @@
{ label: '绔嬮」鏃ユ湡', prop: 'setupDate', align: 'center', width: '120' },
{ label: '椤圭洰鏉ユ簮', prop: 'projectSource', align: 'center' },
{ label: '椤圭洰鍒嗙被', prop: 'projectClassification', align: 'center' },
+ { label: '閿�鍞悎鍚屾�绘暟', prop: 'salesContractCount', align: 'center', dataType: 'slot', slot: 'salesContractCount' },
{ label: '鎿嶄綔', prop: 'action', align: 'center', width: '250', dataType: 'slot', slot: 'action', fixed: 'right' }
]
@@ -188,6 +201,7 @@
projectType: r.planName ?? r.projectType,
projectSource: r.source ?? r.projectSource,
projectClassification: r.departmentName ?? r.projectClassification,
+ salesContractCount: r.salesContractCount,
raw: r
}))
pagination.value.total = response?.total || response?.data?.total || 0
@@ -407,6 +421,18 @@
formDiaRef.value?.openDialog({ operationType: 'edit', row })
}
+function handleArchive(row) {
+ if (!row?.id) return
+ archiveProjectId.value = row.id
+ archiveVisible.value = true
+}
+
+function handleOpenContractDialog(row) {
+ if (!row?.id) return
+ contractProjectId.value = row.id
+ contractVisible.value = true
+}
+
onMounted(() => {
getList()
})
diff --git a/src/views/projectManagement/config/index.vue b/src/views/projectManagement/config/index.vue
new file mode 100644
index 0000000..5f0aaf1
--- /dev/null
+++ b/src/views/projectManagement/config/index.vue
@@ -0,0 +1,109 @@
+<template>
+ <div class="app-container">
+ <el-card class="box-card" shadow="never">
+ <template #header>
+ <div class="card-header">
+ <span>涓氬姟鍙傛暟璁剧疆</span>
+ </div>
+ </template>
+ <el-form
+ ref="configFormRef"
+ :model="form"
+ :rules="rules"
+ label-width="240px"
+ style="max-width: 600px; margin-top: 20px;"
+ >
+ <el-form-item label="鍚堝悓鍘熶欢绾﹀畾鍥炰紶鎻愬墠鎻愰啋澶╂暟" prop="contractReturnReminderDays">
+ <el-input-number
+ v-model="form.contractReturnReminderDays"
+ :min="1"
+ :max="365"
+ :step="1"
+ placeholder="璇疯緭鍏ュぉ鏁�"
+ style="width: 200px"
+ />
+ <span style="margin-left: 10px; color: #999;">澶�</span>
+ </el-form-item>
+
+ <el-form-item>
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmit">淇濆瓨璁剧疆</el-button>
+ <el-button @click="handleReset">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </el-card>
+ </div>
+</template>
+
+<script setup name="ProjectConfig">
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import { getConfig, updateConfig } from '@/api/projectManagement/config'
+
+const configFormRef = ref(null)
+const submitLoading = ref(false)
+const originalData = ref({})
+
+const form = reactive({
+ id: undefined,
+ contractReturnReminderDays: 3
+})
+
+const rules = {
+ contractReturnReminderDays: [
+ { required: true, message: '鎻愬墠鎻愰啋澶╂暟涓嶈兘涓虹┖', trigger: 'blur' }
+ ]
+}
+
+const loadData = async () => {
+ try {
+ const res = await getConfig()
+ if (res.code === 200 && res.data) {
+ Object.assign(form, res.data)
+ originalData.value = JSON.parse(JSON.stringify(res.data))
+ }
+ } catch (error) {
+ console.error('鑾峰彇閰嶇疆澶辫触:', error)
+ }
+}
+
+const handleSubmit = async () => {
+ if (!configFormRef.value) return
+ await configFormRef.value.validate(async (valid) => {
+ if (valid) {
+ submitLoading.value = true
+ try {
+ const res = await updateConfig(form)
+ if (res.code === 200) {
+ ElMessage.success('閰嶇疆鏇存柊鎴愬姛')
+ loadData()
+ }
+ } catch (error) {
+ console.error('鏇存柊閰嶇疆澶辫触:', error)
+ } finally {
+ submitLoading.value = false
+ }
+ }
+ })
+}
+
+const handleReset = () => {
+ if (originalData.value.id) {
+ Object.assign(form, originalData.value)
+ } else {
+ form.contractReturnReminderDays = 3
+ }
+}
+
+onMounted(() => {
+ loadData()
+})
+</script>
+
+<style scoped>
+.app-container {
+ padding: 20px;
+}
+.box-card {
+ min-height: calc(100vh - 124px);
+}
+</style>
diff --git a/src/views/qualityManagement/finalInspection/components/formDia.vue b/src/views/qualityManagement/finalInspection/components/formDia.vue
index 94801ff..b2b271f 100644
--- a/src/views/qualityManagement/finalInspection/components/formDia.vue
+++ b/src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -169,13 +169,57 @@
</el-col>
</el-row>
</el-form>
+ <el-row class="mb8" v-if="!isViewMode">
+ <el-button type="primary" plain icon="Plus" @click="handleAddRow">鏂板涓�琛�</el-button>
+ </el-row>
<PIMTable rowKey="id"
:column="tableColumn"
:tableData="tableData"
:tableLoading="tableLoading"
height="400">
+ <template #parameterItemSlot="{ row }">
+ <span v-if="isViewMode">{{ row.parameterItem }}</span>
+ <el-input v-else v-model="row.parameterItem" placeholder="璇疯緭鍏�" />
+ </template>
+ <template #parameterTypeSlot="{ row }">
+ <span v-if="isViewMode">{{ row.parameterType === 1 ? '鏁板瓧' : row.parameterType === 0 ? '鏂囧瓧' : '' }}</span>
+ <el-select v-else v-model="row.parameterType" @change="handleTypeChange(row)">
+ <el-option label="鏂囧瓧" :value="0" />
+ <el-option label="鏁板瓧" :value="1" />
+ </el-select>
+ </template>
+ <template #maxValueSlot="{ row }">
+ <span v-if="isViewMode">{{ row.maxValue }}</span>
+ <el-input-number v-else v-model="row.maxValue" :controls="false" :disabled="row.parameterType !== 1" style="width: 100%" />
+ </template>
+ <template #minValueSlot="{ row }">
+ <span v-if="isViewMode">{{ row.minValue }}</span>
+ <el-input-number v-else v-model="row.minValue" :controls="false" :disabled="row.parameterType !== 1" style="width: 100%" />
+ </template>
+ <template #unitSlot="{ row }">
+ <span v-if="isViewMode">{{ row.unit }}</span>
+ <el-input v-else v-model="row.unit" />
+ </template>
+ <template #standardValueSlot="{ row }">
+ <span v-if="isViewMode">{{ row.standardValue }}</span>
+ <el-input v-else v-model="row.standardValue" :disabled="row.parameterType !== 0" />
+ </template>
+ <template #controlValueSlot="{ row }">
+ <span v-if="isViewMode">{{ row.controlValue }}</span>
+ <el-input v-else v-model="row.controlValue" />
+ </template>
+ <template #operationSlot="{ row }">
+ <el-button v-if="!isViewMode" type="danger" link @click="removeRow(row)">鍒犻櫎</el-button>
+ </template>
<template #slot="{ row }">
- <el-input v-model="row.testValue"
+ <el-input-number v-if="row.parameterType === 1"
+ v-model="row.testValue"
+ :controls="false"
+ clearable
+ :disabled="isViewMode"
+ style="width: 100%" />
+ <el-input v-else
+ v-model="row.testValue"
clearable
:disabled="isViewMode" />
</template>
@@ -203,6 +247,7 @@
computed,
getCurrentInstance,
nextTick,
+ watch
} from "vue";
import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
@@ -267,36 +312,121 @@
});
const supplierList = ref([]);
const productOptions = ref([]);
- const tableColumn = ref([
- {
- label: "鎸囨爣",
- prop: "parameterItem",
- },
- {
- label: "鍗曚綅",
- prop: "unit",
- },
- {
- label: "鏍囧噯鍊�",
- prop: "standardValue",
- },
- {
- label: "鍐呮帶鍊�",
- prop: "controlValue",
- },
- {
- label: "妫�楠屽��",
- prop: "testValue",
- dataType: "slot",
- slot: "slot",
- },
- ]);
+ const tableColumn = computed(() => {
+ const cols = [
+ {
+ label: "鎸囨爣",
+ prop: "parameterItem",
+ dataType: "slot",
+ slot: "parameterItemSlot"
+ },
+ {
+ label: "鍙傛暟绫诲瀷",
+ prop: "parameterType",
+ dataType: "slot",
+ slot: "parameterTypeSlot",
+ width: "120"
+ },
+ {
+ label: "鏈�澶у��",
+ prop: "maxValue",
+ dataType: "slot",
+ slot: "maxValueSlot",
+ width: "100"
+ },
+ {
+ label: "鏈�灏忓��",
+ prop: "minValue",
+ dataType: "slot",
+ slot: "minValueSlot",
+ width: "100"
+ },
+ {
+ label: "鍗曚綅",
+ prop: "unit",
+ dataType: "slot",
+ slot: "unitSlot",
+ width: "100"
+ },
+ {
+ label: "鏍囧噯鍊�",
+ prop: "standardValue",
+ dataType: "slot",
+ slot: "standardValueSlot"
+ },
+ {
+ label: "鍐呮帶鍊�",
+ prop: "controlValue",
+ dataType: "slot",
+ slot: "controlValueSlot"
+ },
+ {
+ label: "妫�楠屽��",
+ prop: "testValue",
+ dataType: "slot",
+ slot: "slot",
+ }
+ ];
+ if (!isViewMode.value) {
+ cols.push({
+ label: "鎿嶄綔",
+ prop: "operation",
+ dataType: "slot",
+ slot: "operationSlot",
+ width: "80",
+ fixed: "right"
+ });
+ }
+ return cols;
+ });
const tableData = ref([]);
const tableLoading = ref(false);
const userList = ref([]);
const currentProductId = ref(0);
const testStandardOptions = ref([]); // 鎸囨爣閫夋嫨涓嬫媺妗嗘暟鎹�
const modelOptions = ref([]);
+
+ watch(() => tableData.value, (newVal) => {
+ if (isViewMode.value) return;
+ if (!newVal || newVal.length === 0) return;
+
+ let passCount = 0;
+ let failCount = 0;
+
+ newVal.forEach(item => {
+ let isPass = false;
+ const val = item.testValue;
+
+ if (val === null || val === undefined || val === '') {
+ isPass = false;
+ } else if (item.parameterType === 1) {
+ const numVal = Number(val);
+ const min = Number(item.minValue);
+ const max = Number(item.maxValue);
+ if (!isNaN(numVal) && numVal >= min && numVal <= max) {
+ isPass = true;
+ }
+ } else {
+ if (String(val) === String(item.standardValue)) {
+ isPass = true;
+ }
+ }
+
+ if (isPass) {
+ passCount++;
+ } else {
+ failCount++;
+ }
+ });
+
+ if (failCount === 0 && passCount > 0) {
+ form.value.checkResult = "鍚堟牸";
+ } else if (passCount === 0 && failCount > 0) {
+ form.value.checkResult = "涓嶅悎鏍�";
+ } else {
+ form.value.checkResult = "閮ㄥ垎鍚堟牸";
+ }
+ }, { deep: true });
// 鎵撳紑寮规
const openDialog = async (type, row) => {
@@ -533,6 +663,10 @@
...item,
id: null,
}));
+
+ if (tableData.value.some(item => item.parameterType === null || item.parameterType === undefined)) {
+ proxy.$modal.msgWarning("鍙傛暟绫诲瀷鏈厤缃紝璇烽厤缃寚鏍囩淮鎶ゆā鏉�");
+ }
})
.catch(error => {
console.error("鑾峰彇鏍囧噯鍙傛暟澶辫触:", error);
@@ -547,6 +681,36 @@
tableData.value = res.data;
});
};
+
+ const handleAddRow = () => {
+ tableData.value.push({
+ parameterItem: '',
+ parameterType: 0,
+ maxValue: undefined,
+ minValue: undefined,
+ unit: '',
+ standardValue: '',
+ controlValue: '',
+ testValue: ''
+ });
+ };
+
+ const removeRow = (row) => {
+ const index = tableData.value.indexOf(row);
+ if (index !== -1) {
+ tableData.value.splice(index, 1);
+ }
+ };
+
+ const handleTypeChange = (row) => {
+ if (row.parameterType === 0) {
+ row.maxValue = undefined;
+ row.minValue = undefined;
+ } else {
+ row.standardValue = '';
+ }
+ row.testValue = '';
+ };
// 鍏抽棴寮规
const closeDia = () => {
proxy.resetForm("formRef");
diff --git a/src/views/qualityManagement/finalInspection/index.vue b/src/views/qualityManagement/finalInspection/index.vue
index 4a59fa4..6c60f86 100644
--- a/src/views/qualityManagement/finalInspection/index.vue
+++ b/src/views/qualityManagement/finalInspection/index.vue
@@ -128,6 +128,7 @@
import { ElMessageBox } from "element-plus";
import {
downloadQualityInspect,
+ downloadQualityInspectPdf,
qualityInspectDel,
qualityInspectListPage,
qualityInspectUpdate,
@@ -315,9 +316,17 @@
},
{
name: "涓嬭浇",
- type: "text",
- clickFun: row => {
- downLoadFile(row);
+ type: "dropdown",
+ dropdownItems: [
+ { label: "Word鏍煎紡", command: "word" },
+ { label: "PDF鏍煎紡", command: "pdf" }
+ ],
+ commandFun: (command, row) => {
+ if (command === 'word') {
+ downLoadFile(row);
+ } else if (command === 'pdf') {
+ downLoadPdfFile(row);
+ }
},
},
],
@@ -500,7 +509,23 @@
const link = document.createElement("a");
link.href = downloadUrl;
- link.download = "鍘熸潗鏂欐楠屾姤鍛�.docx";
+ link.download = "鍑哄巶妫�楠屾姤鍛�.docx";
+ document.body.appendChild(link);
+ link.click();
+
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(downloadUrl);
+ });
+ };
+
+ const downLoadPdfFile = row => {
+ downloadQualityInspectPdf({ id: row.id }).then(blobData => {
+ const blob = new Blob([blobData], { type: "application/pdf" });
+ const downloadUrl = window.URL.createObjectURL(blob);
+
+ const link = document.createElement("a");
+ link.href = downloadUrl;
+ link.download = "鍑哄巶妫�楠屾姤鍛�.pdf";
document.body.appendChild(link);
link.click();
diff --git a/src/views/qualityManagement/metricMaintenance/ParamFormDialog.vue b/src/views/qualityManagement/metricMaintenance/ParamFormDialog.vue
index 4c958a0..6aed3dd 100644
--- a/src/views/qualityManagement/metricMaintenance/ParamFormDialog.vue
+++ b/src/views/qualityManagement/metricMaintenance/ParamFormDialog.vue
@@ -20,9 +20,23 @@
<el-form-item label="鍗曚綅" prop="unit">
<el-input v-model="form.unit" placeholder="璇疯緭鍏ュ崟浣�" />
</el-form-item>
- <el-form-item label="鏍囧噯鍊�" prop="standardValue">
+ <el-form-item label="鍙傛暟绫诲瀷" prop="parameterType">
+ <el-radio-group v-model="form.parameterType">
+ <el-radio :label="0">鏂囧瓧</el-radio>
+ <el-radio :label="1">鏁板瓧</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="鏍囧噯鍊�" prop="standardValue" v-if="form.parameterType === 0">
<el-input v-model="form.standardValue" placeholder="璇疯緭鍏ユ爣鍑嗗��" />
</el-form-item>
+ <template v-if="form.parameterType === 1">
+ <el-form-item label="鏈�澶у��" prop="maxValue">
+ <el-input-number v-model="form.maxValue" :controls="false" placeholder="璇疯緭鍏ユ渶澶у��" style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="鏈�灏忓��" prop="minValue">
+ <el-input-number v-model="form.minValue" :controls="false" placeholder="璇疯緭鍏ユ渶灏忓��" style="width: 100%" />
+ </el-form-item>
+ </template>
<el-form-item label="鍐呮帶鍊�" prop="controlValue">
<el-input v-model="form.controlValue" placeholder="璇疯緭鍏ュ唴鎺у��" />
</el-form-item>
@@ -52,11 +66,45 @@
const formRef = ref(null)
-const rules = {
- parameterItem: [{ required: true, message: '璇疯緭鍏ュ弬鏁伴」', trigger: 'blur' }],
- unit: [{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: 'blur' }]
+const validateMaxValue = (rule, value, callback) => {
+ if (value === undefined || value === null || value === '') {
+ callback(new Error('璇疯緭鍏ユ渶澶у��'))
+ } else if (props.form.minValue !== undefined && props.form.minValue !== null && props.form.minValue !== '' && Number(value) < Number(props.form.minValue)) {
+ callback(new Error('鏈�澶у�间笉鑳藉皬浜庢渶灏忓��'))
+ } else {
+ if (props.form.minValue !== undefined && props.form.minValue !== null && props.form.minValue !== '') {
+ formRef.value?.clearValidate('minValue')
+ }
+ callback()
+ }
}
+const validateMinValue = (rule, value, callback) => {
+ if (value === undefined || value === null || value === '') {
+ callback(new Error('璇疯緭鍏ユ渶灏忓��'))
+ } else if (props.form.maxValue !== undefined && props.form.maxValue !== null && props.form.maxValue !== '' && Number(value) > Number(props.form.maxValue)) {
+ callback(new Error('鏈�灏忓�间笉鑳藉ぇ浜庢渶澶у��'))
+ } else {
+ if (props.form.maxValue !== undefined && props.form.maxValue !== null && props.form.maxValue !== '') {
+ formRef.value?.clearValidate('maxValue')
+ }
+ callback()
+ }
+}
+
+const rules = computed(() => {
+ const baseRules = {
+ parameterItem: [{ required: true, message: '璇疯緭鍏ュ弬鏁伴」', trigger: 'blur' }],
+ unit: [{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: 'blur' }],
+ parameterType: [{ required: true, message: '璇烽�夋嫨鍙傛暟绫诲瀷', trigger: 'change' }]
+ };
+ if (props.form.parameterType === 1) {
+ baseRules.maxValue = [{ required: true, validator: validateMaxValue, trigger: 'blur' }];
+ baseRules.minValue = [{ required: true, validator: validateMinValue, trigger: 'blur' }];
+ }
+ return baseRules;
+})
+
const computedTitle = computed(() => (props.operationType === 'edit' ? '缂栬緫鏍囧噯鍙傛暟' : '鏂板鏍囧噯鍙傛暟'))
const handleConfirm = () => {
diff --git a/src/views/qualityManagement/metricMaintenance/index.vue b/src/views/qualityManagement/metricMaintenance/index.vue
index deb3e5f..243e63f 100644
--- a/src/views/qualityManagement/metricMaintenance/index.vue
+++ b/src/views/qualityManagement/metricMaintenance/index.vue
@@ -122,7 +122,14 @@
<el-table-column type="index" label="搴忓彿" width="60" align="center" />
<el-table-column prop="parameterItem" label="鍙傛暟椤�" min-width="120" />
<el-table-column prop="unit" label="鍗曚綅" width="80" />
+ <el-table-column prop="parameterType" label="鍙傛暟绫诲瀷" width="100">
+ <template #default="{ row }">
+ {{ row.parameterType === 1 ? '鏁板瓧' : (row.parameterType === 0 ? '鏂囧瓧' : '') }}
+ </template>
+ </el-table-column>
<el-table-column prop="standardValue" label="鏍囧噯鍊�" min-width="120" />
+ <el-table-column prop="maxValue" label="鏈�澶у��" min-width="100" />
+ <el-table-column prop="minValue" label="鏈�灏忓��" min-width="100" />
<el-table-column prop="controlValue" label="鍐呮帶鍊�" min-width="120" />
<el-table-column prop="defaultValue" label="榛樿鍊�" min-width="120" />
<el-table-column label="鎿嶄綔" width="140" fixed="right" align="center">
@@ -266,7 +273,10 @@
id: undefined,
parameterItem: '',
unit: '',
+ parameterType: 0,
standardValue: '',
+ maxValue: null,
+ minValue: null,
controlValue: '',
defaultValue: ''
})
@@ -514,7 +524,10 @@
id: undefined,
parameterItem: '',
unit: '',
+ parameterType: 0,
standardValue: '',
+ maxValue: null,
+ minValue: null,
controlValue: '',
defaultValue: ''
})
diff --git a/src/views/qualityManagement/processInspection/components/formDia.vue b/src/views/qualityManagement/processInspection/components/formDia.vue
index 370b5f6..c82422f 100644
--- a/src/views/qualityManagement/processInspection/components/formDia.vue
+++ b/src/views/qualityManagement/processInspection/components/formDia.vue
@@ -292,6 +292,19 @@
prop: "parameterItem",
},
{
+ label: "鍙傛暟绫诲瀷",
+ prop: "parameterType",
+ formatter: (row) => (row.parameterType === 1 ? "鏁板瓧" : row.parameterType === 0 ? "鏂囧瓧" : ""),
+ },
+ {
+ label: "鏈�澶у��",
+ prop: "maxValue",
+ },
+ {
+ label: "鏈�灏忓��",
+ prop: "minValue",
+ },
+ {
label: "鍗曚綅",
prop: "unit",
},
@@ -581,6 +594,10 @@
...item,
id: null,
}));
+
+ if (tableData.value.some(item => item.parameterType === null || item.parameterType === undefined)) {
+ proxy.$modal.msgWarning("鍙傛暟绫诲瀷鏈厤缃紝璇烽厤缃寚鏍囩淮鎶ゆā鏉�");
+ }
})
.catch(error => {
console.error("鑾峰彇鏍囧噯鍙傛暟澶辫触:", error);
diff --git a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
index 595bf80..aeab1ef 100644
--- a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -191,6 +191,9 @@
:tableData="tableData"
:tableLoading="tableLoading"
height="400">
+ <template #typeSlot="{ row }">
+ {{ row.parameterType === 1 ? '鏁板瓧' : (row.parameterType === 0 ? '鏂囧瓧' : '') }}
+ </template>
<template #slot="{ row }">
<el-input v-model="row.testValue"
clearable
@@ -290,8 +293,22 @@
prop: "unit",
},
{
+ label: "鍙傛暟绫诲瀷",
+ prop: "parameterType",
+ dataType: "slot",
+ slot: "typeSlot",
+ },
+ {
label: "鏍囧噯鍊�",
prop: "standardValue",
+ },
+ {
+ label: "鏈�澶у��",
+ prop: "maxValue",
+ },
+ {
+ label: "鏈�灏忓��",
+ prop: "minValue",
},
{
label: "鍐呮帶鍊�",
@@ -546,6 +563,10 @@
...item,
id: null,
}));
+
+ if (tableData.value.some(item => item.parameterType === null || item.parameterType === undefined)) {
+ proxy.$modal.msgWarning("鍙傛暟绫诲瀷鏈厤缃紝璇烽厤缃寚鏍囩淮鎶ゆā鏉�");
+ }
})
.catch(error => {
console.error("鑾峰彇鏍囧噯鍙傛暟澶辫触:", error);
diff --git a/src/views/salesManagement/biddingManagement/index.vue b/src/views/salesManagement/biddingManagement/index.vue
index 310d0c5..6db238b 100644
--- a/src/views/salesManagement/biddingManagement/index.vue
+++ b/src/views/salesManagement/biddingManagement/index.vue
@@ -278,6 +278,7 @@
const formRef = ref();
const form = reactive({
+ id: undefined,
projectId: "",
projectName: "",
bidDocName: "",
@@ -418,6 +419,7 @@
Object.keys(form).forEach((key) => {
form[key] = "";
});
+ form.id = undefined;
form.bidDocFee = undefined;
form.bidDeposit = undefined;
};
@@ -458,12 +460,12 @@
dialogVisible.value = false;
};
-// 涓爣鍚庡彂璧烽攢鍞悎鍚屽綍鍏ワ細璺宠浆鍚堝悓绠$悊椤碉紝鎼哄甫椤圭洰涓庢嫑鏍囧崟浣嶄俊鎭�
+// 涓爣鍚庡彂璧烽攢鍞彴璐﹀綍鍏ワ細璺宠浆閿�鍞彴璐︼紝鎼哄甫椤圭洰淇℃伅
const handleStartContract = (row) => {
- proxy.$tab.openPage("鍙戣捣閿�鍞悎鍚屽綍鍏�", "/salesManagement/contractManagement", {
+ proxy.$tab.openPage("閿�鍞彴璐�", "/salesManagement/salesLedger", {
+ action: "add",
projectId: row.projectId,
projectName: row.projectName,
- signingUnit: row.tenderUnit,
});
};
diff --git a/src/views/salesManagement/deliveryLedger/index.vue b/src/views/salesManagement/deliveryLedger/index.vue
index 33fa45f..af41f00 100644
--- a/src/views/salesManagement/deliveryLedger/index.vue
+++ b/src/views/salesManagement/deliveryLedger/index.vue
@@ -32,6 +32,36 @@
@change="handleQuery"
/>
</el-form-item>
+ <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectId">
+ <el-select
+ v-model="searchForm.projectId"
+ placeholder="璇烽�夋嫨椤圭洰"
+ clearable
+ filterable
+ style="width: 200px"
+ @change="handleQuery"
+ >
+ <el-option
+ v-for="item in projectOptions"
+ :key="item.id"
+ :label="item.no + ' - ' + item.title"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍙戣揣鏃ユ湡锛�" prop="shippingDateRange">
+ <el-date-picker
+ v-model="searchForm.shippingDateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ style="width: 240px"
+ @change="handleQuery"
+ clearable
+ />
+ </el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"> 鎼滅储</el-button>
</el-form-item>
@@ -64,6 +94,12 @@
<el-table-column
label="鍙戣揣璁㈠崟鍙�"
prop="shippingNo"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="椤圭洰鍚嶇О"
+ prop="projectName"
+ min-width="150"
show-overflow-tooltip
/>
<el-table-column
@@ -124,14 +160,22 @@
show-overflow-tooltip
width="130"
/>
+ <el-table-column label="閫�璐у崟" align="center" width="140">
+ <template #default="scope">
+ <el-button v-if="scope.row.returnNoList && scope.row.returnNoList.length > 0" link type="primary" @click="openReturnNosDialog(scope.row)">
+ 鏌ョ湅 {{ scope.row.returnNoList.length }} 涓��璐у崟
+ </el-button>
+ <span v-else>--</span>
+ </template>
+ </el-table-column>
<el-table-column fixed="right" label="鎿嶄綔" width="220" align="center">
<template #default="scope">
- <!-- <el-button-->
- <!-- link-->
- <!-- type="primary"-->
- <!-- :disabled="!isApproved(scope.row.status)"-->
- <!-- @click="openForm('edit', scope.row)">鍙戣揣-->
- <!-- </el-button>-->
+ <el-button
+ link
+ type="primary"
+ :disabled="scope.row.status === '瀹℃牳閫氳繃' || scope.row.status === '宸插彂璐�'"
+ @click="openForm('edit', scope.row)">琛ュ厖鍙戣揣淇℃伅
+ </el-button>
<el-button
link
type="primary"
@@ -139,13 +183,13 @@
@click="openDetail(scope.row)"
>璇︽儏
</el-button>
- <!-- <el-button-->
- <!-- link-->
- <!-- type="danger"-->
- <!-- :disabled="isApproving(scope.row.status)"-->
- <!-- @click="handleDeleteSingle(scope.row)"-->
- <!-- >鍒犻櫎-->
- <!-- </el-button>-->
+ <el-button
+ link
+ type="danger"
+ :disabled="scope.row.status === '宸插彂璐�' || scope.row.status === '瀹℃牳閫氳繃'"
+ @click="handleDeleteSingle(scope.row)"
+ >鍒犻櫎
+ </el-button>
</template>
</el-table-column>
</el-table>
@@ -239,6 +283,56 @@
</el-form-item>
</el-col>
</el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="鍙戣揣鍗曟嵁锛�" prop="shippingDocument">
+ <el-input
+ v-model="form.shippingDocument"
+ placeholder="璇疯緭鍏ュ彂璐у崟鎹�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="鏄惁瀵硅处锛�" prop="isReconciled">
+ <el-switch v-model="form.isReconciled" :active-value="1" :inactive-value="0" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鏄惁缁撶畻锛�" prop="isSettled">
+ <el-switch v-model="form.isSettled" :active-value="1" :inactive-value="0" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鏄惁宸插紑绁細" prop="isInvoiced">
+ <el-switch v-model="form.isInvoiced" :active-value="1" :inactive-value="0" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12" v-if="form.isReconciled === 1">
+ <el-form-item label="瀵硅处鍗曞綊妗f儏鍐碉細" prop="statementArchivingStatus">
+ <el-input
+ type="textarea"
+ v-model="form.statementArchivingStatus"
+ placeholder="璇疯緭鍏ュ璐﹀崟褰掓。鎯呭喌"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12" v-if="form.isSettled === 1">
+ <el-form-item label="缁撶畻鍗曞綊妗f儏鍐碉細" prop="settlementArchivingStatus">
+ <el-input
+ type="textarea"
+ v-model="form.settlementArchivingStatus"
+ placeholder="璇疯緭鍏ョ粨绠楀崟褰掓。鎯呭喌"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
@@ -293,6 +387,24 @@
<el-descriptions-item label="鍑哄簱鍗曞彿" :span="2">{{
detailRow.outboundBatches || "--"
}}</el-descriptions-item>
+ <el-descriptions-item label="鍙戣揣鍗曟嵁">{{
+ detailRow.shippingDocument || "--"
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鏄惁瀵硅处">{{
+ detailRow.isReconciled === 1 ? '鏄�' : '鍚�'
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鏄惁缁撶畻">{{
+ detailRow.isSettled === 1 ? '鏄�' : '鍚�'
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鏄惁宸插紑绁�">{{
+ detailRow.isInvoiced === 1 ? '鏄�' : '鍚�'
+ }}</el-descriptions-item>
+ <el-descriptions-item label="瀵硅处鍗曞綊妗f儏鍐�" :span="2">{{
+ detailRow.statementArchivingStatus || "--"
+ }}</el-descriptions-item>
+ <el-descriptions-item label="缁撶畻鍗曞綊妗f儏鍐�" :span="2">{{
+ detailRow.settlementArchivingStatus || "--"
+ }}</el-descriptions-item>
</el-descriptions>
<el-table
:data="getDeliveryProductInfoList()"
@@ -334,6 +446,19 @@
</div>
</template>
</el-dialog>
+
+ <!-- 閫�璐у崟鍙峰脊妗� -->
+ <el-dialog v-model="returnNosDialogVisible" title="閫�璐у崟鍙峰垪琛�" width="400px" @close="closeReturnNosDialog">
+ <el-table :data="currentReturnNos" border size="small">
+ <el-table-column label="搴忓彿" type="index" width="60" align="center" />
+ <el-table-column label="閫�璐у崟鍙�" prop="returnNo" align="center" />
+ </el-table>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="closeReturnNosDialog">鍏抽棴</el-button>
+ </div>
+ </template>
+ </el-dialog>
</div>
</template>
@@ -349,6 +474,7 @@
getDeliveryDetail,
} from "@/api/salesManagement/deliveryLedger.js";
import { delLedgerFile } from "@/api/salesManagement/salesLedger.js";
+import { listProject } from "@/api/projectManagement/project.js";
import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
import ImagePreview from "@/components/AttachmentPreview/image/index.vue";
@@ -357,6 +483,7 @@
const selectedRows = ref([]);
const tableLoading = ref(false);
const salesOrderOptions = ref([]);
+const projectOptions = ref([]);
const page = reactive({
current: 1,
size: 100,
@@ -368,14 +495,30 @@
const detailRow = ref(null);
const detailProductList = ref([]);
+// 閫�璐у崟鍙峰脊妗�
+const returnNosDialogVisible = ref(false);
+const currentReturnNos = ref([]);
+
+const openReturnNosDialog = (row) => {
+ currentReturnNos.value = (row.returnNoList || []).map(no => ({ returnNo: no }));
+ returnNosDialogVisible.value = true;
+};
+
+const closeReturnNosDialog = () => {
+ returnNosDialogVisible.value = false;
+ currentReturnNos.value = [];
+};
+
// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
searchForm: {
- salesContractNo: "", // 閿�鍞鍗曞彿
- shippingCarNumber: "", // 杞︾墝鍙�
- expressNumber: "", // 蹇�掑崟鍙�
+ salesContractNo: undefined,
+ shippingCarNumber: undefined,
+ expressNumber: undefined,
+ projectId: undefined,
+ shippingDateRange: []
},
form: {
id: null,
@@ -433,7 +576,13 @@
const getList = () => {
tableLoading.value = true;
- deliveryLedgerListPage({ ...searchForm.value, ...page })
+ const params = { ...searchForm.value };
+ if (params.shippingDateRange && params.shippingDateRange.length === 2) {
+ params.beginShippingDate = params.shippingDateRange[0];
+ params.endShippingDate = params.shippingDateRange[1];
+ }
+ delete params.shippingDateRange;
+ deliveryLedgerListPage({ ...params, ...page })
.then((res) => {
tableLoading.value = false;
tableData.value = res.data.records || [];
@@ -461,9 +610,8 @@
// 鎵撳紑寮规
const openForm = async (type, row) => {
- // 鍙戣揣锛氫粎鈥滃鏍搁�氳繃鈥濆厑璁哥紪杈�
- if (type === "edit" && row && !isApproved(row.status)) {
- proxy.$modal.msgWarning("鍙湁瀹℃牳閫氳繃鐨勬暟鎹墠鍙互鍙戣揣");
+ if (type === "edit" && row && (row.status === "瀹℃牳閫氳繃" || row.status === "宸插彂璐�")) {
+ proxy.$modal.msgWarning("璇ョ姸鎬佺殑鏁版嵁涓嶅彲浠ョ紪杈戝彂璐т俊鎭�");
return;
}
@@ -479,6 +627,12 @@
shippingCarNumber: row.shippingCarNumber ?? "",
expressCompany: row.expressCompany ?? "",
expressNumber: row.expressNumber ?? "",
+ shippingDocument: row.shippingDocument ?? "",
+ isReconciled: row.isReconciled ?? 0,
+ statementArchivingStatus: row.statementArchivingStatus ?? "",
+ isSettled: row.isSettled ?? 0,
+ settlementArchivingStatus: row.settlementArchivingStatus ?? "",
+ isInvoiced: row.isInvoiced ?? 0,
};
deliveryFileList.value = row.storageBlobVOs || [];
}
@@ -616,7 +770,13 @@
type: "warning",
})
.then(() => {
- proxy.download("/shippingInfo/export", {}, "鍙戣揣鍙拌处.xlsx");
+ const params = { ...searchForm.value };
+ if (params.shippingDateRange && params.shippingDateRange.length === 2) {
+ params.beginShippingDate = params.shippingDateRange[0];
+ params.endShippingDate = params.shippingDateRange[1];
+ }
+ delete params.shippingDateRange;
+ proxy.download("/shippingInfo/export", params, "鍙戣揣鍙拌处.xlsx");
})
.catch(() => {
proxy.$modal.msg("宸插彇娑�");
@@ -866,6 +1026,7 @@
};
onMounted(() => {
+ listProject({}).then(res => projectOptions.value = res.rows || res.data.records || res.data);
getList();
});
</script>
diff --git a/src/views/salesManagement/lawsuitManagement/index.vue b/src/views/salesManagement/lawsuitManagement/index.vue
index 1cd39f2..2b52310 100644
--- a/src/views/salesManagement/lawsuitManagement/index.vue
+++ b/src/views/salesManagement/lawsuitManagement/index.vue
@@ -253,6 +253,7 @@
const fileList = ref([]);
const form = reactive({
+ id: undefined,
caseNo: "",
projectId: "",
projectName: "",
@@ -299,9 +300,13 @@
const initProjects = async () => {
try {
- const res = await listProject({ pageSize: 1000 });
- const rows = res?.rows || res?.data?.rows || [];
- projectOptions.value = rows.map((p) => ({ label: p.projectName, value: p.id }));
+ const res = await listProject({ pageSize: 1000, current: 1, size: 1000 });
+ const rows = res?.records || res?.rows || res?.data?.records || res?.data?.rows || res?.data || [];
+ projectOptions.value = rows.map((p) => ({
+ label: (p.no ? p.no + ' - ' : '') + (p.title || ''),
+ value: p.id,
+ title: p.title
+ }));
} catch (e) {
console.error("鍔犺浇椤圭洰鍒楄〃澶辫触", e);
}
@@ -310,7 +315,7 @@
const initContracts = async () => {
try {
const res = await contractListNoPage({});
- const list = Array.isArray(res) ? res : res?.data ?? res?.records ?? [];
+ const list = Array.isArray(res) ? res : (res?.data?.records || res?.data || res?.records || []);
contractOptions.value = list.map((item) => ({
label: item.contractNo + (item.signingUnit ? `锛�${item.signingUnit}锛塦 : ""),
value: item.id,
@@ -323,7 +328,7 @@
const handleProjectChange = (val) => {
const project = projectOptions.value.find((item) => item.value === val);
- form.projectName = project ? project.label : "";
+ form.projectName = project ? (project.title || project.label) : "";
};
const handleContractChange = (val) => {
@@ -390,6 +395,7 @@
form[key] = "";
});
form.claimAmount = undefined;
+ form.id = undefined;
};
const openForm = async (type, row) => {
diff --git a/src/views/salesManagement/receiptPaymentLedger/index.vue b/src/views/salesManagement/receiptPaymentLedger/index.vue
index c5e3415..72fc6a8 100644
--- a/src/views/salesManagement/receiptPaymentLedger/index.vue
+++ b/src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -1,272 +1,610 @@
<template>
<div class="app-container">
- <div class="search_form"
- style="margin-bottom: 20px;">
- <div>
- <span class="search_title">瀹㈡埛鍚嶇О锛�</span>
- <el-input v-model="searchForm.searchText"
- style="width: 240px"
- placeholder="杈撳叆瀹㈡埛鍚嶇О鎼滅储"
- @change="handleQuery"
- clearable
- prefix-icon="Search" />
- <el-button type="primary"
- @click="handleQuery"
- style="margin-left: 10px">鎼滅储</el-button>
+ <div class="search_form">
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="椤圭洰鍚嶇О锛�">
+ <el-input
+ v-model="searchForm.projectName"
+ placeholder="璇疯緭鍏ラ」鐩悕绉�"
+ clearable
+ prefix-icon="Search"
+ style="width: 200px"
+ @change="handleSearch"
+ />
+ </el-form-item>
+ <el-form-item label="瀹㈡埛鍚嶇О锛�">
+ <el-input
+ v-model="searchForm.customerName"
+ placeholder="璇疯緭鍏ュ鎴峰悕绉�"
+ clearable
+ prefix-icon="Search"
+ style="width: 200px"
+ @change="handleSearch"
+ />
+ </el-form-item>
+ <el-form-item label="鍚堝悓鍚嶇О锛�">
+ <el-input
+ v-model="searchForm.contractName"
+ placeholder="璇疯緭鍏ュ悎鍚屽悕绉�"
+ clearable
+ prefix-icon="Search"
+ style="width: 200px"
+ @change="handleSearch"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="handleReset">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ </div>
+ <div class="table_list">
+ <div class="app-container layout-fullscreen">
+ <!-- Search Form -->
+ <div class="summary-strip">
+ <div class="summary-stats">
+ <span>褰撳墠椤圭洰锛�<b>{{ summaryData.totalProjects || 0 }}</b> 涓�</span>
+ <span>鍏宠仈瀹㈡埛锛�<b>{{ summaryData.totalCustomers || 0 }}</b> 瀹�</span>
+ <span>鍚堝悓鎬绘暟锛�<b>{{ summaryData.totalContracts || 0 }}</b> 浠�</span>
+ <span>绛剧害鎬婚锛�<b>{{ fmtMoney(summaryData.totalAmount || 0) }}</b></span>
+ <span class="warn-summary">鈿� 绛捐瓒�30澶╂湭瀹屾垚鍥炴锛�<span>{{
+ summaryData.totalOverdueCount || 0
+ }}</span> 浠�</span>
</div>
</div>
- <el-row :gutter="20">
- <el-col :span="12">
- <div class="table_list"
- style="width: 100%">
- <el-table :data="tableData"
- border
- v-loading="tableLoading"
- :row-key="(row) => row.customerId"
- show-summary
- :summary-method="summarizeMainTable"
- @row-click="rowClickMethod"
- highlight-current-row
- height="calc(100vh - 18.5em)">
- <el-table-column align="center"
- label="搴忓彿"
- type="index"
- width="60" />
- <el-table-column label="瀹㈡埛鍚嶇О"
- prop="customerName"
- show-overflow-tooltip
- width="200" />
- <el-table-column label="鍚堝悓閲戦(鍏�)"
- prop="contractAmounts"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="鍥炴閲戦(鍏�)"
- prop="receiptPaymentAmount"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="閫�璐ч噾棰�(鍏�)"
- prop="returnAmount"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="搴旀敹閲戦(鍏�)"
- prop="receiptableAmount"
- show-overflow-tooltip
- width="200">
- <template #default="{ row, column }">
- <el-text type="danger">
- {{ formattedNumber(row, column, row.receiptableAmount) }}
- </el-text>
+
+ <!-- Layout Columns -->
+ <div class="layout-columns">
+ <!-- Column 1: Projects -->
+ <div class="column-panel" v-loading="prjLoading">
+ <div class="panel-header">
+ <span>1. 椤圭洰姹囨�� ({{ prjTotal }})</span>
+ </div>
+ <div class="panel-body">
+ <div v-if="projects.length === 0" class="empty-state">鏃犲尮閰嶉」鐩�</div>
+
+ <div v-for="p in projects" :key="p.projectId"
+ class="item-card" :class="{ active: p.projectId === selectedProjectId }"
+ @click="handleSelectProject(p)">
+ <div class="card-title-row">
+ <div class="card-main-title">{{ p.projectName }}</div>
+ </div>
+ <div class="card-code">{{ p.projectCode || '鏆傛棤椤圭洰缂栧彿' }}</div>
+ <div class="meta-row" style="margin-top: 8px;">
+ <span>鎬婚: <strong>{{ fmtMoney(p.contractAmounts) }}</strong></span>
+ <span>寮�绁�: <strong>{{ fmtMoney(p.invoiceAmount) }}</strong></span>
+ </div>
+ <div class="meta-row">
+ <span>鍥炴: <strong>{{ fmtMoney(p.receiptPaymentAmount) }}</strong></span>
+ <span>搴旀敹: <strong>{{ fmtMoney(p.receiptableAmount) }}</strong></span>
+ </div>
+ <div class="meta-row">
+ <span>閫�璐�: <strong>{{ fmtMoney(p.returnAmount) }}</strong></span>
+ <span>杩涘害: <strong :class="{'text-danger': p.overdueCount > 0}">{{
+ calcRate(p.receiptPaymentAmount, p.contractAmounts)
+ }}%</strong></span>
+ </div>
+ <div class="meta-row">
+ <span>瀹㈡埛: <strong>{{ p.customerCount || 0 }} 瀹�</strong></span>
+ <span>鍚堝悓: <strong>{{ p.contractCount || 0 }} 浠�</strong></span>
+ </div>
+ <div v-if="p.overdueCount > 0" style="margin-top: 6px;">
+ <el-tag type="danger" size="small" effect="light">鈿� {{ p.overdueCount }} 浠借秴鏈熷悎鍚�</el-tag>
+ </div>
+ </div>
+ </div>
+ <div class="panel-pagination">
+ <el-pagination background small layout="total, prev, pager, next" :total="prjTotal" :page-size="prjPage.size"
+ v-model:current-page="prjPage.current" @current-change="handlePrjPageChange"/>
+ </div>
+ </div>
+
+ <!-- Column 2: Customers -->
+ <div class="column-panel" v-loading="custLoading">
+ <div class="panel-header">
+ <span>2. 瀹㈡埛姹囨�� ({{ custTotal }})</span>
+ </div>
+ <div class="panel-body">
+ <div v-if="!selectedProjectId" class="empty-state">璇峰厛鍦ㄥ乏渚ч�夋嫨椤圭洰</div>
+ <div v-else-if="customers.length === 0" class="empty-state">璇ラ」鐩笅鏃犲尮閰嶇殑瀹㈡埛淇℃伅</div>
+
+ <div v-for="c in customers" :key="c.customerId"
+ class="item-card" :class="{ active: c.customerId === selectedCustomerId }"
+ @click="handleSelectCustomer(c)">
+ <div class="card-title-row">
+ <div class="card-main-title">{{ c.customerName || '鏈懡鍚嶅鎴�' }}</div>
+ </div>
+ <div class="meta-row" style="margin-top: 8px;">
+ <span>绛剧害: <strong>{{ fmtMoney(c.contractAmounts) }}</strong></span>
+ <span>杩涘害: <strong :class="{'text-danger': c.overdueCount > 0}">{{
+ calcRate(c.receiptPaymentAmount, c.contractAmounts)
+ }}%</strong></span>
+ </div>
+ <div class="meta-row">
+ <span>閫�璐�: <strong>{{ fmtMoney(c.returnAmount) }}</strong></span>
+ <span>搴旀敹: <strong>{{ fmtMoney(c.receiptableAmount) }}</strong></span>
+ </div>
+ <div class="meta-row">
+ <span>宸插洖娆�: <strong>{{ fmtMoney(c.receiptPaymentAmount) }}</strong></span>
+ <span>鍚堝悓鏁�: <strong>{{ c.contractCount || 0 }} 浠�</strong></span>
+ </div>
+ <div style="margin-top: 6px;">
+ <el-tag v-if="c.overdueCount > 0" type="danger" size="small" effect="light">鈿� {{ c.overdueCount }} 浠借秴鏈�</el-tag>
+ <el-tag v-else type="success" size="small" effect="light">姝e父灞ョ害</el-tag>
+ </div>
+ </div>
+ </div>
+ <div class="panel-pagination">
+ <el-pagination background small layout="total, prev, pager, next" :total="custTotal"
+ :page-size="custPage.size" v-model:current-page="custPage.current"
+ @current-change="handleCustPageChange"/>
+ </div>
+ </div>
+
+ <!-- Column 3: Contracts -->
+ <div class="column-panel" v-loading="contractLoading">
+ <div class="panel-header">
+ <span>3. 鍚堝悓鏄庣粏 ({{ contractTotal }})</span>
+ </div>
+ <div class="panel-body" style="padding: 0;">
+ <div v-if="!selectedCustomerId" class="empty-state" style="padding-top: 40px;">璇峰厛鍦ㄤ腑闂存爮閫夋嫨瀹㈡埛</div>
+ <div v-else-if="contracts.length === 0" class="empty-state" style="padding-top: 40px;">
+ 璇ュ鎴蜂笅鏃犲尮閰嶇殑鍚堝悓鏄庣粏
+ </div>
+
+ <el-table v-else :data="contracts" size="small" style="width: 100%; height: 100%;" stripe
+ :row-class-name="tableRowClassName">
+ <el-table-column label="鍚堝悓淇℃伅" min-width="160" show-overflow-tooltip>
+ <template #default="{ row }">
+ <div style="font-weight: 600; color: #0f172a;">{{
+ row.contractName || row.salesContractNo || '鏈懡鍚�'
+ }}
+ </div>
+ <div style="font-size: 11px; color: #64748b;">鍚堝悓缂栧彿锛歿{ row.salesContractNo }}</div>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="閲戦涓庡洖娆�" min-width="150">
+ <template #default="{ row }">
+ <div style="font-size: 12px; color: #64748b;">鎬婚: <span
+ style="color: #0f172a; font-weight: 500;">{{ fmtMoney(row.contractAmount) }}</span></div>
+ <div :class="row.isPaymentTimeout ? 'text-danger' : ''" style="font-size: 12px; font-weight: 500;">
+ 宸插洖: {{ fmtMoney(row.receiptPaymentAmount) }}
+ ({{ calcRate(row.receiptPaymentAmount, row.contractAmount) }}%)
+ </div>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="搴旀敹涓庨��璐�" min-width="130">
+ <template #default="{ row }">
+ <div style="font-size: 12px; color: #64748b;">搴旀敹: <span
+ style="color: #0f172a;">{{ fmtMoney(row.receiptableAmount) }}</span></div>
+ <div style="font-size: 12px; color: #64748b;">閫�璐�: <span
+ style="color: #0f172a;">{{ fmtMoney(row.returnAmount) }}</span></div>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="鐘舵��" width="100" align="center">
+ <template #default="{ row }">
+ <el-tag v-if="row.isPaymentTimeout" type="danger" size="small" effect="dark">瓒呮湡鏈洖</el-tag>
+ <el-tag
+ v-else-if="(row.receiptPaymentAmount || 0) >= (row.contractAmount || 0) && (row.contractAmount || 0) > 0"
+ type="success" size="small" effect="dark">宸茬粨娓�
+ </el-tag>
+ <el-tag v-else type="primary" size="small" effect="plain">灞ヨ涓�</el-tag>
</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>
- </el-col>
- <el-col :span="12">
- <div class="table_list"
- style="width: 100%">
- <el-table :data="receiptRecord"
- border
- :row-key="(row) => row.id"
- show-summary
- :summary-method="summarizeMainTable1"
- height="calc(100vh - 18.5em)">
- <el-table-column align="center"
- label="搴忓彿"
- type="index"
- width="60" />
- <el-table-column label="鍚堝悓绛捐鏃ユ湡"
- prop="executionDate"
- show-overflow-tooltip
- width="110" />
- <el-table-column label="閿�鍞悎鍚屽彿"
- prop="salesContractNo"
- show-overflow-tooltip
- width="200" />
- <el-table-column label="鍚堝悓閲戦(鍏�)"
- prop="contractAmount"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="鍥炴閲戦(鍏�)"
- prop="receiptPaymentAmount"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="閫�璐ч噾棰�(鍏�)"
- prop="returnAmount"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="搴旀敹閲戦(鍏�)"
- prop="receiptableAmount"
- show-overflow-tooltip
- width="200">
- <template #default="{ row, column }">
- <el-text type="danger">
- {{ formattedNumber(row, column, row.receiptableAmount) }}
- </el-text>
- </template>
- </el-table-column>
- </el-table>
- <pagination v-show="recordTotal > 0"
- :total="recordTotal"
- layout="total, sizes, prev, pager, next, jumper"
- :page="recordPage.current"
- :limit="recordPage.size"
- @pagination="recordPaginationChange" />
+ <div class="panel-pagination">
+ <el-pagination background small layout="total, prev, pager, next" :total="contractTotal"
+ :page-size="contractPage.size" v-model:current-page="contractPage.current"
+ @current-change="handleContractPageChange"/>
</div>
- </el-col>
- </el-row>
+ </div>
+
+ </div>
+ </div>
</div>
</template>
<script setup>
- import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
- import {
- customewTransactions,
- customewTransactionsDetails,
- } from "@/api/salesManagement/indicatorStats.js";
- import Pagination from "../../../components/PIMTable/Pagination.vue";
- const { proxy } = getCurrentInstance();
- const tableData = ref([]);
- const receiptRecord = ref([]);
- const tableLoading = ref(false);
- const page = reactive({
- current: 1,
- size: 100,
+import {onMounted, ref, reactive, toRefs} from "vue";
+import {
+ projectTransactions,
+ customewTransactions,
+ customewTransactionsDetails,
+ transactionsSummary
+} from "@/api/salesManagement/indicatorStats.js";
+
+const summaryData = ref({});
+const data = reactive({
+ searchForm: {
+ projectName: "",
+ customerName: "",
+ contractName: "",
+ },
+});
+const {searchForm} = toRefs(data);
+
+// Column 1: Projects
+const projects = ref([]);
+const prjLoading = ref(false);
+const prjTotal = ref(0);
+const prjPage = reactive({current: 1, size: 20});
+const selectedProjectId = ref(null);
+
+// Column 2: Customers
+const customers = ref([]);
+const custLoading = ref(false);
+const custTotal = ref(0);
+const custPage = reactive({current: 1, size: 20});
+const selectedCustomerId = ref(null);
+
+// Column 3: Contracts
+const contracts = ref([]);
+const contractLoading = ref(false);
+const contractTotal = ref(0);
+const contractPage = reactive({current: 1, size: 20});
+
+const getSummary = () => {
+ transactionsSummary().then(res => {
+ summaryData.value = res.data || {};
});
- const recordPage = reactive({
- current: 1,
- size: 100,
+};
+
+const handleSearch = () => {
+ prjPage.current = 1;
+ loadProjects();
+ getSummary();
+};
+
+const handleReset = () => {
+ searchForm.value = {
+ projectName: "",
+ customerName: "",
+ contractName: "",
+ };
+ handleSearch();
+};
+
+const loadProjects = () => {
+ prjLoading.value = true;
+ projectTransactions({...searchForm.value, ...prjPage}).then(res => {
+ prjLoading.value = false;
+ projects.value = res.data.records || [];
+ prjTotal.value = res.data.total || 0;
+
+ if (projects.value.length > 0) {
+ handleSelectProject(projects.value[0]);
+ } else {
+ selectedProjectId.value = null;
+ customers.value = [];
+ custTotal.value = 0;
+ contracts.value = [];
+ contractTotal.value = 0;
+ }
+ }).catch(() => {
+ prjLoading.value = false;
});
- const total = ref(0);
- const recordTotal = ref(0);
- const data = reactive({
- searchForm: {
- searchText: "",
- invoiceDate: "",
- },
+};
+
+const handleSelectProject = (prj) => {
+ selectedProjectId.value = prj.projectId;
+ custPage.current = 1;
+ loadCustomers();
+};
+
+const loadCustomers = () => {
+ if (!selectedProjectId.value) return;
+ custLoading.value = true;
+ const params = {
+ projectId: selectedProjectId.value,
+ customerName: searchForm.value.customerName,
+ contractName: searchForm.value.contractName,
+ ...custPage
+ };
+ customewTransactions(params).then(res => {
+ custLoading.value = false;
+ const records = res.data.records || res.data || [];
+ customers.value = records;
+ custTotal.value = res.data.total || records.length || 0;
+
+ if (customers.value.length > 0) {
+ handleSelectCustomer(customers.value[0]);
+ } else {
+ selectedCustomerId.value = null;
+ contracts.value = [];
+ contractTotal.value = 0;
+ }
+ }).catch(() => {
+ custLoading.value = false;
});
- const customerId = ref("");
- const { searchForm } = toRefs(data);
- const originReceiptRecord = ref([]);
- // 鏌ヨ鍒楄〃
- /** 鎼滅储鎸夐挳鎿嶄綔 */
- const handleQuery = () => {
- page.current = 1;
- getList();
- };
- const paginationChange = obj => {
- page.current = obj.page;
- page.size = obj.limit;
- getList();
- };
- const getList = () => {
- tableLoading.value = true;
- customewTransactions({ ...searchForm.value, ...page }).then(res => {
- tableLoading.value = false;
- tableData.value = res.data.records;
- total.value = res.data.total;
- if (tableData.value.length > 0) {
- recordPage.current = 1;
- customerId.value = tableData.value[0].customerId;
- receiptPaymentList(customerId.value);
- }
- });
- };
- const formattedNumber = (row, column, cellValue) => {
- return cellValue ? parseFloat(cellValue).toFixed(2) : "0.00";
- };
- // 涓昏〃鍚堣鏂规硶
- const summarizeMainTable = param => {
- return proxy.summarizeTable(
- param,
- [
- "contractAmounts",
- "receiptPaymentAmount",
- "returnAmount",
- "receiptableAmount",
- ],
- {
- ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
- futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
- }
- );
- };
- // 瀛愯〃鍚堣鏂规硶
- const summarizeMainTable1 = param => {
- var summarizeTable = proxy.summarizeTable(
- param,
- [
- "contractAmount",
- "receiptPaymentAmount",
- "returnAmount",
- "receiptableAmount",
- ],
- {
- ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
- futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
- }
- );
- return summarizeTable;
- };
+};
- const receiptPaymentList = id => {
- const param = {
- customerId: id,
- current: recordPage.current,
- size: recordPage.size,
- };
- customewTransactionsDetails(param).then(res => {
- if (Array.isArray(res.data)) {
- originReceiptRecord.value = res.data;
- recordTotal.value = res.data.length;
- handlePagination({ page: 1, limit: recordPage.size });
- } else {
- receiptRecord.value = res.data.records;
- recordTotal.value = res.data.total;
- }
- });
+const handleSelectCustomer = (cust) => {
+ selectedCustomerId.value = cust.customerId;
+ contractPage.current = 1;
+ loadContracts();
+};
+
+const loadContracts = () => {
+ if (!selectedCustomerId.value) return;
+ contractLoading.value = true;
+ const params = {
+ projectId: selectedProjectId.value,
+ customerId: selectedCustomerId.value,
+ contractName: searchForm.value.contractName,
+ ...contractPage
};
-
- // 姹囨璁板綍鍒楄〃鍒嗛〉
- const recordPaginationChange = pagination => {
- recordPage.current = pagination.page;
- recordPage.size = pagination.limit;
- receiptPaymentList(customerId.value);
- };
-
- const rowClickMethod = row => {
- customerId.value = row.customerId;
- receiptPaymentList(customerId.value);
- };
-
- const handlePagination = ({ page, limit }) => {
- recordPage.current = page;
- recordPage.size = limit;
-
- const start = (page - 1) * limit;
- const end = start + limit;
-
- receiptRecord.value = originReceiptRecord.value.slice(start, end);
- };
-
- onMounted(() => {
- getList();
+ customewTransactionsDetails(params).then(res => {
+ contractLoading.value = false;
+ const records = Array.isArray(res.data) ? res.data : (res.data.records || []);
+ contracts.value = records;
+ contractTotal.value = res.data.total || records.length || 0;
+ }).catch(() => {
+ contractLoading.value = false;
});
+};
+
+const handlePrjPageChange = (val) => {
+ prjPage.current = val;
+ loadProjects();
+};
+
+const handleCustPageChange = (val) => {
+ custPage.current = val;
+ loadCustomers();
+};
+
+const handleContractPageChange = (val) => {
+ contractPage.current = val;
+ loadContracts();
+};
+
+const fmtMoney = (num) => {
+ if (num === null || num === undefined || isNaN(num)) return '楼 0.00';
+ return '楼 ' + parseFloat(num).toLocaleString('zh-CN', {minimumFractionDigits: 2, maximumFractionDigits: 2});
+};
+
+const calcRate = (paid, amount) => {
+ if (!amount || amount == 0) return '0.0';
+ const val = ((paid || 0) / amount) * 100;
+ return val.toFixed(1);
+};
+
+const getDaysFromSign = (dateStr) => {
+ if (!dateStr) return 0;
+ const sign = new Date(dateStr);
+ const now = new Date();
+ const diffTime = now - sign;
+ return Math.max(0, Math.floor(diffTime / (1000 * 60 * 60 * 24)));
+};
+
+const tableRowClassName = ({row}) => {
+ if (row.isPaymentTimeout) {
+ return 'warning-row';
+ }
+ return '';
+};
+
+onMounted(() => {
+ loadProjects();
+ getSummary();
+});
</script>
-<style scoped lang="scss">
- .table_list {
- width: 50%;
+<style scoped>
+/* CSS Variables */
+.layout-fullscreen {
+ --bg: #f8fafc;
+ --card: #ffffff;
+ --text: #0f172a;
+ --muted: #64748b;
+ --border: #e2e8f0;
+ --primary: #2563eb;
+ --primary-50: #eff6ff;
+ --success: #16a34a;
+ --success-50: #dcfce7;
+ --warning: #d97706;
+ --danger: #dc2626;
+ --danger-50: #fef2f2;
+ --danger-border: #fecaca;
+
+ height: calc(100vh - 84px); /* Fallback height, allows container to take viewport height */
+ display: flex;
+ flex-direction: column;
+ background-color: var(--bg);
+ padding: 16px;
+ overflow: hidden; /* Hide body scroll, let columns scroll */
+}
+
+/* 椤堕儴鎼滅储 */
+.search_form {
+ background: var(--card);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ padding: 16px 20px 0 20px;
+ margin-bottom: 12px;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
+}
+
+/* 鍔ㄦ�佺粺璁℃眹鎬绘爮 */
+.summary-strip {
+ background: var(--card);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ padding: 10px 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 13px;
+ flex-shrink: 0;
+}
+
+.summary-stats {
+ display: flex;
+ gap: 20px;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.summary-stats b {
+ color: var(--primary);
+ font-size: 14px;
+}
+
+.warn-summary {
+ background: var(--danger-50);
+ color: var(--danger);
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-weight: 600;
+ border: 1px solid var(--danger-border);
+}
+
+/* 涓夋爮鑱斿姩 */
+.layout-columns {
+ flex: 1;
+ display: grid;
+ grid-template-columns: 310px 330px 1fr;
+ gap: 16px;
+ margin-top: 16px;
+ overflow: hidden;
+}
+
+@media (max-width: 1400px) {
+ .layout-columns {
+ grid-template-columns: 280px 300px 1fr;
}
+}
+
+@media (max-width: 1200px) {
+ .layout-columns {
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+ }
+
+ .layout-fullscreen {
+ overflow: auto;
+ }
+
+ .column-panel {
+ min-height: 400px;
+ }
+}
+
+.column-panel {
+ background: var(--card);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
+}
+
+.panel-header {
+ padding: 12px 16px;
+ background: #f8fafc;
+ border-bottom: 1px solid var(--border);
+ font-size: 14px;
+ font-weight: 600;
+ flex-shrink: 0;
+ color: var(--text);
+}
+
+.panel-body {
+ flex: 1;
+ overflow-y: auto;
+ padding: 12px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.panel-pagination {
+ padding: 8px;
+ background: #ffffff;
+ border-top: 1px solid var(--border);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+/* 鍒楄〃鍗$墖 (椤圭洰 & 瀹㈡埛) */
+.item-card {
+ background: #ffffff;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ padding: 12px 14px;
+ cursor: pointer;
+ transition: all 0.15s;
+}
+
+.item-card:hover {
+ border-color: #cbd5e1;
+ background: #f8fafc;
+}
+
+.item-card.active {
+ border-color: var(--primary);
+ background: var(--primary-50);
+ box-shadow: 0 0 0 1px var(--primary);
+}
+
+.card-title-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 6px;
+}
+
+.card-main-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--text);
+ line-height: 1.4;
+}
+
+.card-code {
+ font-size: 11px;
+ color: var(--muted);
+ background: #f1f5f9;
+ padding: 2px 6px;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+.meta-row {
+ display: flex;
+ justify-content: space-between;
+ font-size: 12px;
+ color: var(--muted);
+ margin-top: 4px;
+}
+
+.meta-row strong {
+ color: var(--text);
+ font-weight: 600;
+}
+
+.text-danger {
+ color: var(--danger) !important;
+ font-weight: 700;
+}
+
+:deep(.warning-row) {
+ background-color: var(--danger-50) !important;
+}
+
+.empty-state {
+ text-align: center;
+ color: var(--muted);
+ padding: 40px 16px;
+ font-size: 13px;
+}
</style>
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index d2e82b5..ccef759 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -188,6 +188,7 @@
</template>
</el-table-column>
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
+
<el-table-column
label="閿�鍞悎鍚屽彿"
prop="salesContractNo"
@@ -216,7 +217,14 @@
label="浠樻鏂瑰紡"
prop="paymentMethod"
show-overflow-tooltip
- />
+ >
+ <template #default="scope">
+ <dict-tag
+ :options="checkout_payment"
+ :value="scope.row.paymentMethod"
+ />
+ </template>
+ </el-table-column>
<el-table-column
label="鍚堝悓閲戦(鍏�)"
prop="contractAmount"
@@ -322,6 +330,76 @@
</el-button>
</el-col>
</el-row>
+
+ <el-divider content-position="left">鍏宠仈椤圭洰</el-divider>
+ <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'"
+ filterable
+ >
+ <el-option
+ v-for="item in customerOption"
+ :key="item.id"
+ :label="item.customerName"
+ :value="item.id"
+ >
+ {{
+ item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
+ }}
+ </el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectId">
+ <el-select
+ v-model="form.projectId"
+ placeholder="璇烽�夋嫨椤圭洰"
+ clearable
+ filterable
+ :disabled="operationType === 'view'"
+ @focus="previousProjectId = form.projectId"
+ @change="handleProjectChange"
+ >
+ <el-option
+ v-for="item in projectOptions"
+ :key="item.id"
+ :label="item.no + ' - ' + item.title"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="鍏宠仈鍚堝悓锛�" prop="associatedContractIds">
+ <el-select
+ v-model="form.associatedContractIds"
+ multiple
+ placeholder="璇烽�夋嫨鍏宠仈鍚堝悓"
+ clearable
+ filterable
+ :disabled="operationType === 'view'"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in contractOptions"
+ :key="item.id"
+ :label="item.contractNo"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-divider content-position="left">鍙拌处淇℃伅</el-divider>
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="閿�鍞悎鍚屽彿锛�" prop="salesContractNo">
@@ -372,44 +450,11 @@
</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'"
- filterable
- >
- <el-option
- v-for="item in customerOption"
- :key="item.id"
- :label="item.customerName"
- :value="item.id"
- >
- {{
- item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
- }}
- </el-option>
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
- <el-input
- v-model="form.projectName"
- placeholder="璇疯緭鍏�"
- clearable
- :disabled="operationType === 'view'"
- />
- </el-form-item>
- </el-col>
- </el-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"
+ @change="(val) => { if(val) form.year = parseInt(val.substring(0, 4)) }"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="date"
@@ -421,12 +466,19 @@
</el-col>
<el-col :span="12">
<el-form-item label="浠樻鏂瑰紡">
- <el-input
+ <el-select
v-model="form.paymentMethod"
- placeholder="璇疯緭鍏�"
+ placeholder="璇烽�夋嫨浠樻鏂瑰紡"
clearable
:disabled="operationType === 'view'"
- />
+ >
+ <el-option
+ v-for="item in checkout_payment"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
</el-form-item>
</el-col>
</el-row>
@@ -483,13 +535,160 @@
</el-form-item>
</el-col>
</el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="绛惧崟鍗曚綅锛�" prop="signingUnitId">
+ <el-tree-select
+ v-model="form.signingUnitId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ value-key="id"
+ placeholder="璇烽�夋嫨绛惧崟鍗曚綅"
+ check-strictly
+ clearable
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍔炰簨澶勶細" prop="officeId">
+ <el-tree-select
+ v-model="form.officeId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ value-key="id"
+ placeholder="璇烽�夋嫨鍔炰簨澶�"
+ check-strictly
+ clearable
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="缁勮礋璐d汉锛�" prop="groupLeaderId">
+ <el-select
+ v-model="form.groupLeaderId"
+ filterable
+ placeholder="璇烽�夋嫨缁勮礋璐d汉"
+ clearable
+ :disabled="operationType === 'view'"
+ >
+ <el-option
+ v-for="item in userList"
+ :key="item.userId"
+ :label="item.nickName"
+ :value="item.userId"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="骞翠唤锛�">
+ <el-input
+ v-model="form.year"
+ placeholder="鑷姩鍙栫璁㈡棩鏈�"
+ disabled
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鏄惁寮�鍙戠エ锛�">
+ <el-switch
+ v-model="form.isInvoice"
+ :active-value="1"
+ :inactive-value="0"
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁涓爣锛�">
+ <el-switch
+ v-model="form.isBid"
+ :active-value="1"
+ :inactive-value="0"
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12" v-if="form.isBid === 1">
+ <el-form-item label="涓爣浠凤細">
+ <el-input-number
+ v-model="form.bidPrice"
+ :precision="2"
+ :step="1000"
+ :min="0"
+ style="width: 100%"
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12" v-if="form.isBid !== 1">
+ <el-form-item label="鏈腑鏍囧師鍥狅細">
+ <el-input
+ v-model="form.unbidReason"
+ placeholder="璇疯緭鍏ユ湭涓爣鍘熷洜"
+ clearable
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓鍘熶欢绾﹀畾鍥炰紶鏃ユ湡锛�">
+ <el-date-picker
+ style="width: 100%"
+ v-model="form.contractOriginalReturnDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ clearable
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <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>
+ </el-col>
+ </el-row>
+ <el-row v-if="operationType !== 'view'" :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="闄勪欢鏉愭枡锛�" prop="salesLedgerFiles">
+ <FileUpload
+ v-model:file-list="fileList"
+ :disabled="operationType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-divider content-position="left">浜у搧淇℃伅</el-divider>
<el-row>
- <el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
+ <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'"
@@ -566,30 +765,6 @@
</template>
</el-table-column>
</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>
- </el-col>
- </el-row>
- <el-row v-if="operationType !== 'view'" :gutter="30">
- <el-col :span="24">
- <el-form-item label="闄勪欢鏉愭枡锛�" prop="salesLedgerFiles">
- <FileUpload
- v-model:file-list="fileList"
- :disabled="operationType === 'view'"
- />
- </el-form-item>
- </el-col>
- </el-row>
</el-form>
</FormDialog>
<!-- 浠庢姤浠峰崟瀵煎叆锛堜粎瀹℃壒閫氳繃锛� -->
@@ -1105,6 +1280,56 @@
</el-row>
<el-row :gutter="30">
<el-col :span="24">
+ <el-form-item label="鍙戣揣鍗曟嵁锛�" prop="shippingDocument">
+ <el-input
+ v-model="deliveryForm.shippingDocument"
+ placeholder="璇疯緭鍏ュ彂璐у崟鎹�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="鏄惁瀵硅处锛�" prop="isReconciled">
+ <el-switch v-model="deliveryForm.isReconciled" :active-value="1" :inactive-value="0" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鏄惁缁撶畻锛�" prop="isSettled">
+ <el-switch v-model="deliveryForm.isSettled" :active-value="1" :inactive-value="0" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鏄惁宸插紑绁細" prop="isInvoiced">
+ <el-switch v-model="deliveryForm.isInvoiced" :active-value="1" :inactive-value="0" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12" v-if="deliveryForm.isReconciled === 1">
+ <el-form-item label="瀵硅处鍗曞綊妗f儏鍐碉細" prop="statementArchivingStatus">
+ <el-input
+ type="textarea"
+ v-model="deliveryForm.statementArchivingStatus"
+ placeholder="璇疯緭鍏ュ璐﹀崟褰掓。鎯呭喌"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12" v-if="deliveryForm.isSettled === 1">
+ <el-form-item label="缁撶畻鍗曞綊妗f儏鍐碉細" prop="settlementArchivingStatus">
+ <el-input
+ type="textarea"
+ v-model="deliveryForm.settlementArchivingStatus"
+ placeholder="璇疯緭鍏ョ粨绠楀崟褰掓。鎯呭喌"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
<el-form-item label="搴撳瓨锛�" prop="batchNo">
<el-table
:data="deliveryForm.batchNoList"
@@ -1176,9 +1401,11 @@
import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
import { ElMessageBox, ElMessage } from "element-plus";
import useUserStore from "@/store/modules/user";
-import { userListNoPage } from "@/api/system/user.js";
+import { userListNoPage, deptTreeSelect } from "@/api/system/user.js";
+import { listProject, getProject } from "@/api/projectManagement/project.js";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
+import { contractListNoPage } from "@/api/salesManagement/contractManagement.js";
import {
ledgerListPage,
productList,
@@ -1208,12 +1435,15 @@
const route = useRoute();
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
-const { tax_rate } = proxy.useDict("tax_rate");
+const { tax_rate, checkout_payment } = proxy.useDict("tax_rate", "checkout_payment");
const tableData = ref([]);
const productData = ref([]);
const selectedRows = ref([]);
const productSelectedRows = ref([]);
const userList = ref([]);
+const deptOptions = ref([]);
+const projectOptions = ref([]);
+const contractOptions = ref([]);
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
@@ -1248,6 +1478,18 @@
maintenanceTime: "",
productData: [],
executionDate: "",
+ projectId: undefined,
+ associatedContractIds: [],
+
+ year: "",
+ signingUnitId: undefined,
+ officeId: undefined,
+ groupLeaderId: undefined,
+ isInvoice: 0,
+ bidPrice: undefined,
+ isBid: 0,
+ unbidReason: "",
+ contractOriginalReturnDate: "",
hasProductionRecord: false,
createTime: "",
},
@@ -1323,6 +1565,51 @@
total: 0,
});
const selectedQuotation = ref(null);
+
+const previousProjectId = ref(null);
+const handleProjectChange = (val) => {
+ if (productData.value && productData.value.length > 0) {
+ ElMessageBox.confirm("褰撳墠宸插綍鍏ヤ骇鍝佹槑缁嗭紝鍒囨崲椤圭洰灏嗚瑕嗙洊锛屾槸鍚︾户缁紵", "鎻愮ず", {
+ type: "warning",
+ })
+ .then(() => {
+ previousProjectId.value = val;
+ fetchAndPopulateProject(val);
+ })
+ .catch(() => {
+ form.value.projectId = previousProjectId.value;
+ });
+ } else {
+ previousProjectId.value = val;
+ fetchAndPopulateProject(val);
+ }
+};
+
+const fetchAndPopulateProject = (val) => {
+ let proj = projectOptions.value.find((p) => p.id === val);
+ form.value.projectName = proj ? proj.title : "";
+
+ if (!val) {
+ productData.value = [];
+ return;
+ }
+
+ getProject(val).then((res) => {
+ const projectInfo = res.data;
+ if (projectInfo.info && projectInfo.info.clientId) {
+ form.value.customerId = projectInfo.info.clientId;
+ }
+ if (projectInfo.salesLedgerProductList) {
+ productData.value = projectInfo.salesLedgerProductList.map((item) => ({
+ ...item,
+ id: undefined,
+ salesLedgerId: undefined
+ }));
+ } else {
+ productData.value = [];
+ }
+ });
+};
// 鍙戣揣鐩稿叧
const deliveryFormVisible = ref(false);
@@ -1423,6 +1710,12 @@
expressCompany: "",
expressNumber: "",
type: "璐ц溅", // 璐ц溅, 蹇��
+ shippingDocument: "",
+ isReconciled: 0,
+ statementArchivingStatus: "",
+ isSettled: 0,
+ settlementArchivingStatus: "",
+ isInvoiced: 0,
},
deliveryRules: {
shippingCarNumber: [
@@ -1750,6 +2043,9 @@
form.value = {};
productData.value = [];
selectedQuotation.value = null;
+ deptTreeSelect().then(res => deptOptions.value = res.data);
+ listProject({}).then(res => projectOptions.value = res.rows || res.data.records || res.data);
+ contractListNoPage({}).then(res => contractOptions.value = res.data);
let userLists = await userListNoPage();
userList.value = userLists.data;
listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
@@ -2993,13 +3289,22 @@
let res = await userStore.getInfo();
currentFactoryName.value = res.user.currentFactoryName;
};
-onMounted(() => {
+onMounted(async () => {
searchForm.salesContractNo = route.query.salesContractNo;
getList();
userListNoPage().then((res) => {
userList.value = res.data;
});
getCurrentFactoryName();
+
+ if (route.query.action === "add") {
+ await openForm("add");
+ if (route.query.projectId) {
+ const pId = Number(route.query.projectId);
+ form.value.projectId = pId;
+ fetchAndPopulateProject(pId);
+ }
+ }
});
</script>
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index fce764f..edd0102 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -3,7 +3,7 @@
<el-card class="box-card">
<!-- 鎼滅储鍖哄煙 -->
<el-row :gutter="20" class="search-row">
- <el-col :span="8">
+ <el-col :span="6">
<el-input
v-model="searchForm.quotationNo"
placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
@@ -15,7 +15,7 @@
</template>
</el-input>
</el-col>
- <el-col :span="8">
+ <el-col :span="6">
<el-select v-model="searchForm.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
<el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
{{
@@ -32,7 +32,12 @@
<!-- <el-option label="宸茶繃鏈�" value="宸茶繃鏈�"></el-option>-->
<!-- </el-select>-->
<!-- </el-col>-->
- <el-col :span="8">
+ <el-col :span="6">
+ <el-select v-model="searchForm.projectId" placeholder="璇烽�夋嫨椤圭洰鍚嶇О" clearable filterable>
+ <el-option v-for="item in projectOptions" :key="item.id" :label="item.no + ' - ' + item.title" :value="item.id" />
+ </el-select>
+ </el-col>
+ <el-col :span="6">
<el-button type="primary" @click="handleSearch">鎼滅储</el-button>
<el-button @click="resetSearch">閲嶇疆</el-button>
<el-button style="float: right;" type="primary" @click="handleAdd">
@@ -52,6 +57,7 @@
>
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" />
+ <el-table-column prop="projectName" label="椤圭洰鍚嶇О" />
<el-table-column prop="customer" label="瀹㈡埛鍚嶇О" />
<el-table-column prop="salesperson" label="涓氬姟鍛�" width="100" />
<el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="120" />
@@ -149,6 +155,13 @@
<el-col :span="12">
<el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
<el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏ヤ粯娆炬柟寮�" clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="椤圭洰鍚嶇О" prop="projectId">
+ <el-select v-model="form.projectId" placeholder="璇烽�夋嫨椤圭洰鍚嶇О" style="width: 100%" clearable filterable>
+ <el-option v-for="item in projectOptions" :key="item.id" :label="item.no + ' - ' + item.title" :value="item.id" />
+ </el-select>
</el-form-item>
</el-col>
</el-row>
@@ -258,6 +271,7 @@
<el-dialog v-model="viewDialogVisible" title="鎶ヤ环璇︽儏" width="800px">
<el-descriptions :column="2" border>
<el-descriptions-item label="鎶ヤ环鍗曞彿">{{ currentQuotation.quotationNo }}</el-descriptions-item>
+ <el-descriptions-item label="椤圭洰鍚嶇О">{{ currentQuotation.projectName }}</el-descriptions-item>
<el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentQuotation.customer }}</el-descriptions-item>
<el-descriptions-item label="涓氬姟鍛�">{{ currentQuotation.salesperson }}</el-descriptions-item>
<el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
@@ -303,19 +317,22 @@
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {listCustomer} from "@/api/basicData/customer.js";
import { userListNoPage } from "@/api/system/user.js";
+import { listProject } from "@/api/projectManagement/project.js";
// 鍝嶅簲寮忔暟鎹�
const loading = ref(false)
const searchForm = reactive({
quotationNo: '',
customerId: '',
- status: ''
+ status: '',
+ projectId: undefined
})
const quotationList = ref([])
const userList = ref([])
const productOptions = ref([]);
const modelOptions = ref([]);
+const projectOptions = ref([]);
const pagination = reactive({
total: 3,
currentPage: 1,
@@ -328,6 +345,7 @@
const form = reactive({
quotationNo: '',
customerId: undefined,
+ projectId: undefined,
customer: '',
salesperson: '',
quotationDate: '',
@@ -396,6 +414,7 @@
searchForm.quotationNo = ''
searchForm.customer = ''
searchForm.status = ''
+ searchForm.projectId = undefined
// 閲嶇疆鍒扮涓�椤靛苟閲嶆柊鏌ヨ
pagination.currentPage = 1
handleSearch()
@@ -407,7 +426,15 @@
resetForm()
dialogVisible.value = true
getProductOptions();
- fetchCustomerOptions()
+ fetchCustomerOptions();
+ fetchProjectOptions();
+}
+
+const fetchProjectOptions = () => {
+ if (projectOptions.value.length > 0) return
+ listProject({current: -1,size:-1}).then((res) => {
+ projectOptions.value = res.data.records;
+ });
}
const fetchCustomerOptions = () => {
@@ -530,7 +557,8 @@
unitPrice: product.unitPrice || 0,
amount: product.amount || 0
})) : [],
- totalAmount: row.totalAmount || 0
+ totalAmount: row.totalAmount || 0,
+ projectName: row.projectName || ''
}
viewDialogVisible.value = true
}
@@ -543,11 +571,13 @@
// 鍏堝姞杞戒骇鍝佹爲鏁版嵁锛屽惁鍒� el-tree-select 鏃犳硶鍙嶆樉浜у搧鍚嶇О
await getProductOptions()
await fetchCustomerOptions()
+ await fetchProjectOptions()
// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
form.quotationNo = row.quotationNo || ''
form.customer = row.customer || ''
form.customerId = row.customerId || undefined
+ form.projectId = row.projectId || undefined
form.salesperson = row.salesperson || ''
form.quotationDate = row.quotationDate || ''
form.validDate = row.validDate || ''
@@ -629,6 +659,8 @@
const resetForm = () => {
form.customer = ''
+ form.customerId = undefined
+ form.projectId = undefined
form.salesperson = ''
form.quotationDate = ''
form.validDate = ''
@@ -790,6 +822,7 @@
getUserList()
handleSearch()
fetchCustomerOptions()
+ fetchProjectOptions()
})
</script>
--
Gitblit v1.9.3