From f670b79094c95d791fc43c8fc8b5858ebf8426dc Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 17 九月 2026 13:50:29 +0800
Subject: [PATCH] 一些向web对齐
---
src/pages/oa/_utils/approvalTemplateType.js | 13
src/api/salesManagement/indicatorStats.js | 20
src/api/salesManagement/invoiceLedger.js | 9
src/pages.json | 14
src/pages/cooperativeOffice/collaborativeApproval/approvalManagement.vue | 454 ++++++++
src/api/collaborativeApproval/approvalManagement.js | 20
src/pages/oa/ApproveManage/approve-list/index.vue | 11
src/api/inventoryManagement/stockInRecord.js | 55 +
src/pages/indexItem.vue | 103 +
src/api/customerService/index.js | 2
src/pages/sales/receiptPaymentLedger/index.vue | 18
src/api/procurementManagement/paymentLedger.js | 12
src/pages/cooperativeOffice/collaborativeApproval/_components/ApproveFlowSteps.vue | 261 ++++
src/pages/qualityManagement/processInspection/index.vue | 6
src/pages/oa/ApproveManage/approve-template/edit.vue | 2
src/pages/sales/receiptPaymentLedger/detail.vue | 46
src/pages/message.vue | 14
src/pages/oa/ApproveManage/approve-list/detail.vue | 15
src/pages/oa/ApproveManage/approve-template/detail.vue | 8
src/api/salesManagement/receiptPayment.js | 9
src/pages/cooperativeOffice/collaborativeApproval/approve.vue | 249 ----
src/pages/oa/ApproveManage/approve-list/_components/ApproveInstanceDetailBody.vue | 237 +++
src/pages/cooperativeOffice/collaborativeApproval/index.vue | 174 ++
src/pages/oa/_utils/approvalModuleListSearch.js | 56
src/pages/works.vue | 4
src/pages/qualityManagement/finalInspection/index.vue | 6
src/pages/oa/ApproveManage/approve-list/template-select.vue | 59
src/pages/cooperativeOffice/collaborativeApproval/detail.vue | 585 ++++-------
src/pages/procurementManagement/paymentLedger/index.vue | 6
src/pages/cooperativeOffice/collaborativeApproval/_utils/approveFlowSteps.js | 33
src/pages/oa/_utils/approveListUtils.js | 48
src/pages/procurementManagement/paymentLedger/detail.vue | 75
src/pages/login.vue | 14
src/pages/procurementManagement/purchaseReturnOrder/add.vue | 8
src/api/inventoryManagement/stockOut.js | 46
src/pages/oa/ApproveManage/approve-list/apply.vue | 83 +
src/pages/cooperativeOffice/collaborativeApproval/_components/ApprovalBusinessDetail.vue | 353 ++++++
src/pages/qualityManagement/materialInspection/index.vue | 35
src/pages/sales/salesQuotation/edit.vue | 11
39 files changed, 2,257 insertions(+), 917 deletions(-)
diff --git a/src/api/collaborativeApproval/approvalManagement.js b/src/api/collaborativeApproval/approvalManagement.js
new file mode 100644
index 0000000..875d4cf
--- /dev/null
+++ b/src/api/collaborativeApproval/approvalManagement.js
@@ -0,0 +1,20 @@
+// 鍗忓悓瀹℃壒 / 瀹℃壒娴佺▼閰嶇疆
+import request from "@/utils/request";
+
+/** 鏌ヨ鏌愬鎵圭被鍨嬬殑娴佺▼閰嶇疆鑺傜偣鍒楄〃 */
+export function getApproveProcessConfigNodeList(type) {
+ return request({
+ url: "/approveProcessConfigNode/list",
+ method: "get",
+ params: { type },
+ });
+}
+
+/** 淇濆瓨鏌愬鎵圭被鍨嬬殑娴佺▼閰嶇疆鑺傜偣锛堟暣缁勮鐩栵級 */
+export function addApproveProcessConfigNode(data) {
+ return request({
+ url: "/approveProcessConfigNode/add",
+ method: "post",
+ data,
+ });
+}
diff --git a/src/api/customerService/index.js b/src/api/customerService/index.js
index b5d9cea..e4527a7 100644
--- a/src/api/customerService/index.js
+++ b/src/api/customerService/index.js
@@ -77,7 +77,7 @@
// 鏍规嵁瀹㈡埛鏌ヨ閿�鍞鍗曞彿
export function getSalesLedger(query) {
return request({
- url: '/afterSalesService/listSalesLedger',
+ url: '/shippingInfo/getByCustomerName',
method: 'get',
params: query,
})
diff --git a/src/api/inventoryManagement/stockInRecord.js b/src/api/inventoryManagement/stockInRecord.js
new file mode 100644
index 0000000..1e588ff
--- /dev/null
+++ b/src/api/inventoryManagement/stockInRecord.js
@@ -0,0 +1,55 @@
+import request from "@/utils/request";
+
+// 鏌ヨ鍏ュ簱璁板綍鍒楄〃
+export const getStockInRecordListPage = params => {
+ return request({
+ url: "/stockInRecord/listPage",
+ method: "get",
+ params,
+ });
+};
+
+// 淇敼鍏ュ簱璁板綍
+export const updateStockInRecord = (id, data) => {
+ return request({
+ url: "/stockInRecord/" + id,
+ method: "put",
+ data,
+ });
+};
+
+// 鎵归噺鍒犻櫎鍏ュ簱璁板綍
+export const batchDeleteStockInRecords = ids => {
+ return request({
+ url: "/stockInRecord",
+ method: "delete",
+ data: ids,
+ });
+};
+
+// 鍒犻櫎寰呭鎵瑰叆搴撹褰�
+export const batchDeletePendingStockInRecords = ids => {
+ return request({
+ url: "/stockInRecord/pending",
+ method: "delete",
+ data: ids,
+ });
+};
+
+// 鎵归噺瀹℃壒鍏ュ簱璁板綍
+export const batchApproveStockInRecords = data => {
+ return request({
+ url: "/stockInRecord/approve",
+ method: "post",
+ data,
+ });
+};
+
+// 鎵归噺鍙嶅鍏ュ簱璁板綍
+export const batchUnapproveStockInRecords = data => {
+ return request({
+ url: "/stockInRecord/reAudit",
+ method: "post",
+ data,
+ });
+};
diff --git a/src/api/inventoryManagement/stockOut.js b/src/api/inventoryManagement/stockOut.js
new file mode 100644
index 0000000..42b9f99
--- /dev/null
+++ b/src/api/inventoryManagement/stockOut.js
@@ -0,0 +1,46 @@
+import request from "@/utils/request";
+
+// 鏌ヨ鍑哄簱璁板綍鍒楄〃
+export const getStockOutPage = params => {
+ return request({
+ url: "/stockOutRecord/listPage",
+ method: "get",
+ params,
+ });
+};
+
+// 鍒犻櫎鍑哄簱璁板綍
+export const delStockOut = ids => {
+ return request({
+ url: "/stockOutRecord",
+ method: "delete",
+ data: ids,
+ });
+};
+
+// 鍒犻櫎寰呭鎵瑰嚭搴撹褰�
+export const delPendingStockOut = ids => {
+ return request({
+ url: "/stockOutRecord/pending",
+ method: "delete",
+ data: ids,
+ });
+};
+
+// 鎵归噺瀹℃壒鍑哄簱璁板綍
+export const batchApproveStockOutRecords = data => {
+ return request({
+ url: "/stockOutRecord/approve",
+ method: "post",
+ data,
+ });
+};
+
+// 鎵归噺鍙嶅鍑哄簱璁板綍
+export const batchUnapproveStockOutRecords = data => {
+ return request({
+ url: "/stockOutRecord/reAudit",
+ method: "post",
+ data,
+ });
+};
diff --git a/src/api/procurementManagement/paymentLedger.js b/src/api/procurementManagement/paymentLedger.js
index 31aff29..6c5d9de 100644
--- a/src/api/procurementManagement/paymentLedger.js
+++ b/src/api/procurementManagement/paymentLedger.js
@@ -1,20 +1,20 @@
// 閲囪喘鍙拌处椤甸潰鎺ュ彛
import request from "@/utils/request";
-// 鍒嗛〉鏌ヨ
+/** 浠樻鍙拌处 - 渚涘簲鍟嗗線鏉ユ眹鎬� */
export function paymentLedgerList(query) {
return request({
- url: "/purchase/paymentRegistration/supplierNameListPage",
+ url: "/purchase/report/supplierTransactions",
method: "get",
params: query,
});
}
-// 鍒嗛〉鏌ヨ
-export function paymentRecordList(supplierId) {
+/** 浠樻鍙拌处 - 渚涘簲鍟嗗線鏉ユ槑缁� */
+export function paymentRecordList(params) {
return request({
- url: "/purchase/paymentRegistration/supplierNameListPageDetails",
+ url: "/purchase/report/supplierTransactionsDetails",
method: "get",
- params: supplierId,
+ params,
});
}
diff --git a/src/api/salesManagement/indicatorStats.js b/src/api/salesManagement/indicatorStats.js
new file mode 100644
index 0000000..ba3f4cc
--- /dev/null
+++ b/src/api/salesManagement/indicatorStats.js
@@ -0,0 +1,20 @@
+// 閿�鍞寚鏍囩粺璁℃帴鍙�
+import request from "@/utils/request";
+
+/** 瀹㈡埛寰�鏉ユ眹鎬� */
+export function customewTransactions(query) {
+ return request({
+ url: "/metricStatistics/customewTransactions",
+ method: "get",
+ params: query,
+ });
+}
+
+/** 瀹㈡埛寰�鏉ユ槑缁� */
+export function customewTransactionsDetails(query) {
+ return request({
+ url: "/metricStatistics/customewTransactionsDetails",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/salesManagement/invoiceLedger.js b/src/api/salesManagement/invoiceLedger.js
index 6a54493..b8752c5 100644
--- a/src/api/salesManagement/invoiceLedger.js
+++ b/src/api/salesManagement/invoiceLedger.js
@@ -55,15 +55,6 @@
})
}
-// 鍒嗛〉鏌ヨ
-export function invoiceLedgerSalesAccount(query) {
- return request({
- url: '/invoiceLedger/salesAccount',
- method: 'get',
- params: query
- })
-}
-
// 浜у搧寮�绁ㄨ褰曞垎椤垫煡璇�
export function registrationProductPage(query) {
return request({
diff --git a/src/api/salesManagement/receiptPayment.js b/src/api/salesManagement/receiptPayment.js
index 0f0529d..1a6562b 100644
--- a/src/api/salesManagement/receiptPayment.js
+++ b/src/api/salesManagement/receiptPayment.js
@@ -10,15 +10,6 @@
})
}
-// 瀹㈡埛寰�鏉ヨ褰曟煡璇�
-export function customerInteractions(query) {
- return request({
- url: '/receiptPayment/customerInteractions',
- method: 'get',
- params: query
- })
-}
-
// 璇︽儏
export function receiptPaymentInfo(query) {
return request({
diff --git a/src/pages.json b/src/pages.json
index 1aa49ad..92e2a06 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -416,6 +416,13 @@
}
},
{
+ "path": "pages/cooperativeOffice/collaborativeApproval/index",
+ "style": {
+ "navigationBarTitleText": "鍗忓悓瀹℃壒",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/cooperativeOffice/collaborativeApproval/index1",
"style": {
"navigationBarTitleText": "鍏嚭绠$悊",
@@ -992,6 +999,13 @@
}
},
{
+ "path": "pages/cooperativeOffice/collaborativeApproval/approvalManagement",
+ "style": {
+ "navigationBarTitleText": "瀹℃壒娴佺▼閰嶇疆",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/safeProduction/dangerInvestigation/index",
"style": {
"navigationBarTitleText": "闅愭偅鎺掓煡涓婃姤",
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/_components/ApprovalBusinessDetail.vue b/src/pages/cooperativeOffice/collaborativeApproval/_components/ApprovalBusinessDetail.vue
new file mode 100644
index 0000000..f09f220
--- /dev/null
+++ b/src/pages/cooperativeOffice/collaborativeApproval/_components/ApprovalBusinessDetail.vue
@@ -0,0 +1,353 @@
+<!--
+ 鍗忓悓瀹℃壒 / 涓氬姟鍗曟嵁璇︽儏
+ approveType 5=閲囪喘 6=鎶ヤ环 7=鍙戣揣锛屽崟鎹彿鍙栬〃鍗曘�屽鎵逛簨鐢便�嶅瓧娈垫壙杞界殑缂栧彿
+-->
+<template>
+ <view class="business-detail">
+ <!-- 鎶ヤ环瀹℃壒璇︽儏 -->
+ <view v-if="isQuotationApproval"
+ class="business-detail-block">
+ <u-divider text="鎶ヤ环璇︽儏"
+ text-size="28rpx"
+ color="#2979ff"></u-divider>
+ <u-skeleton :loading="quotationLoading"
+ rows="3"
+ animated>
+ <view v-if="!currentQuotation || !currentQuotation.quotationNo"
+ class="business-detail-empty">
+ 鏈煡璇㈠埌瀵瑰簲鎶ヤ环璇︽儏
+ </view>
+ <view v-else>
+ <u-cell-group :border="false">
+ <u-cell title="鎶ヤ环鍗曞彿"
+ :value="currentQuotation.quotationNo"></u-cell>
+ <u-cell title="瀹㈡埛鍚嶇О"
+ :value="currentQuotation.customer"></u-cell>
+ <u-cell title="涓氬姟鍛�"
+ :value="currentQuotation.salesperson"></u-cell>
+ <u-cell title="鎶ヤ环鏃ユ湡"
+ :value="currentQuotation.quotationDate"></u-cell>
+ <u-cell title="鏈夋晥鏈熻嚦"
+ :value="currentQuotation.validDate"></u-cell>
+ <u-cell title="浠樻鏂瑰紡"
+ :value="currentQuotation.paymentMethod"></u-cell>
+ <u-cell title="鎶ヤ环鎬婚">
+ <template #value>
+ <text class="business-detail-amount">
+ 楼{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }}
+ </text>
+ </template>
+ </u-cell>
+ </u-cell-group>
+ <view class="business-detail-products">
+ <view class="business-detail-subtitle">浜у搧鏄庣粏</view>
+ <view v-for="(item, index) in (currentQuotation.products || [])"
+ :key="index"
+ class="business-detail-item">
+ <view class="business-detail-item-head">
+ <text class="business-detail-item-name">{{ item.product }}</text>
+ <text class="business-detail-amount">楼{{ Number(item.unitPrice ?? 0).toFixed(2) }}</text>
+ </view>
+ <view class="business-detail-item-desc">
+ 瑙勬牸: {{ item.specification }} | 鍗曚綅: {{ item.unit }}
+ </view>
+ </view>
+ </view>
+ <view v-if="currentQuotation.remark"
+ class="business-detail-remark">
+ <view class="business-detail-subtitle">澶囨敞</view>
+ <view class="business-detail-remark-text">{{ currentQuotation.remark }}</view>
+ </view>
+ </view>
+ </u-skeleton>
+ </view>
+
+ <!-- 閲囪喘瀹℃壒璇︽儏 -->
+ <view v-if="isPurchaseApproval"
+ class="business-detail-block">
+ <u-divider text="閲囪喘璇︽儏"
+ text-size="28rpx"
+ color="#2979ff"></u-divider>
+ <u-skeleton :loading="purchaseLoading"
+ rows="3"
+ animated>
+ <view v-if="!currentPurchase || !currentPurchase.purchaseContractNumber"
+ class="business-detail-empty">
+ 鏈煡璇㈠埌瀵瑰簲閲囪喘璇︽儏
+ </view>
+ <view v-else>
+ <u-cell-group :border="false">
+ <u-cell title="閲囪喘鍚堝悓鍙�"
+ :value="currentPurchase.purchaseContractNumber"></u-cell>
+ <u-cell title="渚涘簲鍟嗗悕绉�"
+ :value="currentPurchase.supplierName"></u-cell>
+ <u-cell title="椤圭洰鍚嶇О"
+ :value="currentPurchase.projectName"></u-cell>
+ <u-cell title="閿�鍞悎鍚屽彿"
+ :value="currentPurchase.salesContractNo"></u-cell>
+ <u-cell title="绛捐鏃ユ湡"
+ :value="currentPurchase.executionDate"></u-cell>
+ <u-cell title="褰曞叆鏃ユ湡"
+ :value="currentPurchase.entryDate"></u-cell>
+ <u-cell title="浠樻鏂瑰紡"
+ :value="currentPurchase.paymentMethod"></u-cell>
+ <u-cell title="鍚堝悓閲戦">
+ <template #value>
+ <text class="business-detail-amount">
+ 楼{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }}
+ </text>
+ </template>
+ </u-cell>
+ </u-cell-group>
+ <view class="business-detail-products">
+ <view class="business-detail-subtitle">浜у搧鏄庣粏</view>
+ <view v-for="(item, index) in (currentPurchase.productData || [])"
+ :key="index"
+ class="business-detail-item">
+ <view class="business-detail-item-head">
+ <text class="business-detail-item-name">{{ item.productCategory }}</text>
+ <text class="business-detail-amount">楼{{ Number(item.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</text>
+ </view>
+ <view class="business-detail-item-desc">
+ 瑙勬牸: {{ item.specificationModel }} | 鏁伴噺: {{ item.quantity }} {{ item.unit }}
+ </view>
+ <view class="business-detail-item-extra">
+ 鍚◣鍗曚环: 楼{{ Number(item.taxInclusiveUnitPrice ?? 0).toFixed(2) }}
+ </view>
+ </view>
+ </view>
+ </view>
+ </u-skeleton>
+ </view>
+
+ <!-- 鍙戣揣瀹℃壒璇︽儏 -->
+ <view v-if="isDeliveryApproval"
+ class="business-detail-block">
+ <u-divider text="鍙戣揣璇︽儏"
+ text-size="28rpx"
+ color="#2979ff"></u-divider>
+ <u-skeleton :loading="deliveryLoading"
+ rows="3"
+ animated>
+ <view v-if="!currentDelivery || !currentDelivery.shippingInfo"
+ class="business-detail-empty">
+ 鏈煡璇㈠埌瀵瑰簲鍙戣揣璇︽儏
+ </view>
+ <view v-else>
+ <u-cell-group :border="false">
+ <u-cell title="閿�鍞鍗�"
+ :value="currentDelivery.shippingInfo.salesContractNo || '--'"></u-cell>
+ <u-cell title="鍙戣揣璁㈠崟鍙�"
+ :value="currentDelivery.shippingInfo.shippingNo || '--'"></u-cell>
+ <u-cell title="瀹㈡埛鍚嶇О"
+ :value="currentDelivery.shippingInfo.customerName || '--'"></u-cell>
+ <u-cell title="鍙戣揣绫诲瀷"
+ :value="currentDelivery.shippingInfo.type || '--'"></u-cell>
+ <u-cell title="鍙戣揣鏃ユ湡"
+ :value="currentDelivery.shippingInfo.shippingDate || '--'"></u-cell>
+ <u-cell title="瀹℃牳鐘舵��"
+ :value="currentDelivery.shippingInfo.status || '--'"></u-cell>
+ <u-cell title="鍙戣揣杞︾墝鍙�"
+ :value="currentDelivery.shippingInfo.shippingCarNumber || '--'"></u-cell>
+ <u-cell title="蹇�掑叕鍙�"
+ :value="currentDelivery.shippingInfo.expressCompany || '--'"></u-cell>
+ <u-cell title="蹇�掑崟鍙�"
+ :value="currentDelivery.shippingInfo.expressNumber || '--'"></u-cell>
+ </u-cell-group>
+ <view class="business-detail-products">
+ <view class="business-detail-subtitle">浜у搧鏄庣粏</view>
+ <view v-for="(item, index) in deliveryProductList"
+ :key="index"
+ class="business-detail-item">
+ <view class="business-detail-item-head">
+ <text class="business-detail-item-name">{{ item.productName }}</text>
+ <text class="business-detail-count">鏁伴噺: {{ item.deliveryQuantity }}</text>
+ </view>
+ <view class="business-detail-item-desc">
+ 瑙勬牸: {{ item.specificationModel }}
+ </view>
+ <view v-if="item.batchNo"
+ class="business-detail-item-extra">
+ 鎵瑰彿: {{ item.batchNo }}
+ </view>
+ </view>
+ </view>
+ <view v-if="currentDelivery.shippingInfo.storageBlobVOs && currentDelivery.shippingInfo.storageBlobVOs.length"
+ class="business-detail-remark">
+ <view class="business-detail-subtitle">鍙戣揣鍥剧墖</view>
+ <CommonUpload :model-value="currentDelivery.shippingInfo.storageBlobVOs"
+ disabled />
+ </view>
+ </view>
+ </u-skeleton>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { computed, ref, watch } from "vue";
+ import CommonUpload from "@/components/CommonUpload.vue";
+ import { getDeliveryDetailByShippingNo } from "@/api/collaborativeApproval/approvalProcess";
+ import { getQuotationList } from "@/api/salesManagement/salesQuotation";
+ import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger";
+
+ const props = defineProps({
+ /** 瀹℃壒绫诲瀷锛�5 閲囪喘 / 6 鎶ヤ环 / 7 鍙戣揣 */
+ approveType: { type: [Number, String], default: 0 },
+ /** 涓氬姟鍗曟嵁缂栧彿锛堣〃鍗曘�屽鎵逛簨鐢便�嶆壙杞斤級 */
+ businessNo: { type: String, default: "" },
+ });
+
+ const type = computed(() => Number(props.approveType));
+ const isQuotationApproval = computed(() => type.value === 6);
+ const isPurchaseApproval = computed(() => type.value === 5);
+ const isDeliveryApproval = computed(() => type.value === 7);
+
+ const quotationLoading = ref(false);
+ const currentQuotation = ref({});
+ const purchaseLoading = ref(false);
+ const currentPurchase = ref({});
+ const deliveryLoading = ref(false);
+ const currentDelivery = ref({});
+ const deliveryProductList = ref([]);
+
+ const fetchDetailData = async () => {
+ const no = (props.businessNo || "").trim();
+ if (!no) return;
+
+ if (isQuotationApproval.value) {
+ quotationLoading.value = true;
+ getQuotationList({ quotationNo: no })
+ .then(res => {
+ const records = res?.data?.records || [];
+ currentQuotation.value = records[0] || {};
+ })
+ .catch(() => {
+ currentQuotation.value = {};
+ })
+ .finally(() => {
+ quotationLoading.value = false;
+ });
+ return;
+ }
+
+ if (isPurchaseApproval.value) {
+ purchaseLoading.value = true;
+ getPurchaseByCode({ purchaseContractNumber: no })
+ .then(res => {
+ currentPurchase.value = res?.data || res || {};
+ })
+ .catch(() => {
+ currentPurchase.value = {};
+ })
+ .finally(() => {
+ purchaseLoading.value = false;
+ });
+ return;
+ }
+
+ if (isDeliveryApproval.value) {
+ deliveryLoading.value = true;
+ currentDelivery.value = {};
+ deliveryProductList.value = [];
+ getDeliveryDetailByShippingNo({ shippingNo: no })
+ .then(res => {
+ const detailData = res?.data || res || {};
+ currentDelivery.value = detailData;
+ deliveryProductList.value =
+ detailData.shippingProductDetailDtoList || [];
+ })
+ .catch(() => {
+ currentDelivery.value = {};
+ deliveryProductList.value = [];
+ })
+ .finally(() => {
+ deliveryLoading.value = false;
+ });
+ }
+ };
+
+ watch(
+ () => [props.approveType, props.businessNo],
+ () => {
+ currentQuotation.value = {};
+ currentPurchase.value = {};
+ currentDelivery.value = {};
+ deliveryProductList.value = [];
+ fetchDetailData();
+ },
+ { immediate: true }
+ );
+</script>
+
+<style scoped lang="scss">
+ .business-detail-block {
+ margin: 20rpx 0;
+ }
+
+ .business-detail-empty {
+ padding: 40rpx;
+ text-align: center;
+ color: #999;
+ }
+
+ .business-detail-amount {
+ font-size: 32rpx;
+ color: #e6a23c;
+ font-weight: bold;
+ }
+
+ .business-detail-count {
+ color: #2979ff;
+ }
+
+ .business-detail-products {
+ margin-top: 20rpx;
+ padding: 0 30rpx;
+ }
+
+ .business-detail-subtitle {
+ font-size: 28rpx;
+ font-weight: bold;
+ margin-bottom: 10rpx;
+ }
+
+ .business-detail-item {
+ background: #f8f8f8;
+ border-radius: 8rpx;
+ padding: 20rpx;
+ margin-bottom: 10rpx;
+ }
+
+ .business-detail-item-head {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .business-detail-item-name {
+ font-weight: bold;
+ }
+
+ .business-detail-item-desc {
+ font-size: 24rpx;
+ color: #666;
+ margin-top: 10rpx;
+ }
+
+ .business-detail-item-extra {
+ font-size: 24rpx;
+ color: #999;
+ margin-top: 4rpx;
+ }
+
+ .business-detail-remark {
+ margin-top: 20rpx;
+ padding: 0 30rpx;
+ }
+
+ .business-detail-remark-text {
+ font-size: 26rpx;
+ color: #666;
+ margin-top: 10rpx;
+ }
+</style>
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/_components/ApproveFlowSteps.vue b/src/pages/cooperativeOffice/collaborativeApproval/_components/ApproveFlowSteps.vue
new file mode 100644
index 0000000..5ca6cb7
--- /dev/null
+++ b/src/pages/cooperativeOffice/collaborativeApproval/_components/ApproveFlowSteps.vue
@@ -0,0 +1,261 @@
+<!--
+ 鍗忓悓瀹℃壒 / 瀹℃壒娴佺▼鑺傜偣锛堣鎯呫�佸鏍稿叡鐢級
+ steps 鐢� _utils/approveFlowSteps.js 鐨� mapApprovalSteps 鐢熸垚锛�
+ [{ title, approverName, status, statusText, approveTime, opinion, urlTem, isShen }]
+-->
+<template>
+ <view class="approval-process">
+ <view class="process-header">
+ <text class="process-title">瀹℃壒娴佺▼</text>
+ </view>
+ <view class="process-steps">
+ <view v-for="(step, index) in steps"
+ :key="index"
+ class="process-step"
+ :class="{
+ completed: step.status === 'completed',
+ current: step.status === 'current',
+ pending: step.status === 'pending',
+ rejected: step.status === 'rejected'
+ }">
+ <view class="step-indicator">
+ <view class="step-dot">
+ <text v-if="step.status === 'completed'"
+ class="step-icon">鉁�</text>
+ <text v-else-if="step.status === 'rejected'"
+ class="step-icon">鉁�</text>
+ <text v-else
+ class="step-number">{{ index + 1 }}</text>
+ </view>
+ <view v-if="index < steps.length - 1"
+ class="step-line"></view>
+ </view>
+ <view class="step-content">
+ <view class="step-info">
+ <text class="step-title">{{ step.title }}</text>
+ <text class="step-approver">{{ approverLabel(step) }}</text>
+ <text v-if="step.approveTime"
+ class="step-time">{{ step.approveTime }}</text>
+ </view>
+ <view v-if="step.opinion"
+ class="step-opinion">
+ <text class="opinion-label">瀹℃壒鎰忚锛�</text>
+ <text class="opinion-content">{{ step.opinion }}</text>
+ </view>
+ <!-- 寰呭鑺傜偣锛氫笌 Web 涓�鑷达紝璇︽儏妯″紡娓叉煋绂佺敤鐨勬剰瑙佹 -->
+ <view v-else-if="showPendingOpinion && step.isShen"
+ class="step-opinion step-opinion--pending">
+ <text class="opinion-label">瀹℃壒鎰忚锛�</text>
+ <u-textarea :model-value="step.opinion"
+ disabled
+ height="60"
+ border="surround"
+ placeholder="寰呭鎵�" />
+ </view>
+ <!-- 绛惧悕灞曠ず -->
+ <view v-if="step.urlTem"
+ class="step-sign">
+ <text class="opinion-label">绛惧悕锛�</text>
+ <image :src="step.urlTem"
+ mode="widthFix"
+ class="sign-image" />
+ </view>
+ </view>
+ </view>
+ <view v-if="!steps.length"
+ class="process-empty">鏆傛棤瀹℃壒鑺傜偣</view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ defineProps({
+ steps: { type: Array, default: () => [] },
+ /** 璇︽儏妯″紡锛氬緟瀹¤妭鐐规覆鏌撲竴涓鐢ㄧ殑鎰忚妗嗭紙涓� Web 涓�鑷达級 */
+ showPendingOpinion: { type: Boolean, default: false },
+ });
+
+ /** 瀹℃壒浜�-宸插悓鎰�/宸查┏鍥�/鏈鎵癸紙涓� Web 灞曠ず涓�鑷达級 */
+ const approverLabel = step =>
+ step?.statusText
+ ? `${step.approverName}-${step.statusText}`
+ : step?.approverName || "";
+</script>
+
+<style scoped lang="scss">
+ .approval-process {
+ background: #fff;
+ margin: 16px;
+ border-radius: 12px;
+ overflow: hidden;
+ }
+
+ .process-header {
+ padding: 16px;
+ border-bottom: 1px solid #f0f0f0;
+ background: #f8f9fa;
+ }
+
+ .process-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .process-steps {
+ padding: 20px;
+ }
+
+ .process-empty {
+ font-size: 14px;
+ color: #999;
+ text-align: center;
+ }
+
+ .process-step {
+ display: flex;
+ position: relative;
+ margin-bottom: 24px;
+
+ &:last-child {
+ margin-bottom: 0;
+
+ .step-line {
+ display: none;
+ }
+ }
+ }
+
+ .step-indicator {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin-right: 16px;
+ }
+
+ .step-dot {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 14px;
+ font-weight: 600;
+ position: relative;
+ z-index: 2;
+ }
+
+ .process-step.completed .step-dot {
+ background: #52c41a;
+ color: #fff;
+ }
+
+ .process-step.current .step-dot {
+ background: #1890ff;
+ color: #fff;
+ animation: pulse 2s infinite;
+ }
+
+ .process-step.pending .step-dot {
+ background: #d9d9d9;
+ color: #999;
+ }
+
+ .step-line {
+ width: 2px;
+ height: 40px;
+ background: #d9d9d9;
+ margin-top: 8px;
+ }
+
+ .process-step.completed .step-line {
+ background: #52c41a;
+ }
+
+ .process-step.rejected .step-dot {
+ background: #ff4d4f;
+ color: #fff;
+ }
+
+ .process-step.rejected .step-line {
+ background: #ff4d4f;
+ }
+
+ .step-content {
+ flex: 1;
+ padding-top: 4px;
+ }
+
+ .step-info {
+ margin-bottom: 8px;
+ }
+
+ .step-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ display: block;
+ margin-bottom: 4px;
+ }
+
+ .step-approver {
+ font-size: 14px;
+ color: #666;
+ display: block;
+ margin-bottom: 4px;
+ }
+
+ .step-time {
+ font-size: 12px;
+ color: #999;
+ display: block;
+ }
+
+ .step-opinion {
+ background: #f8f9fa;
+ padding: 12px;
+ border-radius: 8px;
+ border-left: 4px solid #52c41a;
+ }
+
+ .step-opinion--pending {
+ background: #fafbfc;
+ border-left-color: #d9d9d9;
+ }
+
+ .step-sign {
+ margin-top: 8px;
+ }
+
+ .sign-image {
+ width: 180px;
+ border-radius: 6px;
+ border: 1px solid #eee;
+ }
+
+ .opinion-label {
+ font-size: 12px;
+ color: #666;
+ display: block;
+ margin-bottom: 4px;
+ }
+
+ .opinion-content {
+ font-size: 14px;
+ color: #333;
+ line-height: 1.5;
+ }
+
+ @keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
+ }
+ 70% {
+ box-shadow: 0 0 0 10px rgba(24, 144, 255, 0);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 rgba(24, 144, 255, 0);
+ }
+ }
+</style>
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/_utils/approveFlowSteps.js b/src/pages/cooperativeOffice/collaborativeApproval/_utils/approveFlowSteps.js
new file mode 100644
index 0000000..39827cc
--- /dev/null
+++ b/src/pages/cooperativeOffice/collaborativeApproval/_utils/approveFlowSteps.js
@@ -0,0 +1,33 @@
+/**
+ * 鍗忓悓瀹℃壒锛氬鎵硅妭鐐� 鈫� 娴佺▼姝ラ锛堜笌 Web approvalDia 涓�鑷达級
+ * @param {Array} list approveProcessDetails 杩斿洖鐨勮妭鐐规暟缁�
+ * @returns {Array<{
+ * title: string, approverName: string, status: string, statusText: string,
+ * approveTime: string|null, opinion: string, urlTem: string, isShen: boolean
+ * }>}
+ */
+export function mapApprovalSteps(list) {
+ const rows = Array.isArray(list) ? list : [];
+ return rows.map((it, idx) => {
+ let status = "pending";
+ if (it.approveNodeStatus === 1) status = "completed";
+ else if (it.approveNodeStatus === 2) status = "rejected";
+ else if (it.isShen) status = "current";
+
+ let statusText = "鏈鎵�";
+ if (it.approveNodeStatus === 2) statusText = "宸查┏鍥�";
+ else if (it.approveNodeStatus === 1) statusText = "宸插悓鎰�";
+
+ return {
+ title: `绗�${idx + 1}姝ュ鎵筦,
+ approverName: it.approveNodeUser || "鏈煡鐢ㄦ埛",
+ status,
+ statusText,
+ approveTime: it.approveTime || null,
+ opinion: it.approveNodeReason || "",
+ urlTem:
+ it.urlTem || (it.url ? String(it.url).replace(/word/g, "img") : ""),
+ isShen: !!it.isShen,
+ };
+ });
+}
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/approvalManagement.vue b/src/pages/cooperativeOffice/collaborativeApproval/approvalManagement.vue
new file mode 100644
index 0000000..cb5f35e
--- /dev/null
+++ b/src/pages/cooperativeOffice/collaborativeApproval/approvalManagement.vue
@@ -0,0 +1,454 @@
+<!--
+ 鍗忓悓瀹℃壒 / 瀹℃壒娴佺▼閰嶇疆
+ 鎸夊鎵圭被鍨嬶紙1~7锛夌淮鎶ゅ鎵逛汉鑺傜偣锛屾帴鍙� /approveProcessConfigNode
+-->
+<template>
+ <view class="approve-config-page">
+ <PageHeader title="瀹℃壒娴佺▼閰嶇疆"
+ @back="goBack" />
+
+ <view class="tabs-wrap">
+ <up-tabs :list="tabList"
+ :current="activeTab"
+ line-color="#2979ff"
+ @click="onTabClick" />
+ </view>
+
+ <scroll-view class="config-scroll"
+ scroll-y
+ :show-scrollbar="false">
+ <view class="config-tip">
+ <text class="config-tip-title">{{ currentTypeLabel }}</text>
+ <text class="config-tip-desc">
+ {{ approverList.length ? `宸查厤缃� ${approverList.length} 涓鎵逛汉` : "鏈厤缃鎵逛汉" }}
+ </text>
+ </view>
+
+ <view v-if="loading"
+ class="loading-wrap">
+ <up-loading-icon mode="circle" />
+ <text class="loading-text">鍔犺浇涓�...</text>
+ </view>
+
+ <view v-else-if="approverList.length"
+ class="node-list">
+ <view v-for="(item, index) in approverList"
+ :key="index"
+ class="node-card">
+ <view class="node-head">
+ <view class="node-badge">{{ index + 1 }}</view>
+ <text class="node-level">{{ levelText(index) }}</text>
+ <view class="node-actions">
+ <view class="icon-btn"
+ :class="{ 'is-disabled': index === 0 }"
+ @click="moveUp(index)">
+ <up-icon name="arrow-up"
+ size="16"
+ color="#2979ff" />
+ </view>
+ <view class="icon-btn"
+ :class="{ 'is-disabled': index === approverList.length - 1 }"
+ @click="moveDown(index)">
+ <up-icon name="arrow-down"
+ size="16"
+ color="#2979ff" />
+ </view>
+ <view class="icon-btn"
+ @click="removeNode(index)">
+ <up-icon name="trash"
+ size="16"
+ color="#f56c6c" />
+ </view>
+ </view>
+ </view>
+ <view class="node-body"
+ @click="openUserPicker(index)">
+ <text :class="['node-approver', item.approverName ? '' : 'is-placeholder']">
+ {{ item.approverName || "璇烽�夋嫨瀹℃壒浜�" }}
+ </text>
+ <up-icon name="arrow-right"
+ size="16"
+ color="#c0c4cc" />
+ </view>
+ </view>
+ </view>
+
+ <view v-else
+ class="empty-wrap">
+ <up-empty mode="data"
+ text="鏆傛棤瀹℃壒浜洪厤缃�" />
+ </view>
+
+ <view class="add-node"
+ @click="addNode">
+ <up-icon name="plus"
+ size="16"
+ color="#2979ff" />
+ <text class="add-node-text">鏂板瀹℃壒浜�</text>
+ </view>
+
+ <view class="bottom-space" />
+ </scroll-view>
+
+ <FooterButtons cancel-text="杩斿洖"
+ confirm-text="淇濆瓨閰嶇疆"
+ :loading="saving"
+ @cancel="goBack"
+ @confirm="handleSave" />
+
+ <OaUserSearchPicker v-model:show="showUserPicker"
+ v-model="pickingUserId"
+ title="閫夋嫨瀹℃壒浜�"
+ :users="userList"
+ @select="onUserPicked" />
+ </view>
+</template>
+
+<script setup>
+ import { computed, ref } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import FooterButtons from "@/components/FooterButtons.vue";
+ import OaUserSearchPicker from "@/pages/oa/_components/OaUserSearchPicker.vue";
+ import { userListNoPage } from "@/api/system/user";
+ import {
+ addApproveProcessConfigNode,
+ getApproveProcessConfigNodeList,
+ } from "@/api/collaborativeApproval/approvalManagement";
+
+ /** 涓� Web approvalManagement 涓�鑷� */
+ const APPROVE_TYPES = [
+ { value: "1", label: "鍏嚭绠$悊" },
+ { value: "2", label: "璇峰亣绠$悊" },
+ { value: "3", label: "鍑哄樊绠$悊" },
+ { value: "4", label: "鎶ラ攢绠$悊" },
+ { value: "5", label: "閲囪喘瀹℃壒" },
+ { value: "6", label: "鎶ヤ环瀹℃壒" },
+ { value: "7", label: "鍙戣揣瀹℃壒" },
+ ];
+
+ const activeTab = ref(0);
+ const approverList = ref([]);
+ const loading = ref(false);
+ const saving = ref(false);
+ const userList = ref([]);
+
+ const showUserPicker = ref(false);
+ const pickingUserId = ref("");
+ const pickingIndex = ref(-1);
+
+ const tabList = computed(() => APPROVE_TYPES.map(t => ({ name: t.label })));
+ const currentTypeLabel = computed(
+ () => APPROVE_TYPES[activeTab.value]?.label || ""
+ );
+ const currentApproveType = computed(
+ () => Number(APPROVE_TYPES[activeTab.value]?.value ?? 1)
+ );
+
+ const levelText = index => `绗�${index + 1}绾;
+
+ const unwrapNodeList = res => {
+ if (Array.isArray(res?.data)) return res.data;
+ if (Array.isArray(res?.rows)) return res.rows;
+ if (Array.isArray(res?.data?.records)) return res.data.records;
+ return [];
+ };
+
+ const unwrapUserList = res => {
+ if (Array.isArray(res?.data)) return res.data;
+ if (Array.isArray(res?.rows)) return res.rows;
+ return [];
+ };
+
+ const loadData = async () => {
+ loading.value = true;
+ try {
+ const res = await getApproveProcessConfigNodeList(currentApproveType.value);
+ approverList.value = unwrapNodeList(res)
+ .map((item, index) => ({
+ ...item,
+ sortOrder: item.nodeOrder ?? item.sortOrder ?? index + 1,
+ }))
+ .sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
+ } catch {
+ approverList.value = [];
+ uni.showToast({ title: "鍔犺浇瀹℃壒閰嶇疆澶辫触", icon: "none" });
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const loadUserList = async () => {
+ try {
+ userList.value = unwrapUserList(await userListNoPage());
+ } catch {
+ userList.value = [];
+ uni.showToast({ title: "鍔犺浇浜哄憳鍒楄〃澶辫触", icon: "none" });
+ }
+ };
+
+ const onTabClick = item => {
+ activeTab.value = item?.index ?? 0;
+ loadData();
+ };
+
+ const addNode = () => {
+ approverList.value.push({
+ id: null,
+ approveType: currentApproveType.value,
+ approverId: null,
+ approverName: "",
+ sortOrder: approverList.value.length + 1,
+ });
+ };
+
+ const removeNode = index => {
+ uni.showModal({
+ title: "鍒犻櫎纭",
+ content: "纭畾鍒犻櫎璇ュ鎵逛汉鍚楋紵",
+ confirmText: "纭畾鍒犻櫎",
+ confirmColor: "#f56c6c",
+ success: res => {
+ if (!res.confirm) return;
+ approverList.value.splice(index, 1);
+ approverList.value.forEach((item, idx) => {
+ item.sortOrder = idx + 1;
+ });
+ },
+ });
+ };
+
+ const swapNode = (from, to) => {
+ const list = approverList.value;
+ const temp = list[from];
+ list[from] = list[to];
+ list[to] = temp;
+ list.forEach((item, idx) => {
+ item.sortOrder = idx + 1;
+ });
+ };
+
+ const moveUp = index => {
+ if (index <= 0) return;
+ swapNode(index, index - 1);
+ };
+
+ const moveDown = index => {
+ if (index >= approverList.value.length - 1) return;
+ swapNode(index, index + 1);
+ };
+
+ const openUserPicker = index => {
+ pickingIndex.value = index;
+ pickingUserId.value = approverList.value[index]?.approverId ?? "";
+ showUserPicker.value = true;
+ };
+
+ const onUserPicked = user => {
+ const idx = pickingIndex.value;
+ if (idx < 0 || !user) return;
+ approverList.value[idx].approverId = user.userId ?? user.id;
+ approverList.value[idx].approverName =
+ user.nickName || user.userName || "";
+ };
+
+ const handleSave = async () => {
+ if (saving.value) return;
+ if (!approverList.value.length) {
+ uni.showToast({ title: "璇疯嚦灏戦厤缃竴涓鎵逛汉", icon: "none" });
+ return;
+ }
+ if (approverList.value.some(item => !item.approverId)) {
+ uni.showToast({ title: "璇烽�夋嫨鎵�鏈夊鎵逛汉", icon: "none" });
+ return;
+ }
+ const ids = approverList.value.map(item => item.approverId);
+ if (new Set(ids).size !== ids.length) {
+ uni.showToast({ title: "瀹℃壒浜轰笉鑳介噸澶�", icon: "none" });
+ return;
+ }
+
+ saving.value = true;
+ try {
+ await addApproveProcessConfigNode(
+ approverList.value.map((item, index) => ({
+ approveType: currentApproveType.value,
+ nodeOrder: index + 1,
+ approverId: item.approverId,
+ approverName: item.approverName,
+ }))
+ );
+ uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
+ await loadData();
+ } catch {
+ uni.showToast({ title: "淇濆瓨澶辫触", icon: "none" });
+ } finally {
+ saving.value = false;
+ }
+ };
+
+ const goBack = () => uni.navigateBack();
+
+ onLoad(async () => {
+ await loadUserList();
+ await loadData();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/sales-common.scss";
+
+ .approve-config-page {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: #f0f3f8;
+ }
+
+ .tabs-wrap {
+ background: #fff;
+ border-bottom: 1px solid #f0f0f0;
+ padding: 0 8px;
+ }
+
+ .config-scroll {
+ flex: 1;
+ height: 0;
+ padding: 10px 12px 0;
+ }
+
+ .config-tip {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ padding: 2px 4px 10px;
+ }
+
+ .config-tip-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ .config-tip-desc {
+ font-size: 12px;
+ color: #909399;
+ }
+
+ .loading-wrap {
+ padding: 48px 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .loading-text {
+ font-size: 14px;
+ color: #909399;
+ }
+
+ .node-card {
+ background: #fff;
+ border-radius: 10px;
+ padding: 12px;
+ margin-bottom: 10px;
+ box-shadow: 0 2px 12px rgba(31, 45, 61, 0.05);
+ }
+
+ .node-head {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 10px;
+ }
+
+ .node-badge {
+ width: 26px;
+ height: 26px;
+ border-radius: 8px;
+ background: #2979ff;
+ color: #fff;
+ font-size: 14px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ }
+
+ .node-level {
+ flex: 1;
+ font-size: 15px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ .node-actions {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .icon-btn {
+ width: 30px;
+ height: 30px;
+ border-radius: 8px;
+ background: #f5f7fa;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &.is-disabled {
+ opacity: 0.4;
+ pointer-events: none;
+ }
+ }
+
+ .node-body {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 12px;
+ background: #f7f9fc;
+ border: 1px solid #eef1f6;
+ border-radius: 8px;
+ }
+
+ .node-approver {
+ font-size: 15px;
+ color: #303133;
+
+ &.is-placeholder {
+ color: #c0c4cc;
+ }
+ }
+
+ .empty-wrap {
+ padding: 40px 20px 20px;
+ }
+
+ .add-node {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ height: 44px;
+ background: #fff;
+ border: 1px dashed #c0c4cc;
+ border-radius: 10px;
+
+ &:active {
+ opacity: 0.85;
+ }
+ }
+
+ .add-node-text {
+ font-size: 14px;
+ color: #2979ff;
+ }
+
+ .bottom-space {
+ height: calc(80px + env(safe-area-inset-bottom));
+ }
+</style>
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/approve.vue b/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
index b3c8687..e406c1e 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
@@ -49,58 +49,14 @@
</view>
</view>
</view>
- <!-- 瀹℃壒娴佺▼ -->
- <view class="approval-process">
- <view class="process-header">
- <text class="process-title">瀹℃壒娴佺▼</text>
- </view>
- <view class="process-steps">
- <view v-for="(step, index) in approvalSteps"
- :key="index"
- class="process-step"
- :class="{
- 'completed': step.status === 'completed',
- 'current': step.status === 'current',
- 'pending': step.status === 'pending',
- 'rejected': step.status === 'rejected'
- }">
- <view class="step-indicator">
- <view class="step-dot">
- <text v-if="step.status === 'completed'"
- class="step-icon">鉁�</text>
- <text v-else-if="step.status === 'rejected'"
- class="step-icon">鉁�</text>
- <text v-else
- class="step-number">{{ index + 1 }}</text>
- </view>
- <view v-if="index < approvalSteps.length - 1"
- class="step-line"></view>
- </view>
- <view class="step-content">
- <view class="step-info">
- <text class="step-title">{{ step.title }}</text>
- <text class="step-approver">{{ step.approverName }}</text>
- <text v-if="step.approveTime"
- class="step-time">{{ step.approveTime }}</text>
- </view>
- <view v-if="step.opinion"
- class="step-opinion">
- <text class="opinion-label">瀹℃壒鎰忚锛�</text>
- <text class="opinion-content">{{ step.opinion }}</text>
- </view>
- <!-- 绛惧悕灞曠ず -->
- <view v-if="step.urlTem"
- class="step-opinion"
- style="margin-top:8px;">
- <text class="opinion-label">绛惧悕锛�</text>
- <image :src="step.urlTem"
- mode="widthFix"
- style="width:180px;border-radius:6px;border:1px solid #eee;" />
- </view>
- </view>
- </view>
- </view>
+ <!-- 閲囪喘/鎶ヤ环/鍙戣揣瀹℃壒璇︽儏 -->
+ <view v-if="isBusinessApproval"
+ class="business-detail-wrap">
+ <ApprovalBusinessDetail :approve-type="approvalData.approveType || approveType"
+ :business-no="approvalData.approveReason" />
</view>
+ <!-- 瀹℃壒娴佺▼ -->
+ <ApproveFlowSteps :steps="approvalSteps" />
<!-- 瀹℃牳鎰忚杈撳叆 -->
<view v-if="canApprove"
class="approval-input">
@@ -134,6 +90,9 @@
updateApproveNode,
} from "@/api/collaborativeApproval/approvalProcess";
import useUserStore from "@/store/modules/user";
+ import ApprovalBusinessDetail from "./_components/ApprovalBusinessDetail.vue";
+ import ApproveFlowSteps from "./_components/ApproveFlowSteps.vue";
+ import { mapApprovalSteps } from "./_utils/approveFlowSteps.js";
const showToast = message => {
uni.showToast({
title: message,
@@ -147,6 +106,14 @@
const approvalSteps = ref([]);
const approvalOpinion = ref("");
const approveId = ref("");
+ const approveType = ref(0);
+
+ /** 5 閲囪喘 / 6 鎶ヤ环 / 7 鍙戣揣锛氶渶灞曠ず涓氬姟鍗曟嵁璇︽儏 */
+ const isBusinessApproval = computed(() =>
+ [5, 6, 7].includes(
+ Number(approvalData.value.approveType || approveType.value)
+ )
+ );
// 浠庤鎯呮帴鍙e瓧娈靛榻� canApprove锛氫粎褰撴湁 isShen 鐨勮妭鐐规椂鍙鎵�
const canApprove = computed(() => {
@@ -155,6 +122,7 @@
onMounted(() => {
approveId.value = uni.getStorageSync("approveId");
+ approveType.value = Number(uni.getStorageSync("approveType")) || 0;
if (approveId.value) {
loadApprovalData();
}
@@ -170,23 +138,7 @@
const list = Array.isArray(res.data) ? res.data : [];
// 淇濆瓨鍘熷鑺傜偣鏁版嵁渚涙彁浜や娇鐢�
activities.value = list;
-
- approvalSteps.value = list.map((it, idx) => {
- // 鑺傜偣鐘舵�佹槧灏勶細1=閫氳繃锛�2=涓嶉�氳繃锛屽惁鍒欑湅鏄惁褰撳墠(isShen)锛屽啀榛樿涓哄緟澶勭悊
- let status = "pending";
- if (it.approveNodeStatus === 1) status = "completed";
- else if (it.approveNodeStatus === 2) status = "rejected";
- else if (it.isShen) status = "current";
- return {
- title: `绗�${idx + 1}姝ュ鎵筦,
- approverName: it.approveNodeUser || "鏈煡鐢ㄦ埛",
- status,
- approveTime: it.approveTime || null,
- opinion: it.approveNodeReason || "",
- urlTem: it.urlTem || "",
- isShen: !!it.isShen,
- };
- });
+ approvalSteps.value = mapApprovalSteps(list);
});
};
@@ -282,6 +234,14 @@
overflow: hidden;
}
+ .business-detail-wrap {
+ background: #fff;
+ margin: 16px;
+ border-radius: 12px;
+ overflow: hidden;
+ padding: 1px 0;
+ }
+
.info-header {
padding: 16px;
border-bottom: 1px solid #f0f0f0;
@@ -319,148 +279,6 @@
font-size: 14px;
color: #333;
flex: 1;
- }
-
- .approval-process {
- background: #fff;
- margin: 16px;
- border-radius: 12px;
- overflow: hidden;
- }
-
- .process-header {
- padding: 16px;
- border-bottom: 1px solid #f0f0f0;
- background: #f8f9fa;
- }
-
- .process-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- }
-
- .process-steps {
- padding: 20px;
- }
-
- .process-step {
- display: flex;
- position: relative;
- margin-bottom: 24px;
-
- &:last-child {
- margin-bottom: 0;
-
- .step-line {
- display: none;
- }
- }
- }
-
- .step-indicator {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-right: 16px;
- }
-
- .step-dot {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- font-weight: 600;
- position: relative;
- z-index: 2;
- }
-
- .process-step.completed .step-dot {
- background: #52c41a;
- color: #fff;
- }
-
- .process-step.current .step-dot {
- background: #1890ff;
- color: #fff;
- animation: pulse 2s infinite;
- }
-
- .process-step.pending .step-dot {
- background: #d9d9d9;
- color: #999;
- }
-
- .step-line {
- width: 2px;
- height: 40px;
- background: #d9d9d9;
- margin-top: 8px;
- }
-
- .process-step.completed .step-line {
- background: #52c41a;
- }
-
- .process-step.rejected .step-dot {
- background: #ff4d4f;
- color: #fff;
- }
- .process-step.rejected .step-line {
- background: #ff4d4f;
- }
-
- .step-content {
- flex: 1;
- padding-top: 4px;
- }
-
- .step-info {
- margin-bottom: 8px;
- }
-
- .step-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- display: block;
- margin-bottom: 4px;
- }
-
- .step-approver {
- font-size: 14px;
- color: #666;
- display: block;
- margin-bottom: 4px;
- }
-
- .step-time {
- font-size: 12px;
- color: #999;
- display: block;
- }
-
- .step-opinion {
- background: #f8f9fa;
- padding: 12px;
- border-radius: 8px;
- border-left: 4px solid #52c41a;
- }
-
- .opinion-label {
- font-size: 12px;
- color: #666;
- display: block;
- margin-bottom: 4px;
- }
-
- .opinion-content {
- font-size: 14px;
- color: #333;
- line-height: 1.5;
}
.approval-input {
@@ -517,17 +335,6 @@
border-radius: 6px;
}
- @keyframes pulse {
- 0% {
- box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
- }
- 70% {
- box-shadow: 0 0 0 10px rgba(24, 144, 255, 0);
- }
- 100% {
- box-shadow: 0 0 0 0 rgba(24, 144, 255, 0);
- }
- }
.signature-section {
background: #fff;
padding: 12px 16px 16px;
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/detail.vue b/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
index 16f9923..1c9eb5f 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
@@ -8,39 +8,38 @@
:rules="rules"
:model="form"
label-width="140rpx">
- <template v-if="operationType !== 'detail'">
- <u-form-item prop="approveReason"
- label="娴佺▼缂栧彿">
- <u-input v-model="form.approveId"
- disabled
- placeholder="鑷姩缂栧彿" />
- </u-form-item>
- <u-form-item prop="approveReason"
- :label="approveType === 5 ? '閲囪喘浜嬬敱' : '鐢宠浜嬬敱'"
- required>
- <u-input v-model="form.approveReason"
- type="textarea"
- rows="2"
- auto-height
- maxlength="200"
- :placeholder="approveType === 5 ? '璇疯緭鍏ラ噰璐簨鐢�' : '璇疯緭鍏ョ敵璇蜂簨鐢�'"
- show-word-limit />
- </u-form-item>
- <u-form-item prop="approveDeptName"
- label="鐢宠閮ㄩ棬"
- required>
- <!-- <u-input v-model="form.approveDeptName"
- placeholder="璇烽�夋嫨鐢宠閮ㄩ棬" /> -->
- <u-input v-model="form.approveDeptName"
- readonly
- placeholder="璇烽�夋嫨鐢宠閮ㄩ棬"
- @click="showPicker = true" />
- <template #right>
- <up-icon name="arrow-right"
- @click="showPicker = true"></up-icon>
- </template>
- </u-form-item>
- <!-- <u-form-item prop="approveUser"
+ <u-form-item prop="approveReason"
+ label="娴佺▼缂栧彿">
+ <u-input v-model="form.approveId"
+ disabled
+ placeholder="鑷姩缂栧彿" />
+ </u-form-item>
+ <u-form-item prop="approveReason"
+ :label="approveType === 5 ? '閲囪喘浜嬬敱' : '鐢宠浜嬬敱'"
+ required>
+ <u-input v-model="form.approveReason"
+ type="textarea"
+ rows="2"
+ auto-height
+ maxlength="200"
+ :disabled="isDetailMode"
+ :placeholder="approveType === 5 ? '璇疯緭鍏ラ噰璐簨鐢�' : '璇疯緭鍏ョ敵璇蜂簨鐢�'"
+ show-word-limit />
+ </u-form-item>
+ <u-form-item prop="approveDeptName"
+ label="鐢宠閮ㄩ棬"
+ required>
+ <u-input v-model="form.approveDeptName"
+ readonly
+ :disabled="isDetailMode"
+ placeholder="璇烽�夋嫨鐢宠閮ㄩ棬"
+ @click="isDetailMode || (showPicker = true)" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="isDetailMode || (showPicker = true)"></up-icon>
+ </template>
+ </u-form-item>
+ <!-- <u-form-item prop="approveUser"
label="鐢宠浜�"
required>
<u-input v-model="form.approveUserName"
@@ -59,228 +58,91 @@
@click="showDatePicker"></up-icon>
</template>
</u-form-item> -->
- <!-- approveType=2 璇峰亣鐩稿叧瀛楁 -->
- <template v-if="approveType === 2">
- <u-form-item prop="startDate"
- label="寮�濮嬫椂闂�"
- required>
- <u-input v-model="form.startDate"
- readonly
- placeholder="璇峰亣寮�濮嬫椂闂�"
- @click="showStartDatePicker" />
- <template #right>
- <up-icon name="arrow-right"
- @click="showStartDatePicker"></up-icon>
- </template>
- </u-form-item>
- <u-form-item prop="endDate"
- label="缁撴潫鏃堕棿"
- required>
- <u-input v-model="form.endDate"
- readonly
- placeholder="璇峰亣缁撴潫鏃堕棿"
- @click="showEndDatePicker" />
- <template #right>
- <up-icon name="arrow-right"
- @click="showEndDatePicker"></up-icon>
- </template>
- </u-form-item>
+ <!-- approveType=2 璇峰亣鐩稿叧瀛楁 -->
+ <u-form-item v-if="approveType === 2"
+ prop="startDate"
+ label="寮�濮嬫椂闂�"
+ required>
+ <u-input v-model="form.startDate"
+ readonly
+ :disabled="isDetailMode"
+ placeholder="璇峰亣寮�濮嬫椂闂�"
+ @click="showStartDatePicker" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showStartDatePicker"></up-icon>
</template>
- <!-- approveType=3 鍑哄樊鐩稿叧瀛楁 -->
- <u-form-item v-if="approveType === 3"
- prop="location"
- label="鍑哄樊鍦扮偣"
- required>
- <u-input v-model="form.location"
- placeholder="璇疯緭鍏ュ嚭宸湴鐐�"
- clearable />
- </u-form-item>
- <!-- approveType=4 鎶ラ攢鐩稿叧瀛楁 -->
- <u-form-item v-if="approveType === 4"
- prop="price"
- label="鎶ラ攢閲戦"
- required>
- <u-input v-model="form.price"
- type="number"
- placeholder="璇疯緭鍏ユ姤閿�閲戦"
- clearable />
- </u-form-item>
- </template>
- <!-- 鎶ヤ环瀹℃壒璇︽儏 -->
- <view v-if="isQuotationApproval"
- style="margin: 20rpx 0;">
- <u-divider text="鎶ヤ环璇︽儏"
- text-size="28rpx"
- color="#2979ff"></u-divider>
- <u-skeleton :loading="quotationLoading"
- rows="3"
- animated>
- <view v-if="!currentQuotation || !currentQuotation.quotationNo"
- style="padding: 40rpx; text-align: center; color: #999;">
- 鏈煡璇㈠埌瀵瑰簲鎶ヤ环璇︽儏
- </view>
- <view v-else>
- <u-cell-group :border="false">
- <u-cell title="鎶ヤ环鍗曞彿"
- :value="currentQuotation.quotationNo"></u-cell>
- <u-cell title="瀹㈡埛鍚嶇О"
- :value="currentQuotation.customer"></u-cell>
- <u-cell title="涓氬姟鍛�"
- :value="currentQuotation.salesperson"></u-cell>
- <u-cell title="鎶ヤ环鏃ユ湡"
- :value="currentQuotation.quotationDate"></u-cell>
- <u-cell title="鏈夋晥鏈熻嚦"
- :value="currentQuotation.validDate"></u-cell>
- <u-cell title="浠樻鏂瑰紡"
- :value="currentQuotation.paymentMethod"></u-cell>
- <u-cell title="鎶ヤ环鎬婚">
- <template #value>
- <text style="font-size: 32rpx; color: #e6a23c; font-weight: bold;">
- 楼{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }}
- </text>
- </template>
- </u-cell>
- </u-cell-group>
- <view style="margin-top: 20rpx; padding: 0 30rpx;">
- <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">浜у搧鏄庣粏</view>
- <view v-for="(item, index) in (currentQuotation.products || [])"
- :key="index"
- style="background: #f8f8f8; border-radius: 8rpx; padding: 20rpx; margin-bottom: 10rpx;">
- <view style="display: flex; justify-content: space-between;">
- <text style="font-weight: bold;">{{ item.product }}</text>
- <text style="color: #e6a23c;">楼{{ Number(item.unitPrice ?? 0).toFixed(2) }}</text>
- </view>
- <view style="font-size: 24rpx; color: #666; margin-top: 10rpx;">
- 瑙勬牸: {{ item.specification }} | 鍗曚綅: {{ item.unit }}
- </view>
- </view>
- </view>
- <view v-if="currentQuotation.remark"
- style="margin-top: 20rpx; padding: 0 30rpx;">
- <view style="font-size: 28rpx; font-weight: bold;">澶囨敞</view>
- <view style="font-size: 26rpx; color: #666; margin-top: 10rpx;">{{ currentQuotation.remark }}</view>
- </view>
- </view>
- </u-skeleton>
- </view>
- <!-- 閲囪喘瀹℃壒璇︽儏 -->
- <view v-if="isPurchaseApproval"
- style="margin: 20rpx 0;">
- <u-divider text="閲囪喘璇︽儏"
- text-size="28rpx"
- color="#2979ff"></u-divider>
- <u-skeleton :loading="purchaseLoading"
- rows="3"
- animated>
- <view v-if="!currentPurchase || !currentPurchase.purchaseContractNumber"
- style="padding: 40rpx; text-align: center; color: #999;">
- 鏈煡璇㈠埌瀵瑰簲閲囪喘璇︽儏
- </view>
- <view v-else>
- <u-cell-group :border="false">
- <u-cell title="閲囪喘鍚堝悓鍙�"
- :value="currentPurchase.purchaseContractNumber"></u-cell>
- <u-cell title="渚涘簲鍟嗗悕绉�"
- :value="currentPurchase.supplierName"></u-cell>
- <u-cell title="椤圭洰鍚嶇О"
- :value="currentPurchase.projectName"></u-cell>
- <u-cell title="閿�鍞悎鍚屽彿"
- :value="currentPurchase.salesContractNo"></u-cell>
- <u-cell title="绛捐鏃ユ湡"
- :value="currentPurchase.executionDate"></u-cell>
- <u-cell title="褰曞叆鏃ユ湡"
- :value="currentPurchase.entryDate"></u-cell>
- <u-cell title="浠樻鏂瑰紡"
- :value="currentPurchase.paymentMethod"></u-cell>
- <u-cell title="鍚堝悓閲戦">
- <template #value>
- <text style="font-size: 32rpx; color: #e6a23c; font-weight: bold;">
- 楼{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }}
- </text>
- </template>
- </u-cell>
- </u-cell-group>
- <view style="margin-top: 20rpx; padding: 0 30rpx;">
- <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">浜у搧鏄庣粏</view>
- <view v-for="(item, index) in (currentPurchase.productData || [])"
- :key="index"
- style="background: #f8f8f8; border-radius: 8rpx; padding: 20rpx; margin-bottom: 10rpx;">
- <view style="display: flex; justify-content: space-between;">
- <text style="font-weight: bold;">{{ item.productCategory }}</text>
- <text style="color: #e6a23c;">楼{{ Number(item.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</text>
- </view>
- <view style="font-size: 24rpx; color: #666; margin-top: 10rpx;">
- 瑙勬牸: {{ item.specificationModel }} | 鏁伴噺: {{ item.quantity }} {{ item.unit }}
- </view>
- <view style="font-size: 24rpx; color: #999; margin-top: 4rpx;">
- 鍚◣鍗曚环: 楼{{ Number(item.taxInclusiveUnitPrice ?? 0).toFixed(2) }}
- </view>
- </view>
- </view>
- </view>
- </u-skeleton>
- </view>
- <!-- 鍙戣揣瀹℃壒璇︽儏 -->
- <view v-if="isDeliveryApproval"
- style="margin: 20rpx 0;">
- <u-divider text="鍙戣揣璇︽儏"
- text-size="28rpx"
- color="#2979ff"></u-divider>
- <u-skeleton :loading="deliveryLoading"
- rows="3"
- animated>
- <view v-if="!currentDelivery || !currentDelivery.shippingInfo"
- style="padding: 40rpx; text-align: center; color: #999;">
- 鏈煡璇㈠埌瀵瑰簲鍙戣揣璇︽儏
- </view>
- <view v-else>
- <u-cell-group :border="false">
- <u-cell title="閿�鍞鍗�"
- :value="currentDelivery.shippingInfo.salesContractNo || '--'"></u-cell>
- <u-cell title="鍙戣揣璁㈠崟鍙�"
- :value="currentDelivery.shippingInfo.shippingNo || '--'"></u-cell>
- <u-cell title="瀹㈡埛鍚嶇О"
- :value="currentDelivery.shippingInfo.customerName || '--'"></u-cell>
- <u-cell title="鍙戣揣绫诲瀷"
- :value="currentDelivery.shippingInfo.type || '--'"></u-cell>
- <u-cell title="鍙戣揣鏃ユ湡"
- :value="currentDelivery.shippingInfo.shippingDate || '--'"></u-cell>
- <u-cell title="瀹℃牳鐘舵��"
- :value="currentDelivery.shippingInfo.status || '--'"></u-cell>
- <u-cell title="鍙戣揣杞︾墝鍙�"
- :value="currentDelivery.shippingInfo.shippingCarNumber || '--'"></u-cell>
- <u-cell title="蹇�掑叕鍙�"
- :value="currentDelivery.shippingInfo.expressCompany || '--'"></u-cell>
- <u-cell title="蹇�掑崟鍙�"
- :value="currentDelivery.shippingInfo.expressNumber || '--'"></u-cell>
- </u-cell-group>
- <view style="margin-top: 20rpx; padding: 0 30rpx;">
- <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">浜у搧鏄庣粏</view>
- <view v-for="(item, index) in deliveryProductList"
- :key="index"
- style="background: #f8f8f8; border-radius: 8rpx; padding: 20rpx; margin-bottom: 10rpx;">
- <view style="display: flex; justify-content: space-between;">
- <text style="font-weight: bold;">{{ item.productName }}</text>
- <text style="color: #2979ff;">鏁伴噺: {{ item.deliveryQuantity }}</text>
- </view>
- <view style="font-size: 24rpx; color: #666; margin-top: 10rpx;">
- 瑙勬牸: {{ item.specificationModel }}
- </view>
- <view v-if="item.batchNo"
- style="font-size: 24rpx; color: #999; margin-top: 4rpx;">
- 鎵瑰彿: {{ item.batchNo }}
- </view>
- </view>
- </view>
- <view v-if="currentDelivery.shippingInfo.storageBlobVOs && currentDelivery.shippingInfo.storageBlobVOs.length"
- style="margin-top: 20rpx; padding: 0 30rpx;">
- <view style="font-size: 28rpx; font-weight: bold; margin-bottom: 10rpx;">鍙戣揣鍥剧墖</view>
- <CommonUpload :model-value="currentDelivery.shippingInfo.storageBlobVOs"
- disabled />
- </view>
- </view>
- </u-skeleton>
- </view>
+ </u-form-item>
+ <u-form-item v-if="approveType === 2"
+ prop="endDate"
+ label="缁撴潫鏃堕棿"
+ required>
+ <u-input v-model="form.endDate"
+ readonly
+ :disabled="isDetailMode"
+ placeholder="璇峰亣缁撴潫鏃堕棿"
+ @click="showEndDatePicker" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showEndDatePicker"></up-icon>
+ </template>
+ </u-form-item>
+ <!-- approveType=3 鍑哄樊鐩稿叧瀛楁 -->
+ <u-form-item v-if="approveType === 3"
+ prop="startDateTime"
+ label="鍑哄樊寮�濮嬫椂闂�"
+ required>
+ <u-input v-model="form.startDateTime"
+ readonly
+ :disabled="isDetailMode"
+ placeholder="璇烽�夋嫨寮�濮嬫椂闂�"
+ @click="showStartDateTimePicker" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showStartDateTimePicker"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item v-if="approveType === 3"
+ prop="endDateTime"
+ label="鍑哄樊缁撴潫鏃堕棿"
+ required>
+ <u-input v-model="form.endDateTime"
+ readonly
+ :disabled="isDetailMode"
+ placeholder="璇烽�夋嫨缁撴潫鏃堕棿"
+ @click="showEndDateTimePicker" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showEndDateTimePicker"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item v-if="approveType === 3"
+ prop="location"
+ label="鍑哄樊鍦扮偣"
+ required>
+ <u-input v-model="form.location"
+ :disabled="isDetailMode"
+ placeholder="璇疯緭鍏ュ嚭宸湴鐐�"
+ clearable />
+ </u-form-item>
+ <!-- approveType=4 鎶ラ攢鐩稿叧瀛楁 -->
+ <u-form-item v-if="approveType === 4"
+ prop="price"
+ label="鎶ラ攢閲戦"
+ required>
+ <u-input v-model="form.price"
+ type="number"
+ :disabled="isDetailMode"
+ placeholder="璇疯緭鍏ユ姤閿�閲戦"
+ clearable />
+ </u-form-item>
+ <!-- 閲囪喘/鎶ヤ环/鍙戣揣瀹℃壒璇︽儏 -->
+ <ApprovalBusinessDetail :approve-type="approveType"
+ :business-no="form.approveReason" />
+ <!-- 瀹℃壒娴佺▼锛堜粎璇︽儏妯″紡灞曠ず锛屼笌 Web 涓�鑷达級 -->
+ <ApproveFlowSteps v-if="isDetailMode"
+ :steps="approvalSteps"
+ show-pending-opinion />
<u-form-item v-if="operationType !== 'detail'"
label="鍥剧墖闄勪欢"
prop="storageBlobDTOS"
@@ -289,7 +151,7 @@
</u-form-item>
</u-form>
<!-- 閫夋嫨鍣ㄥ脊绐� -->
- <template v-if="operationType !== 'detail'">
+ <view v-if="operationType !== 'detail'">
<up-action-sheet :show="showPicker"
:actions="productOptions"
title="閫夋嫨閮ㄩ棬"
@@ -325,7 +187,27 @@
@cancel="showEndDate = false"
mode="date" />
</up-popup>
- </template>
+ <!-- 鍑哄樊寮�濮嬫椂闂撮�夋嫨鍣� -->
+ <up-popup :show="showStartDateTime"
+ mode="bottom"
+ @close="showStartDateTime = false">
+ <up-datetime-picker :show="true"
+ v-model="startDateTimeValue"
+ @confirm="onStartDateTimeConfirm"
+ @cancel="showStartDateTime = false"
+ mode="datetime" />
+ </up-popup>
+ <!-- 鍑哄樊缁撴潫鏃堕棿閫夋嫨鍣� -->
+ <up-popup :show="showEndDateTime"
+ mode="bottom"
+ @close="showEndDateTime = false">
+ <up-datetime-picker :show="true"
+ v-model="endDateTimeValue"
+ @confirm="onEndDateTimeConfirm"
+ @cancel="showEndDateTime = false"
+ mode="datetime" />
+ </up-popup>
+ </view>
<!-- 搴曢儴鎸夐挳 -->
<view class="footer-btns"
v-if="operationType !== 'detail'">
@@ -338,28 +220,21 @@
</template>
<script setup>
- import {
- ref,
- onMounted,
- onUnmounted,
- reactive,
- toRefs,
- computed,
- watch,
- } from "vue";
+ import { ref, onMounted, onUnmounted, reactive, toRefs, computed } from "vue";
import PageHeader from "@/components/PageHeader.vue";
import CommonUpload from "@/components/CommonUpload.vue";
+ import ApprovalBusinessDetail from "./_components/ApprovalBusinessDetail.vue";
+ import ApproveFlowSteps from "./_components/ApproveFlowSteps.vue";
+ import { mapApprovalSteps } from "./_utils/approveFlowSteps.js";
import useUserStore from "@/store/modules/user";
- import { formatDateToYMD } from "@/utils/ruoyi";
+ import { formatDateToYMD, parseTime } from "@/utils/ruoyi";
import {
getDept,
approveProcessGetInfo,
approveProcessAdd,
approveProcessUpdate,
- getDeliveryDetailByShippingNo,
+ approveProcessDetails,
} from "@/api/collaborativeApproval/approvalProcess";
- import { getQuotationList } from "@/api/salesManagement/salesQuotation";
- import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger";
const showToast = message => {
uni.showToast({
title: message,
@@ -380,6 +255,8 @@
storageBlobDTOS: [],
startDate: "",
endDate: "",
+ startDateTime: "",
+ endDateTime: "",
location: "",
price: "",
},
@@ -395,6 +272,12 @@
endDate: [
{ required: false, message: "璇烽�夋嫨缁撴潫鏃堕棿", trigger: "change" },
],
+ startDateTime: [
+ { required: false, message: "璇烽�夋嫨鍑哄樊寮�濮嬫椂闂�", trigger: "change" },
+ ],
+ endDateTime: [
+ { required: false, message: "璇烽�夋嫨鍑哄樊缁撴潫鏃堕棿", trigger: "change" },
+ ],
location: [{ required: false, message: "璇疯緭鍏ュ嚭宸湴鐐�", trigger: "blur" }],
price: [{ required: false, message: "璇疯緭鍏ユ姤閿�閲戦", trigger: "blur" }],
},
@@ -404,6 +287,21 @@
const showPicker = ref(false);
const productOptions = ref([]);
const operationType = ref("");
+ const isDetailMode = computed(() => operationType.value === "detail");
+
+ /** 瀹℃壒娴佺▼鑺傜偣锛堜粎璇︽儏妯″紡灞曠ず锛� */
+ const approvalSteps = ref([]);
+
+ const loadApprovalSteps = approveIdValue => {
+ if (!approveIdValue) return;
+ approveProcessDetails(approveIdValue)
+ .then(res => {
+ approvalSteps.value = mapApprovalSteps(res?.data);
+ })
+ .catch(() => {
+ approvalSteps.value = [];
+ });
+ };
const currentApproveStatus = ref("");
const formRef = ref(null);
const message = ref("");
@@ -413,21 +311,12 @@
const startDateValue = ref(Date.now());
const showEndDate = ref(false);
const endDateValue = ref(Date.now());
+ const showStartDateTime = ref(false);
+ const startDateTimeValue = ref(Date.now());
+ const showEndDateTime = ref(false);
+ const endDateTimeValue = ref(Date.now());
const userStore = useUserStore();
const approveType = ref(0);
- const isInitialLoading = ref(false);
-
- const quotationLoading = ref(false);
- const currentQuotation = ref({});
- const purchaseLoading = ref(false);
- const currentPurchase = ref({});
- const deliveryLoading = ref(false);
- const currentDelivery = ref({});
- const deliveryProductList = ref([]);
-
- const isQuotationApproval = computed(() => Number(approveType.value) === 6);
- const isPurchaseApproval = computed(() => Number(approveType.value) === 5);
- const isDeliveryApproval = computed(() => Number(approveType.value) === 7);
const getProductOptions = () => {
getDept().then(res => {
@@ -446,6 +335,7 @@
// 鏄剧ず鏃ユ湡閫夋嫨鍣�
const showDatePicker = () => {
+ if (isDetailMode.value) return;
showDate.value = true;
};
@@ -458,6 +348,7 @@
// 鏄剧ず璇峰亣寮�濮嬫椂闂撮�夋嫨鍣�
const showStartDatePicker = () => {
+ if (isDetailMode.value) return;
showStartDate.value = true;
};
@@ -468,6 +359,7 @@
};
const showEndDatePicker = () => {
+ if (isDetailMode.value) return;
showEndDate.value = true;
};
@@ -477,88 +369,38 @@
showEndDate.value = false;
};
- const fetchDetailData = async row => {
- // 鎶ヤ环瀹℃壒
- if (isQuotationApproval.value) {
- const quotationNo = row?.approveReason;
- if (quotationNo) {
- quotationLoading.value = true;
- getQuotationList({ quotationNo })
- .then(res => {
- const records = res?.data?.records || [];
- currentQuotation.value = records[0] || {};
- })
- .finally(() => {
- quotationLoading.value = false;
- });
- }
- }
-
- // 閲囪喘瀹℃壒
- if (isPurchaseApproval.value) {
- const purchaseContractNumber = row?.approveReason;
- if (purchaseContractNumber) {
- purchaseLoading.value = true;
- getPurchaseByCode({ purchaseContractNumber })
- .then(res => {
- currentPurchase.value = res;
- })
- .catch(err => {
- console.error("鏌ヨ閲囪喘璇︽儏澶辫触:", err);
- })
- .finally(() => {
- purchaseLoading.value = false;
- });
- }
- }
-
- // 鍙戣揣瀹℃壒
- if (isDeliveryApproval.value) {
- const deliveryNo = row?.approveReason;
- if (deliveryNo) {
- deliveryLoading.value = true;
- currentDelivery.value = {};
- deliveryProductList.value = [];
- getDeliveryDetailByShippingNo({ shippingNo: deliveryNo })
- .then(res => {
- const detailData = res?.data || res || {};
- currentDelivery.value = detailData;
- deliveryProductList.value =
- detailData.shippingProductDetailDtoList || [];
- })
- .catch(err => {
- console.error("鏌ヨ鍙戣揣璇︽儏澶辫触:", err);
- })
- .finally(() => {
- deliveryLoading.value = false;
- });
- }
- }
+ // 鏄剧ず鍑哄樊寮�濮嬫椂闂撮�夋嫨鍣紙YYYY-MM-DD HH:mm锛屼笌 Web 涓�鑷达級
+ const showStartDateTimePicker = () => {
+ if (isDetailMode.value) return;
+ startDateTimeValue.value = parseTime(
+ form.value.startDateTime,
+ "{y}-{m}-{d} {h}:{i}"
+ )
+ ? new Date(form.value.startDateTime).getTime()
+ : Date.now();
+ showStartDateTime.value = true;
};
- // 鐩戝惉瀹℃壒浜嬬敱鍙樺寲锛屽鏋滄槸鐗瑰畾瀹℃壒绫诲瀷鍒欏皾璇曡幏鍙栬鎯�
- watch(
- () => form.value.approveReason,
- newVal => {
- if (isInitialLoading.value) return;
- if (
- newVal &&
- (isQuotationApproval.value ||
- isPurchaseApproval.value ||
- isDeliveryApproval.value)
- ) {
- // 寤惰繜涓�浼氬啀璇锋眰锛岄伩鍏嶈緭鍏ヨ繃绋嬩腑棰戠箒瑙﹀彂
- debounceFetchDetail();
- }
- }
- );
+ const onStartDateTimeConfirm = e => {
+ form.value.startDateTime = parseTime(e.value, "{y}-{m}-{d} {h}:{i}");
+ showStartDateTime.value = false;
+ };
- let timer = null;
- const debounceFetchDetail = () => {
- if (timer) clearTimeout(timer);
- timer = setTimeout(() => {
- fetchDetailData(form.value);
- }, 800);
+ // 鏄剧ず鍑哄樊缁撴潫鏃堕棿閫夋嫨鍣�
+ const showEndDateTimePicker = () => {
+ if (isDetailMode.value) return;
+ endDateTimeValue.value = parseTime(
+ form.value.endDateTime,
+ "{y}-{m}-{d} {h}:{i}"
+ )
+ ? new Date(form.value.endDateTime).getTime()
+ : Date.now();
+ showEndDateTime.value = true;
+ };
+
+ const onEndDateTimeConfirm = e => {
+ form.value.endDateTime = parseTime(e.value, "{y}-{m}-{d} {h}:{i}");
+ showEndDateTime.value = false;
};
onMounted(async () => {
@@ -579,9 +421,13 @@
const row = JSON.parse(storedData);
currentApproveStatus.value = row.approveStatus;
- isInitialLoading.value = true;
- approveProcessGetInfo({ id: row.approveId, approveReason: "1" })
- .then(res => {
+ // 璇︽儏妯″紡锛氭媺鍙栧鎵规祦绋嬭妭鐐�
+ if (operationType.value === "detail") {
+ loadApprovalSteps(row.approveId);
+ }
+
+ approveProcessGetInfo({ id: row.approveId, approveReason: "1" }).then(
+ res => {
form.value = { ...res.data };
// 璁剧疆鍥剧墖鍒楄〃鏄剧ず
const fileData =
@@ -589,15 +435,8 @@
if (fileData.length > 0) {
form.value.storageBlobDTOS = fileData;
}
- // 鑾峰彇棰濆璇︽儏
- fetchDetailData(res.data);
- })
- .finally(() => {
- // 寤惰繜涓�浼氶噸缃紝纭繚 watch 涓嶄細琚Е鍙�
- setTimeout(() => {
- isInitialLoading.value = false;
- }, 100);
- });
+ }
+ );
}
}
} catch (error) {
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/index.vue b/src/pages/cooperativeOffice/collaborativeApproval/index.vue
index 910cdc3..dddd75a 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/index.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/index.vue
@@ -4,6 +4,14 @@
<!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
<PageHeader :title="pageTitle"
@back="goBack" />
+ <!-- 瀹℃壒绫诲瀷鍒囨崲锛堜粠鑿滃崟鐩存帴杩涘叆鏃跺彲鍒囨崲锛涚粡 index1~8 杩涘叆鏃朵笉灞曠ず锛� -->
+ <view v-if="showTypeTabs"
+ class="type-tabs-wrap">
+ <up-tabs :list="typeTabList"
+ :current="activeTypeIndex"
+ line-color="#ed8d05"
+ @click="onTypeTabClick" />
+ </view>
<!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
<view class="search-section">
<view class="search-bar">
@@ -13,6 +21,14 @@
v-model="searchForm.approveId"
clearable />
</view>
+ <view class="filter-chip"
+ :class="{ 'filter-chip--active': searchForm.approveStatus !== '' }"
+ @click="openStatusSheet">
+ <text class="filter-chip-text">{{ approveStatusText }}</text>
+ <up-icon name="arrow-down"
+ size="14"
+ color="#999"></up-icon>
+ </view>
<view class="search-button"
@click="getList">
<up-icon name="search"
@@ -21,6 +37,11 @@
</view>
</view>
</view>
+ <up-action-sheet :show="showStatusSheet"
+ title="瀹℃壒鐘舵��"
+ :actions="statusActions"
+ @select="onSelectStatus"
+ @close="showStatusSheet = false" />
<!-- 瀹℃壒鍒楄〃 -->
<view class="ledger-list"
v-if="ledgerList.length > 0">
@@ -105,7 +126,6 @@
缂栬緫
</u-button>
<u-button type="info"
- v-if="item.approveType == 5 || item.approveType == 6 || item.approveType == 7"
size="small"
class="action-btn detail"
@click="handleDetailClick(item)">
@@ -131,7 +151,7 @@
</view>
<!-- 娴姩鎿嶄綔鎸夐挳 -->
<view class="fab-button"
- v-if="props.approveType != 5 && props.approveType != 6 && props.approveType != 7"
+ v-if="activeApproveType != 5 && activeApproveType != 6 && activeApproveType != 7"
@click="handleAdd">
<up-icon name="plus"
size="24"
@@ -141,13 +161,23 @@
</template>
<script setup>
- import { ref, toRefs, reactive } from "vue";
+ import { ref, toRefs, reactive, computed } from "vue";
import PageHeader from "@/components/PageHeader.vue";
import { approveProcessListPage } from "@/api/collaborativeApproval/approvalProcess";
import { onLoad, onShow } from "@dcloudio/uni-app";
import useUserStore from "@/store/modules/user";
- // 鎺ユ敹鐖剁粍浠朵紶閫掔殑 approveType 鍙傛暟
+ /** 瀹℃壒鐘舵�佺瓫閫夛紙涓� Web approvalProcess 鍒楄〃涓�鑷达級 */
+ const APPROVE_STATUS_OPTIONS = [
+ { value: "", label: "鍏ㄩ儴鐘舵��" },
+ { value: 0, label: "寰呭鏍�" },
+ { value: 1, label: "瀹℃牳涓�" },
+ { value: 2, label: "瀹℃牳瀹屾垚" },
+ { value: 3, label: "瀹℃牳鏈�氳繃" },
+ { value: 4, label: "宸查噸鏂版彁浜�" },
+ ];
+
+ // 鎺ユ敹鐖剁粍浠朵紶閫掔殑 approveType 鍙傛暟锛坕ndex1~8 鍥哄畾绫诲瀷锛�0 琛ㄧず鏈寚瀹氾級
const props = defineProps({
approveType: {
type: Number,
@@ -155,22 +185,50 @@
},
});
- // 鏄犲皠 approveType 鍒板搴旂殑椤甸潰鏍囬
- const getPageTitle = type => {
- const titleMap = {
- 1: "鍏嚭绠$悊",
- 2: "璇峰亣绠$悊",
- 3: "鍑哄樊绠$悊",
- 4: "鎶ラ攢绠$悊",
- 5: "閲囪喘绠$悊",
- 6: "鎶ヤ环绠$悊",
- 7: "鍙戣揣瀹℃壒",
- 8: "鍗遍櫓浣滀笟瀹℃壒",
- };
- return titleMap[type] || "瀹℃壒绠$悊";
+ // 瀹℃壒绫诲瀷鏍囬
+ const APPROVE_TYPE_LABELS = {
+ 1: "鍏嚭绠$悊",
+ 2: "璇峰亣绠$悊",
+ 3: "鍑哄樊绠$悊",
+ 4: "鎶ラ攢绠$悊",
+ 5: "閲囪喘瀹℃壒",
+ 6: "鎶ヤ环瀹℃壒",
+ 7: "鍙戣揣瀹℃壒",
+ 8: "鍗遍櫓浣滀笟瀹℃壒",
};
- const pageTitle = getPageTitle(props.approveType);
+ /** 鍙垏鎹㈢殑鍗忓悓瀹℃壒绫诲瀷锛堜笌 Web approvalProcess Tab 涓�鑷达級 */
+ const TYPE_TAB_VALUES = [1, 2, 3, 4, 5, 6, 7];
+
+ /** 鐖剁骇浼犲叆鐨勫浐瀹氱被鍨嬶紱0 琛ㄧず鏈寚瀹氾紝鍙敱椤靛唴 Tab 鍒囨崲 */
+ const fixedApproveType = computed(() => Number(props.approveType) || 0);
+ const activeApproveType = ref(fixedApproveType.value || 1);
+
+ const showTypeTabs = computed(() => fixedApproveType.value === 0);
+
+ const typeTabList = computed(() =>
+ TYPE_TAB_VALUES.map(value => ({ name: APPROVE_TYPE_LABELS[value] }))
+ );
+
+ const activeTypeIndex = computed(() => {
+ const idx = TYPE_TAB_VALUES.indexOf(activeApproveType.value);
+ return idx >= 0 ? idx : 0;
+ });
+
+ const pageTitle = computed(
+ () => APPROVE_TYPE_LABELS[activeApproveType.value] || "鍗忓悓瀹℃壒"
+ );
+
+ const onTypeTabClick = item => {
+ activeApproveType.value = TYPE_TAB_VALUES[item?.index ?? 0] || 1;
+ searchForm.value.approveId = "";
+ searchForm.value.approveStatus = "";
+ getList();
+ };
+
+ /** 鍒楄〃琛岃嚜甯� approveType锛岀己澶辨椂鍥為��褰撳墠 Tab */
+ const currentItemApproveType = item =>
+ Number(item?.approveType) || activeApproveType.value;
const userStore = useUserStore();
// 鏁版嵁
@@ -178,9 +236,33 @@
const data = reactive({
searchForm: {
approveId: "",
+ approveStatus: "",
},
});
const { searchForm } = toRefs(data);
+
+ const showStatusSheet = ref(false);
+
+ const statusActions = computed(() =>
+ APPROVE_STATUS_OPTIONS.map(opt => ({ name: opt.label, value: opt.value }))
+ );
+
+ const approveStatusText = computed(() => {
+ const hit = APPROVE_STATUS_OPTIONS.find(
+ opt => String(opt.value) === String(searchForm.value.approveStatus)
+ );
+ return hit?.label || "鍏ㄩ儴鐘舵��";
+ });
+
+ const openStatusSheet = () => {
+ showStatusSheet.value = true;
+ };
+
+ const onSelectStatus = action => {
+ searchForm.value.approveStatus = action?.value ?? "";
+ showStatusSheet.value = false;
+ getList();
+ };
// 杩斿洖涓婁竴椤�
const goBack = () => {
@@ -195,7 +277,7 @@
};
approveProcessListPage({
...page,
- approveType: props.approveType,
+ approveType: activeApproveType.value,
...searchForm.value,
})
.then(res => {
@@ -263,7 +345,7 @@
uni.setStorageSync("invoiceLedgerEditRow", JSON.stringify(item));
uni.setStorageSync("operationType", "edit");
uni.setStorageSync("approveId", item.approveId);
- uni.setStorageSync("approveType", props.approveType);
+ uni.setStorageSync("approveType", currentItemApproveType(item));
uni.navigateTo({
url: "/pages/cooperativeOffice/collaborativeApproval/detail",
});
@@ -274,7 +356,7 @@
uni.setStorageSync("invoiceLedgerEditRow", JSON.stringify(item));
uni.setStorageSync("operationType", "detail");
uni.setStorageSync("approveId", item.approveId);
- uni.setStorageSync("approveType", props.approveType);
+ uni.setStorageSync("approveType", currentItemApproveType(item));
uni.navigateTo({
url: "/pages/cooperativeOffice/collaborativeApproval/detail",
});
@@ -283,23 +365,27 @@
// 娣诲姞鏂拌褰�
const handleAdd = () => {
uni.setStorageSync("operationType", "add");
- uni.setStorageSync("approveType", props.approveType);
+ uni.setStorageSync("approveType", activeApproveType.value);
uni.navigateTo({
- url: `/pages/cooperativeOffice/collaborativeApproval/detail?approveType=${props.approveType}`,
+ url: `/pages/cooperativeOffice/collaborativeApproval/detail?approveType=${activeApproveType.value}`,
});
};
// 鐐瑰嚮瀹℃牳
const approve = item => {
+ const type = currentItemApproveType(item);
uni.setStorageSync("approveId", item.approveId);
- uni.setStorageSync("approveType", props.approveType);
+ uni.setStorageSync("approveType", type);
uni.navigateTo({
url:
"/pages/cooperativeOffice/collaborativeApproval/approve?approveType=" +
- props.approveType,
+ type,
});
};
onLoad(options => {
+ // index1~8 浼犲浐瀹氱被鍨嬶紱鑿滃崟鐩存帴杩涘叆鏃跺彲鐢� ?approveType= 鎸囧畾锛岄粯璁ゅ叕鍑虹鐞�
+ activeApproveType.value =
+ Number(options?.approveType) || fixedApproveType.value || 1;
// 瑙f瀽approveId
if (options.approveId) {
searchForm.value.approveId = options.approveId;
@@ -319,11 +405,49 @@
margin: 0 !important;
}
+ // 瀹℃壒绫诲瀷鍒囨崲
+ .type-tabs-wrap {
+ background: #fff;
+ border-bottom: 1px solid #f0f0f0;
+ padding: 0 8px;
+ }
+
// 鏂囨。鍥炬爣鏍峰紡 - 瑕嗙洊鍏叡鏍峰紡涓殑鑳屾櫙鑹�
.document-icon {
background: #ed8d05;
}
+ // 鐘舵�佺瓫閫� chip
+ .filter-chip {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ flex-shrink: 0;
+ margin-left: 8px;
+ padding: 6px 10px;
+ background: #fff;
+ border: 1px solid #e5e7eb;
+ border-radius: 16px;
+
+ &--active {
+ border-color: #2979ff;
+ background: #f0f6ff;
+ }
+ }
+
+ .filter-chip-text {
+ font-size: 12px;
+ color: #606266;
+ max-width: 88px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .filter-chip--active .filter-chip-text {
+ color: #2979ff;
+ }
+
// 娴姩鎸夐挳鏍峰紡 - 瑕嗙洊鍏叡鏍峰紡涓殑鑳屾櫙鑹�
.fab-button {
background: #ed8d05;
diff --git a/src/pages/indexItem.vue b/src/pages/indexItem.vue
index 6d04822..bcddb69 100644
--- a/src/pages/indexItem.vue
+++ b/src/pages/indexItem.vue
@@ -15,7 +15,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -37,62 +38,64 @@
// 璺敱鏄犲皠閰嶇疆
const routeMapping = {
- "閿�鍞彴璐�": "/pages/sales/salesAccount/index",
- "寮�绁ㄧ櫥璁�": "/pages/sales/invoicingRegistration/index",
- "寮�绁ㄥ彴璐�": "/pages/sales/invoiceLedger/index",
- "鍥炴鐧昏": "/pages/sales/receiptPayment/index",
- "鍥炴娴佹按": "/pages/sales/receiptPaymentHistory/index",
- "瀹㈡埛寰�鏉�": "/pages/sales/receiptPaymentLedger/index",
- "閲囪喘鍙拌处": "/pages/procurementManagement/procurementLedger/index",
- "鏉ョエ鐧昏": "/pages/procurementManagement/invoiceEntry/index",
- "鏉ョエ鍙拌处": "/pages/procurementManagement/procurementInvoiceLedger/index",
- "浠樻鐧昏": "/pages/procurementManagement/paymentEntry/index",
- "浠樻娴佹按": "/pages/procurementManagement/receiptPaymentHistory/index",
- "渚涘簲鍟嗗線鏉�": "/pages/procurementManagement/paymentLedger/index",
- "閲囪喘閫�璐у崟": "/pages/procurementManagement/purchaseReturnOrder/index",
- "渚涘簲鍟嗙鐞�": "/pages/basicData/supplierManage/index",
- "鍏嚭绠$悊": "/pages/cooperativeOffice/collaborativeApproval/index1",
- "璇峰亣绠$悊": "/pages/cooperativeOffice/collaborativeApproval/index2",
- "鍑哄樊绠$悊": "/pages/cooperativeOffice/collaborativeApproval/index3",
- "鎶ラ攢绠$悊": "/pages/cooperativeOffice/collaborativeApproval/index4",
- "閲囪喘瀹℃壒": "/pages/cooperativeOffice/collaborativeApproval/index5",
- "鎶ヤ环瀹℃壒": "/pages/cooperativeOffice/collaborativeApproval/index6",
- "鍙戣揣瀹℃壒": "/pages/cooperativeOffice/collaborativeApproval/index7",
- "鍗忓悓瀹℃壒": "/pages/cooperativeOffice/collaborativeApproval/index",
- "浼氳璁剧疆": "/pages/managementMeetings/meetingSettings/index",
- "浼氳鍒楄〃": "/pages/managementMeetings/meetingList/index",
- "浼氳鐢宠": "/pages/managementMeetings/meetApplication/index",
- "浼氳瀹℃壒": "/pages/managementMeetings/meetExamine/index",
- "浼氳鍙戝竷": "/pages/managementMeetings/meetPublish/index",
- "浼氳鎬荤粨": "/pages/managementMeetings/meetSummary/index",
- "浼氳鐪嬫澘": "/pages/managementMeetings/meetingBoard/index",
- "閫氱煡鍏憡": "/pages/cooperativeOffice/noticeManagement/index",
- "鐭ヨ瘑搴�": "/pages/managementMeetings/knowledgeBase/index",
- "鐢ㄥ嵃绠$悊": "/pages/managementMeetings/sealManagement/index",
- "瑙勭珷鍒跺害": "/pages/managementMeetings/rulesRegulationsManagement/index",
- "瀹㈡埛鎷滆": "/pages/cooperativeOffice/clientVisit/index",
- "鐢熶骇璁㈠崟": "/pages/productionManagement/productionOrder/index",
- "鐢熶骇娲惧伐": "/pages/productionManagement/productionDispatching/index",
- "宸ュ簭鎺掍骇": "/pages/productionManagement/processScheduling/index",
- "鐢熶骇鏍哥畻": "/pages/productionManagement/productionAccounting/index",
- "璁惧鍙拌处": "/pages/equipmentManagement/ledger/index",
- "璁惧鎶ヤ慨": "/pages/equipmentManagement/repair/index",
- "璁惧淇濆吇": "/pages/equipmentManagement/upkeep/index",
- "璁惧宸℃": "/pages/inspectionUpload/index",
- "鍒嗘瀽杩芥函": "/pages/equipmentManagement/faultAnalysis/index",
- "鏅鸿兘娲惧崟": "/pages/equipmentManagement/smartDispatch/index",
- "浣滀笟鎸囧": "/pages/equipmentManagement/sop/index",
- "缁撴灉楠岃瘉": "/pages/equipmentManagement/verification/index",
+ 閿�鍞彴璐�: "/pages/sales/salesAccount/index",
+ 寮�绁ㄧ櫥璁�: "/pages/sales/invoicingRegistration/index",
+ 寮�绁ㄥ彴璐�: "/pages/sales/invoiceLedger/index",
+ 鍥炴鐧昏: "/pages/sales/receiptPayment/index",
+ 鍥炴娴佹按: "/pages/sales/receiptPaymentHistory/index",
+ 瀹㈡埛寰�鏉�: "/pages/sales/receiptPaymentLedger/index",
+ 閲囪喘鍙拌处: "/pages/procurementManagement/procurementLedger/index",
+ 鏉ョエ鐧昏: "/pages/procurementManagement/invoiceEntry/index",
+ 鏉ョエ鍙拌处: "/pages/procurementManagement/procurementInvoiceLedger/index",
+ 浠樻鐧昏: "/pages/procurementManagement/paymentEntry/index",
+ 浠樻娴佹按: "/pages/procurementManagement/receiptPaymentHistory/index",
+ 渚涘簲鍟嗗線鏉�: "/pages/procurementManagement/paymentLedger/index",
+ 閲囪喘閫�璐у崟: "/pages/procurementManagement/purchaseReturnOrder/index",
+ 渚涘簲鍟嗙鐞�: "/pages/basicData/supplierManage/index",
+ 鍏嚭绠$悊: "/pages/cooperativeOffice/collaborativeApproval/index1",
+ 璇峰亣绠$悊: "/pages/cooperativeOffice/collaborativeApproval/index2",
+ 鍑哄樊绠$悊: "/pages/cooperativeOffice/collaborativeApproval/index3",
+ 鎶ラ攢绠$悊: "/pages/cooperativeOffice/collaborativeApproval/index4",
+ 閲囪喘瀹℃壒: "/pages/cooperativeOffice/collaborativeApproval/index5",
+ 鎶ヤ环瀹℃壒: "/pages/cooperativeOffice/collaborativeApproval/index6",
+ 鍙戣揣瀹℃壒: "/pages/cooperativeOffice/collaborativeApproval/index7",
+ 鍗忓悓瀹℃壒: "/pages/cooperativeOffice/collaborativeApproval/index",
+ 瀹℃壒娴佺▼閰嶇疆:
+ "/pages/cooperativeOffice/collaborativeApproval/approvalManagement",
+ 浼氳璁剧疆: "/pages/managementMeetings/meetingSettings/index",
+ 浼氳鍒楄〃: "/pages/managementMeetings/meetingList/index",
+ 浼氳鐢宠: "/pages/managementMeetings/meetApplication/index",
+ 浼氳瀹℃壒: "/pages/managementMeetings/meetExamine/index",
+ 浼氳鍙戝竷: "/pages/managementMeetings/meetPublish/index",
+ 浼氳鎬荤粨: "/pages/managementMeetings/meetSummary/index",
+ 浼氳鐪嬫澘: "/pages/managementMeetings/meetingBoard/index",
+ 閫氱煡鍏憡: "/pages/cooperativeOffice/noticeManagement/index",
+ 鐭ヨ瘑搴�: "/pages/managementMeetings/knowledgeBase/index",
+ 鐢ㄥ嵃绠$悊: "/pages/managementMeetings/sealManagement/index",
+ 瑙勭珷鍒跺害: "/pages/managementMeetings/rulesRegulationsManagement/index",
+ 瀹㈡埛鎷滆: "/pages/cooperativeOffice/clientVisit/index",
+ 鐢熶骇璁㈠崟: "/pages/productionManagement/productionOrder/index",
+ 鐢熶骇娲惧伐: "/pages/productionManagement/productionDispatching/index",
+ 宸ュ簭鎺掍骇: "/pages/productionManagement/processScheduling/index",
+ 鐢熶骇鏍哥畻: "/pages/productionManagement/productionAccounting/index",
+ 璁惧鍙拌处: "/pages/equipmentManagement/ledger/index",
+ 璁惧鎶ヤ慨: "/pages/equipmentManagement/repair/index",
+ 璁惧淇濆吇: "/pages/equipmentManagement/upkeep/index",
+ 璁惧宸℃: "/pages/inspectionUpload/index",
+ 鍒嗘瀽杩芥函: "/pages/equipmentManagement/faultAnalysis/index",
+ 鏅鸿兘娲惧崟: "/pages/equipmentManagement/smartDispatch/index",
+ 浣滀笟鎸囧: "/pages/equipmentManagement/sop/index",
+ 缁撴灉楠岃瘉: "/pages/equipmentManagement/verification/index",
};
// 鑿滃崟閰嶇疆
const menuConfig = {
- "鑰冨嫟绠$悊": [
+ 鑰冨嫟绠$悊: [
{ icon: "/static/images/icon/gongchuguanli.svg", label: "鍏嚭绠$悊" },
{ icon: "/static/images/icon/qingjiaguanli.svg", label: "璇峰亣绠$悊" },
{ icon: "/static/images/icon/chuchaiguanli.svg", label: "鍑哄樊绠$悊" },
],
- "浼氳绠$悊": [
+ 浼氳绠$悊: [
{ icon: "/static/images/icon/huiyishezhi.svg", label: "浼氳璁剧疆" },
{ icon: "/static/images/icon/huiyiliebiao.svg", label: "浼氳鍒楄〃" },
{ icon: "/static/images/icon/huiyishenqing.svg", label: "浼氳鐢宠" },
@@ -101,7 +104,7 @@
{ icon: "/static/images/icon/huiyizongjie.svg", label: "浼氳鎬荤粨" },
{ icon: "/static/images/icon/huiyikanban.svg", label: "浼氳鐪嬫澘" },
],
- "鍗忓悓瀹℃壒": [
+ 鍗忓悓瀹℃壒: [
{ icon: "/static/images/icon/gongchuguanli.svg", label: "鍏嚭绠$悊" },
{ icon: "/static/images/icon/qingjiaguanli.svg", label: "璇峰亣绠$悊" },
{ icon: "/static/images/icon/chuchaiguanli.svg", label: "鍑哄樊绠$悊" },
diff --git a/src/pages/login.vue b/src/pages/login.vue
index 82e3f18..5498c07 100644
--- a/src/pages/login.vue
+++ b/src/pages/login.vue
@@ -231,6 +231,16 @@
}, 30000);
}
+ // tabbar 瑙掓爣锛氱櫥褰曞悗瀹氭椂鍒锋柊鏃跺彲鑳藉凡涓嶅湪 tabBar 椤甸潰锛屽け璐ラ潤榛樺拷鐣�
+ function callTabBarApi(api, options) {
+ try {
+ const ret = uni[api](options);
+ if (ret && typeof ret.catch === "function") ret.catch(() => {});
+ } catch (e) {
+ /* 闈� tabBar 椤甸潰锛屽拷鐣� */
+ }
+ }
+
// 鏇存柊鏈娑堟伅鏁伴噺
function updateNoticeCount(userId) {
getNoticeCount(userId)
@@ -239,12 +249,12 @@
console.log("鏈娑堟伅鏁伴噺:", count);
// 鏇存柊tabbar鐨勮鏍�
if (count > 0) {
- uni.setTabBarBadge({
+ callTabBarApi("setTabBarBadge", {
index: 2, // 娑堟伅鏍囩椤电殑绱㈠紩
text: count.toString(),
});
} else {
- uni.removeTabBarBadge({
+ callTabBarApi("removeTabBarBadge", {
index: 2,
});
}
diff --git a/src/pages/message.vue b/src/pages/message.vue
index e1ce2a4..efab161 100644
--- a/src/pages/message.vue
+++ b/src/pages/message.vue
@@ -233,6 +233,16 @@
return null;
};
+ // tabbar 瑙掓爣锛氬紓姝ヨ繑鍥炴椂鍙兘宸插垏鍒伴潪 tabBar 椤甸潰锛屽け璐ラ潤榛樺拷鐣�
+ const callTabBarApi = (api, options) => {
+ try {
+ const ret = uni[api](options);
+ if (ret && typeof ret.catch === "function") ret.catch(() => {});
+ } catch (e) {
+ /* 闈� tabBar 椤甸潰锛屽拷鐣� */
+ }
+ };
+
// 鏇存柊 tab 鏍忚鏍�
const updateTabBarBadge = () => {
if (userId.value) {
@@ -241,12 +251,12 @@
const count = res.data || 0;
// 鏇存柊tabbar鐨勮鏍�
if (count > 0) {
- uni.setTabBarBadge({
+ callTabBarApi("setTabBarBadge", {
index: 2, // 娑堟伅鏍囩椤电殑绱㈠紩
text: count.toString(),
});
} else {
- uni.removeTabBarBadge({
+ callTabBarApi("removeTabBarBadge", {
index: 2,
});
}
diff --git a/src/pages/oa/ApproveManage/approve-list/_components/ApproveInstanceDetailBody.vue b/src/pages/oa/ApproveManage/approve-list/_components/ApproveInstanceDetailBody.vue
index 72d30b6..ff88e48 100644
--- a/src/pages/oa/ApproveManage/approve-list/_components/ApproveInstanceDetailBody.vue
+++ b/src/pages/oa/ApproveManage/approve-list/_components/ApproveInstanceDetailBody.vue
@@ -50,30 +50,46 @@
</view>
</view>
</view>
-
<view class="section-card">
<view class="section-head">
- <text class="section-title">濉姤鍐呭</text>
+ <text class="section-title">{{ isSpecialApprovalType ? specialDetailTitle : "濉姤鍐呭" }}</text>
</view>
- <view v-if="displayFields.length"
- class="info-rows">
- <view v-for="field in displayFields"
- :key="field.key"
- class="info-row">
- <text class="info-label">{{ field.label }}</text>
- <text class="info-value">{{ displayFieldValue(field) }}</text>
+ <!-- 閲囪喘/鎶ヤ环/鍙戣揣瀹℃壒锛氬睍绀轰笟鍔″崟鎹鎯咃紙涓� Web approve-list 涓�鑷达級 -->
+ <template v-if="isSpecialApprovalType">
+ <view v-if="businessDetailLoading"
+ class="empty-hint">鍔犺浇涓�...</view>
+ <view v-else-if="!hasBusinessDetail"
+ class="empty-hint">{{ specialDetailEmptyText }}</view>
+ <view v-else
+ class="info-rows">
+ <view v-for="(item, idx) in specialDetailRows"
+ :key="`special-${idx}`"
+ class="info-row">
+ <text class="info-label">{{ item.label }}</text>
+ <text class="info-value">{{ item.value }}</text>
+ </view>
</view>
- <view v-for="(extra, idx) in moduleExtraRows"
- :key="`extra-${idx}`"
- class="info-row">
- <text class="info-label">{{ extra.label }}</text>
- <text class="info-value">{{ extra.value }}</text>
+ </template>
+ <template v-else>
+ <view v-if="displayFields.length"
+ class="info-rows">
+ <view v-for="field in displayFields"
+ :key="field.key"
+ class="info-row">
+ <text class="info-label">{{ field.label }}</text>
+ <text class="info-value">{{ displayFieldValue(field) }}</text>
+ </view>
+ <view v-for="(extra, idx) in moduleExtraRows"
+ :key="`extra-${idx}`"
+ class="info-row">
+ <text class="info-label">{{ extra.label }}</text>
+ <text class="info-value">{{ extra.value }}</text>
+ </view>
</view>
- </view>
- <view v-else
- class="empty-hint">鏆傛棤濉姤鍐呭</view>
+ <view v-else
+ class="empty-hint">鏆傛棤濉姤鍐呭</view>
+ </template>
</view>
-
<view class="section-card">
<view class="section-head">
<text class="section-title">瀹℃壒娴佺▼锛坽{ flowNodes.length }} 椤癸級</text>
@@ -112,7 +128,6 @@
<view v-else
class="empty-hint">鏆傛棤娴佺▼鑺傜偣</view>
</view>
-
<view class="section-card">
<view class="section-head">
<text class="section-title">瀹℃壒璁板綍</text>
@@ -140,8 +155,11 @@
</template>
<script setup>
- import { computed } from "vue";
+ import { computed, ref, watch } from "vue";
import { APPROVAL_MODULE_KEYS } from "../../../_utils/approvalModuleRegistry.js";
+ import { getQuotationList } from "@/api/salesManagement/salesQuotation";
+ import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger";
+ import { getDeliveryDetailByShippingNo } from "@/api/collaborativeApproval/approvalProcess";
import {
computeLeaveDurationDisplay,
computeOvertimeHoursDisplay,
@@ -168,7 +186,19 @@
moduleKey: { type: String, default: "" },
});
- const LEVEL_TEXT = ["", "涓�", "浜�", "涓�", "鍥�", "浜�", "鍏�", "涓�", "鍏�", "涔�", "鍗�"];
+ const LEVEL_TEXT = [
+ "",
+ "涓�",
+ "浜�",
+ "涓�",
+ "鍥�",
+ "浜�",
+ "鍏�",
+ "涓�",
+ "鍏�",
+ "涔�",
+ "鍗�",
+ ];
const isBusinessModule = computed(() =>
[
@@ -181,10 +211,14 @@
);
const statusLabel = status =>
- isBusinessModule.value ? businessStatusText(status) : instanceStatusText(status);
+ isBusinessModule.value
+ ? businessStatusText(status)
+ : instanceStatusText(status);
const statusTagType = status =>
- isBusinessModule.value ? businessStatusTagType(status) : instanceStatusTagType(status);
+ isBusinessModule.value
+ ? businessStatusTagType(status)
+ : instanceStatusTagType(status);
const displayFields = computed(() => resolveInstanceDisplayFields(props.row));
@@ -216,15 +250,166 @@
const flowNodes = computed(() => mapTasksToFlowNodes(props.row?.tasks));
- const approvalRecords = computed(() =>
- mapApprovalRecords(props.row?.records)
- );
+ const approvalRecords = computed(() => mapApprovalRecords(props.row?.records));
const rejectReason = computed(() =>
getRejectReasonFromRecords(props.row?.records)
);
const levelLabel = n => LEVEL_TEXT[Number(n)] || String(n);
+
+ /** 閲囪喘/鎶ヤ环/鍙戣揣瀹℃壒锛氫笟鍔″崟鎹鎯咃紙涓� Web approve-list 涓�鑷达級 */
+ const SPECIAL_BUSINESS_TYPES = [5, 6, 7];
+
+ const businessType = computed(() => Number(props.row?.businessType));
+ const isSpecialApprovalType = computed(() =>
+ SPECIAL_BUSINESS_TYPES.includes(businessType.value)
+ );
+
+ const businessDetail = ref({});
+ const businessProductList = ref([]);
+ const businessDetailLoading = ref(false);
+
+ const specialDetailTitle = computed(() => {
+ if (businessType.value === 5) return "閲囪喘璇︽儏";
+ if (businessType.value === 6) return "鎶ヤ环璇︽儏";
+ return "鍙戣揣璇︽儏";
+ });
+
+ const specialDetailEmptyText = computed(() => {
+ if (businessType.value === 5) return "鏈煡璇㈠埌瀵瑰簲閲囪喘璇︽儏";
+ if (businessType.value === 6) return "鏈煡璇㈠埌瀵瑰簲鎶ヤ环璇︽儏";
+ return "鏈煡璇㈠埌瀵瑰簲鍙戣揣璇︽儏";
+ });
+
+ const hasBusinessDetail = computed(() => {
+ const d = businessDetail.value || {};
+ if (businessType.value === 5) return Boolean(d.purchaseContractNumber);
+ if (businessType.value === 6) return Boolean(d.quotationNo);
+ if (businessType.value === 7) return Boolean(d.shippingInfo);
+ return false;
+ });
+
+ const formatMoney = val => `楼${Number(val ?? 0).toFixed(2)}`;
+
+ const joinDesc = parts => parts.filter(Boolean).join(" 路 ");
+
+ const specialDetailRows = computed(() => {
+ const d = businessDetail.value || {};
+ const products = businessProductList.value || [];
+ const rows = [];
+ const push = (label, value) => rows.push({ label, value: value || "--" });
+
+ if (businessType.value === 6) {
+ push("鎶ヤ环鍗曞彿", d.quotationNo);
+ push("瀹㈡埛鍚嶇О", d.customer);
+ push("涓氬姟鍛�", d.salesperson);
+ push("鎶ヤ环鏃ユ湡", d.quotationDate);
+ push("鏈夋晥鏈熻嚦", d.validDate);
+ push("浠樻鏂瑰紡", d.paymentMethod);
+ push("鎶ヤ环鎬婚", formatMoney(d.totalAmount));
+ products.forEach((p, i) => {
+ push(
+ p.product || `浜у搧${i + 1}`,
+ joinDesc([p.specification, p.unit, formatMoney(p.unitPrice)])
+ );
+ });
+ if (d.remark) push("澶囨敞", d.remark);
+ } else if (businessType.value === 5) {
+ push("閲囪喘鍚堝悓鍙�", d.purchaseContractNumber);
+ push("渚涘簲鍟嗗悕绉�", d.supplierName);
+ push("椤圭洰鍚嶇О", d.projectName);
+ push("閿�鍞悎鍚屽彿", d.salesContractNo);
+ push("绛捐鏃ユ湡", d.executionDate);
+ push("褰曞叆鏃ユ湡", d.entryDate);
+ push("浠樻鏂瑰紡", d.paymentMethod);
+ push("鍚堝悓閲戦", formatMoney(d.contractAmount));
+ products.forEach((p, i) => {
+ push(
+ p.productCategory || `浜у搧${i + 1}`,
+ joinDesc([
+ p.specificationModel,
+ p.quantity != null && p.quantity !== ""
+ ? `${p.quantity}${p.unit || ""}`
+ : "",
+ `鍚◣鍗曚环 ${formatMoney(p.taxInclusiveUnitPrice)}`,
+ `鍚◣鎬讳环 ${formatMoney(p.taxInclusiveTotalPrice)}`,
+ ])
+ );
+ });
+ } else if (businessType.value === 7) {
+ const info = d.shippingInfo || {};
+ push("閿�鍞鍗�", info.salesContractNo);
+ push("鍙戣揣璁㈠崟鍙�", info.shippingNo);
+ push("瀹㈡埛鍚嶇О", info.customerName);
+ push("鍙戣揣绫诲瀷", info.type);
+ push("鍙戣揣鏃ユ湡", info.shippingDate);
+ push("瀹℃牳鐘舵��", info.status);
+ push("鍙戣揣杞︾墝鍙�", info.shippingCarNumber);
+ push("蹇�掑叕鍙�", info.expressCompany);
+ push("蹇�掑崟鍙�", info.expressNumber);
+ products.forEach((p, i) => {
+ push(
+ p.productName || `浜у搧${i + 1}`,
+ joinDesc([
+ p.batchNo ? `鎵瑰彿 ${p.batchNo}` : "",
+ p.specificationModel,
+ p.deliveryQuantity != null ? `鍙戣揣鏁伴噺 ${p.deliveryQuantity}` : "",
+ ])
+ );
+ });
+ }
+ return rows;
+ });
+
+ async function loadBusinessDetail() {
+ const row = props.row || {};
+ const type = Number(row.businessType);
+ businessDetail.value = {};
+ businessProductList.value = [];
+ if (!SPECIAL_BUSINESS_TYPES.includes(type)) return;
+
+ businessDetailLoading.value = true;
+ try {
+ if (type === 6) {
+ if (!row.quotationNo) return;
+ const res = await getQuotationList({ quotationNo: row.quotationNo });
+ const hit = res?.data?.records?.[0] || {};
+ businessDetail.value = hit;
+ businessProductList.value = hit.products || [];
+ } else if (type === 5) {
+ if (!row.purchaseContractNumber) return;
+ const res = await getPurchaseByCode({
+ purchaseContractNumber: row.purchaseContractNumber,
+ approvalInstanceId: row.id,
+ });
+ const hit = res?.data || res || {};
+ businessDetail.value = hit;
+ businessProductList.value = hit.productData || [];
+ } else if (type === 7) {
+ if (!row.shippingNo) return;
+ const res = await getDeliveryDetailByShippingNo({
+ shippingNo: row.shippingNo,
+ });
+ const hit = res?.data || res || {};
+ businessDetail.value = hit;
+ businessProductList.value = hit.shippingProductDetailDtoList || [];
+ }
+ } catch {
+ businessDetail.value = {};
+ businessProductList.value = [];
+ } finally {
+ businessDetailLoading.value = false;
+ }
+ }
+
+ watch(
+ () => [props.row?.id, props.row?.businessType],
+ () => {
+ loadBusinessDetail();
+ },
+ { immediate: true }
+ );
</script>
<style scoped lang="scss">
diff --git a/src/pages/oa/ApproveManage/approve-list/apply.vue b/src/pages/oa/ApproveManage/approve-list/apply.vue
index b529db3..00226a3 100644
--- a/src/pages/oa/ApproveManage/approve-list/apply.vue
+++ b/src/pages/oa/ApproveManage/approve-list/apply.vue
@@ -6,7 +6,6 @@
<view class="approve-apply-page">
<PageHeader :title="pageTitle"
@back="goBack" />
-
<scroll-view class="form-scroll"
scroll-y
:show-scrollbar="false">
@@ -42,7 +41,6 @@
</up-form-item>
</u-cell-group>
</up-form>
-
<view class="section-card">
<view class="section-head">
<text class="section-title">濉姤鍐呭</text>
@@ -121,7 +119,6 @@
</up-form>
<view v-else
class="empty-hint">璇ユā鏉挎殏鏃犲~鎶ラ」</view>
-
<!-- 璇峰亣锛氬亣鏈熶綑棰� + 鏃堕暱鑷姩璁$畻 -->
<view v-if="isLeaveModule"
class="module-extra-block">
@@ -148,7 +145,6 @@
</up-form-item>
</up-form>
</view>
-
<!-- 鍔犵彮锛氭椂闀胯嚜鍔ㄨ绠� -->
<view v-if="isOvertimeModule"
class="module-extra-block">
@@ -166,7 +162,6 @@
</up-form-item>
</up-form>
</view>
-
<!-- 璋冨矖锛氬師宀椾綅鑷姩甯﹀嚭 -->
<view v-if="isTransferModule"
class="module-extra-block">
@@ -182,7 +177,6 @@
</up-form>
</view>
</view>
-
<view class="section-card">
<view class="section-head">
<text class="section-title">瀹℃壒娴佺▼</text>
@@ -234,14 +228,12 @@
text="鏈幏鍙栧埌妯℃澘璇︽儏" />
</view>
</scroll-view>
-
<FooterButtons v-if="!loading && detail"
cancel-text="鍙栨秷"
:confirm-text="confirmText"
:loading="submitting"
@cancel="goBack"
@confirm="handleSubmit" />
-
<up-popup :show="showDatePicker"
mode="bottom"
@close="showDatePicker = false">
@@ -251,7 +243,6 @@
@confirm="onDateConfirm"
@cancel="onDatePickerCancel" />
</up-popup>
-
<up-action-sheet :show="showSelectSheet"
:title="selectSheetTitle"
:actions="selectSheetActions"
@@ -312,9 +303,24 @@
parseFieldDateToTs,
} from "../../_utils/approvalFormField.js";
- import { EDIT_STORAGE_KEY } from "../../_utils/approveListUtils.js";
+ import {
+ EDIT_STORAGE_KEY,
+ mapFlowNodesToTasks,
+ } from "../../_utils/approveListUtils.js";
- const LEVEL_TEXT = ["", "涓�", "浜�", "涓�", "鍥�", "浜�", "鍏�", "涓�", "鍏�", "涔�", "鍗�"];
+ const LEVEL_TEXT = [
+ "",
+ "涓�",
+ "浜�",
+ "涓�",
+ "鍥�",
+ "浜�",
+ "鍏�",
+ "涓�",
+ "鍏�",
+ "涔�",
+ "鍗�",
+ ];
const userStore = useUserStore();
const moduleKey = ref("");
@@ -363,7 +369,9 @@
const isEditMode = computed(() => !!instanceId.value);
const pageTitle = computed(() => (isEditMode.value ? "淇敼瀹℃壒" : "鍙戣捣瀹℃壒"));
- const confirmText = computed(() => (isEditMode.value ? "淇濆瓨淇敼" : "鎻愪氦瀹℃壒"));
+ const confirmText = computed(() =>
+ isEditMode.value ? "淇濆瓨淇敼" : "鎻愪氦瀹℃壒"
+ );
const applicantName = computed(
() => userStore.nickName || userStore.name || "-"
@@ -414,8 +422,10 @@
const levelLabel = n => LEVEL_TEXT[Number(n)] || String(n);
- const selectSheetTitle = computed(
- () => (activeSelectField.value?.label ? `閫夋嫨${activeSelectField.value.label}` : "璇烽�夋嫨")
+ const selectSheetTitle = computed(() =>
+ activeSelectField.value?.label
+ ? `閫夋嫨${activeSelectField.value.label}`
+ : "璇烽�夋嫨"
);
const selectSheetActions = computed(() => {
@@ -466,7 +476,8 @@
});
const matched = options.find(
opt =>
- String(opt.value) === String(stored) || String(opt.label) === String(stored)
+ String(opt.value) === String(stored) ||
+ String(opt.label) === String(stored)
);
return (
matched?.label ||
@@ -547,7 +558,9 @@
const val = formValues[field.key];
if (val === undefined || val === null || String(val).trim() === "") {
const action =
- isSelectField(field) || isDateLikeField(field) || isDatetimerangeField(field)
+ isSelectField(field) ||
+ isDateLikeField(field) ||
+ isDatetimerangeField(field)
? "璇烽�夋嫨"
: "璇峰~鍐�";
uni.showToast({ title: `${action}${field.label}`, icon: "none" });
@@ -569,7 +582,8 @@
options.length &&
!options.some(
opt =>
- String(opt.value) === String(val) || String(opt.label) === String(val)
+ String(opt.value) === String(val) ||
+ String(opt.label) === String(val)
)
) {
uni.showToast({ title: `${field.label}閫夐」鏃犳晥`, icon: "none" });
@@ -602,14 +616,28 @@
formConfigData.value.fields
);
const allFields = formConfigData.value.fields || [];
+ const formPayload = {};
+ allFields.forEach(field => {
+ if (!field?.key) return;
+ formPayload[field.key] = formValues[field.key] ?? "";
+ });
+ Object.entries(formValues).forEach(([key, val]) => {
+ if (key && formPayload[key] === undefined) formPayload[key] = val;
+ });
return JSON.stringify({
- prompt: formConfigData.value.prompt,
- fields: allFields.map(field => ({
- ...field,
- value: formValues[field.key] ?? "",
- })),
+ summaryPlaceholder: formConfigData.value.prompt || "",
+ approvalType: formConfigData.value.approvalType || "",
+ fields: allFields.map(({ value: _value, ...rest }) => rest),
+ formPayload,
});
};
+
+ /** 妯℃澘 nodes 鈫� 瀹℃壒浠诲姟锛堜笌 Web buildInstanceDto 涓�鑷达級 */
+ const buildInstanceTasks = instanceIdValue =>
+ mapFlowNodesToTasks(detail.value?.nodes, {
+ instanceId: instanceIdValue,
+ templateId: detail.value?.id,
+ });
const buildSavePayload = () => ({
templateId: detail.value.id,
@@ -623,6 +651,7 @@
applyTime: parseTime(new Date()),
deptId: userStore.currentDeptId || undefined,
formConfig: buildFormConfigPayload(),
+ tasks: buildInstanceTasks(),
});
const buildUpdatePayload = () => {
@@ -648,6 +677,7 @@
deptId: row.deptId,
deleted: row.deleted,
formConfig: buildFormConfigPayload(),
+ tasks: buildInstanceTasks(instanceId.value),
approveAction: row.approveAction,
approveComment: row.approveComment,
};
@@ -780,7 +810,9 @@
try {
const res = await findPostOptions();
const rows = res?.data ?? res?.rows ?? [];
- postIdToName.value = buildPostIdToNameMap(Array.isArray(rows) ? rows : []);
+ postIdToName.value = buildPostIdToNameMap(
+ Array.isArray(rows) ? rows : []
+ );
} catch {
postIdToName.value = {};
}
@@ -794,7 +826,10 @@
return;
}
const user = userById(transferUserPool.value, uid);
- extraForm.originalPostName = resolveOriginalPostName(user, postIdToName.value);
+ extraForm.originalPostName = resolveOriginalPostName(
+ user,
+ postIdToName.value
+ );
}
const goBack = () => {
diff --git a/src/pages/oa/ApproveManage/approve-list/detail.vue b/src/pages/oa/ApproveManage/approve-list/detail.vue
index 9c38634..ba91beb 100644
--- a/src/pages/oa/ApproveManage/approve-list/detail.vue
+++ b/src/pages/oa/ApproveManage/approve-list/detail.vue
@@ -6,25 +6,22 @@
<view class="oa-detail-page">
<PageHeader :title="pageTitle"
@back="goBack" />
-
<scroll-view v-if="displayReady"
class="oa-detail-scroll"
scroll-y
:show-scrollbar="false">
<ReimburseInstanceDetailBody v-if="isReimburse"
- :reimburse-row="reimburseRow"
- :module-key="detailModuleKey" />
+ :reimburse-row="reimburseRow"
+ :module-key="detailModuleKey" />
<ApproveInstanceDetailBody v-else
:row="row"
:module-key="detailModuleKey" />
</scroll-view>
-
<view v-else
class="oa-empty">
<up-empty mode="data"
:text="loading ? '鍔犺浇涓�' : '鏈幏鍙栧埌瀹℃壒鏁版嵁'" />
</view>
-
<view v-if="displayReady"
class="oa-page-footer">
<text class="oa-footer-btn btn-default"
@@ -46,7 +43,6 @@
import ApproveInstanceDetailBody from "./_components/ApproveInstanceDetailBody.vue";
import ReimburseInstanceDetailBody from "../../ReimburseManage/_components/ReimburseInstanceDetailBody.vue";
import { OA_NAV } from "@/config/oaPaths.js";
- import useUserStore from "@/store/modules/user";
import {
canApproveInstance,
canEditBusinessInstanceRow,
@@ -65,7 +61,6 @@
import { getApprovalModuleConfig } from "../../_utils/approvalModuleRegistry.js";
import { canEditReimbursementRow } from "../../_utils/finReimbursementMappers.js";
- const userStore = useUserStore();
const fromBusiness = ref(false);
const row = ref(null);
const reimburseRow = ref(null);
@@ -103,7 +98,7 @@
if (fromBusiness.value) {
return canEditBusinessInstanceRow(row.value);
}
- return canModifyInstance(row.value, userStore);
+ return canModifyInstance(row.value);
});
const showApprove = computed(() => canApproveInstance(row.value));
@@ -158,7 +153,9 @@
if (isReimburseApprovalInstance(cached)) {
loading.value = true;
try {
- const { reimburseRow: mapped } = await loadReimburseDetailForInstance(cached);
+ const { reimburseRow: mapped } = await loadReimburseDetailForInstance(
+ cached
+ );
reimburseRow.value = mapped;
} catch {
uni.showToast({ title: "鍔犺浇鎶ラ攢璇︽儏澶辫触", icon: "none" });
diff --git a/src/pages/oa/ApproveManage/approve-list/index.vue b/src/pages/oa/ApproveManage/approve-list/index.vue
index 40f9468..f9f4a09 100644
--- a/src/pages/oa/ApproveManage/approve-list/index.vue
+++ b/src/pages/oa/ApproveManage/approve-list/index.vue
@@ -5,7 +5,6 @@
<view class="oa-approval-page">
<PageHeader title="瀹℃壒鍒楄〃"
@back="goBack" />
-
<view class="oa-toolbar">
<view class="oa-filter-chip active-search">
<up-icon name="search"
@@ -25,7 +24,6 @@
color="#2979ff" />
</view>
</view>
-
<scroll-view class="oa-list-scroll"
scroll-y
:show-scrollbar="false"
@@ -47,7 +45,6 @@
{{ statusText(item.status) }}
</text>
</view>
-
<view class="oa-card-body">
<view class="oa-info-grid">
<view class="oa-info-row">
@@ -73,7 +70,6 @@
</view>
</view>
</view>
-
<view v-if="canModify(item) || item.isApprove"
class="oa-card-foot"
@click.stop>
@@ -93,7 +89,6 @@
text="鏆傛棤瀹℃壒鏁版嵁" />
</view>
</scroll-view>
-
<view class="fab-button"
@click="goAdd">
<up-icon name="plus"
@@ -109,7 +104,6 @@
import PageHeader from "@/components/PageHeader.vue";
import { listApprovalInstancePage } from "@/api/oa/approvalInstance.js";
import { OA_NAV } from "@/config/oaPaths.js";
- import useUserStore from "@/store/modules/user";
import { parseTime } from "@/utils/ruoyi";
import {
businessStatusClass,
@@ -123,7 +117,6 @@
stashReimburseEditFromApprove,
} from "../../_utils/reimburseApproveBridge.js";
- const userStore = useUserStore();
const queryParams = reactive({ keyword: "" });
const list = ref([]);
const pageStatus = ref("loadmore");
@@ -149,7 +142,7 @@
return parseTime(val, "{y}-{m}-{d} {h}:{i}:{s}") || String(val);
};
- const canModify = item => canModifyInstance(item, userStore);
+ const canModify = item => canModifyInstance(item);
const currentApproverName = item => {
const tasks = item?.tasks;
@@ -223,7 +216,7 @@
const goModify = item => {
if (!canModify(item)) {
- uni.showToast({ title: "浠呰繘琛屼腑鐨勬湰浜虹敵璇峰彲缂栬緫", icon: "none" });
+ uni.showToast({ title: "浠呰繘琛屼腑鐨勫鎵瑰彲缂栬緫", icon: "none" });
return;
}
const mk = inferReimburseModuleKeyFromInstance(item);
diff --git a/src/pages/oa/ApproveManage/approve-list/template-select.vue b/src/pages/oa/ApproveManage/approve-list/template-select.vue
index 073b556..e984952 100644
--- a/src/pages/oa/ApproveManage/approve-list/template-select.vue
+++ b/src/pages/oa/ApproveManage/approve-list/template-select.vue
@@ -7,7 +7,6 @@
<view class="template-select-page sales-account">
<PageHeader :title="pageHeaderTitle"
@back="goBack" />
-
<view v-if="typeOptions.length && !moduleKey"
class="step-section">
<view class="tabs-wrap">
@@ -17,12 +16,6 @@
@click="onTabClick" />
</view>
</view>
-
- <view v-if="useAllTemplatesFallback && allTemplates.length"
- class="fallback-hint">
- <text>褰撳墠绫诲瀷涓嬫棤鍖归厤妯℃澘锛屽凡鏄剧ず鍏ㄩ儴 {{ allTemplates.length }} 涓彲鐢ㄦā鏉�</text>
- </view>
-
<view class="search-section">
<view class="search-bar">
<view class="search-input">
@@ -38,7 +31,6 @@
</view>
</view>
</view>
-
<scroll-view class="list-scroll"
scroll-y
:show-scrollbar="false">
@@ -144,29 +136,21 @@
const currentTypeOption = computed(() => typeOptions.value[activeTab.value]);
- /** 鏃� moduleKey 涓斿綋鍓� Tab 绛涗笉鍒版椂锛屽睍绀哄叏閮ㄦā鏉块伩鍏嶃�屾湁鏁版嵁鍗寸┖鐧姐�� */
- const useAllTemplatesFallback = computed(() => {
- if (moduleKey.value) return false;
- if (!allTemplates.value.length) return false;
- const businessType = currentTypeOption.value?.value;
- if (businessType == null || businessType === "") return true;
- return filterTemplatesByBusinessType(allTemplates.value, businessType).length === 0;
- });
+ /** 涓嶅厑璁告墜宸ュ彂璧峰鎵圭殑绫诲瀷锛堥噰璐�/鎶ヤ环/鍙戣揣锛岀敱涓氬姟鍗曟嵁浜х敓锛屼笌 Web 涓�鑷达級 */
+ const SUBMIT_EXCLUDED_BUSINESS_TYPES = [5, 6, 7];
+ /** 褰撳墠 Tab锛堟垨涓氬姟妯″潡锛変笅鍙彂璧风殑妯℃澘 */
const currentSource = computed(() => {
if (moduleKey.value && moduleBusinessTypes.value.length) {
- const filtered = filterTemplatesByBusinessTypes(
+ return filterTemplatesByBusinessTypes(
allTemplates.value,
moduleBusinessTypes.value
);
- if (filtered.length) return filtered;
- return allTemplates.value;
}
- if (useAllTemplatesFallback.value) {
- return allTemplates.value;
- }
- const businessType = currentTypeOption.value?.value;
- return filterTemplatesByBusinessType(allTemplates.value, businessType);
+ return filterTemplatesByBusinessType(
+ allTemplates.value,
+ currentTypeOption.value?.value
+ );
});
const displayList = computed(() => {
@@ -184,15 +168,11 @@
if (moduleConfig.value?.label) {
return `鏆傛棤${moduleConfig.value.label}鍙敤妯℃澘`;
}
- if (useAllTemplatesFallback.value) {
- return "褰撳墠绫诲瀷涓嬫棤鍖归厤妯℃澘";
- }
const typeName = currentTypeOption.value?.name || "璇ュ鎵圭被鍨�";
return `鏆傛棤${typeName}涓嬬殑妯℃澘锛堝彲鍒囨崲涓婃柟绫诲瀷锛塦;
});
- const businessTypeText = type =>
- getBusinessTypeLabel(type, typeLabelMap.value);
+ const businessTypeText = type => getBusinessTypeLabel(type, typeLabelMap.value);
const enabledText = enabled => {
const val = String(enabled ?? "");
@@ -222,10 +202,15 @@
fetchApprovalTemplateTypes(),
fetchEnabledApprovalTemplates(),
]);
- let resolvedOpts = opts?.length ? opts : buildTypeOptionsFromTemplates(templates);
+ let resolvedOpts = opts?.length
+ ? opts
+ : buildTypeOptionsFromTemplates(templates);
if (!resolvedOpts.length) {
resolvedOpts = [...FALLBACK_BUSINESS_TYPE_OPTIONS];
}
+ resolvedOpts = resolvedOpts.filter(
+ opt => !SUBMIT_EXCLUDED_BUSINESS_TYPES.includes(Number(opt.value))
+ );
typeOptions.value = resolvedOpts;
typeLabelMap.value = buildTypeLabelMap(resolvedOpts);
allTemplates.value = templates;
@@ -239,8 +224,8 @@
}
if (moduleKey.value) {
- const resolved = resolveModuleBusinessType(moduleKey.value, opts);
- const idx = opts.findIndex(
+ const resolved = resolveModuleBusinessType(moduleKey.value, resolvedOpts);
+ const idx = resolvedOpts.findIndex(
opt => String(opt.value) === String(resolved)
);
activeTab.value =
@@ -292,16 +277,6 @@
display: flex;
flex-direction: column;
min-height: 100vh;
- }
-
- .fallback-hint {
- margin: 8px 12px 0;
- padding: 8px 12px;
- font-size: 12px;
- color: #e6a23c;
- background: #fdf6ec;
- border-radius: 6px;
- line-height: 1.5;
}
.step-section {
diff --git a/src/pages/oa/ApproveManage/approve-template/detail.vue b/src/pages/oa/ApproveManage/approve-template/detail.vue
index 40a8958..09825e0 100644
--- a/src/pages/oa/ApproveManage/approve-template/detail.vue
+++ b/src/pages/oa/ApproveManage/approve-template/detail.vue
@@ -146,8 +146,12 @@
try {
const obj = typeof raw === "string" ? JSON.parse(raw) : raw;
return {
- prompt: obj?.prompt || "",
- fields: Array.isArray(obj?.fields) ? obj.fields : [],
+ prompt: obj?.summaryPlaceholder || obj?.prompt || "",
+ fields: Array.isArray(obj?.fields)
+ ? obj.fields
+ : Array.isArray(obj?.formFields)
+ ? obj.formFields
+ : [],
};
} catch {
return { prompt: "", fields: [] };
diff --git a/src/pages/oa/ApproveManage/approve-template/edit.vue b/src/pages/oa/ApproveManage/approve-template/edit.vue
index 53a2d2e..8f32b00 100644
--- a/src/pages/oa/ApproveManage/approve-template/edit.vue
+++ b/src/pages/oa/ApproveManage/approve-template/edit.vue
@@ -1294,7 +1294,7 @@
businessType: form.businessType,
templateType: form.templateType,
formConfig: JSON.stringify({
- prompt: formConfig.prompt?.trim() || "",
+ summaryPlaceholder: formConfig.prompt?.trim() || "",
fields: formConfig.fields,
}),
nodes: flowNodes.value.map((node, index) => {
diff --git a/src/pages/oa/_utils/approvalModuleListSearch.js b/src/pages/oa/_utils/approvalModuleListSearch.js
index a98409a..b8d06a5 100644
--- a/src/pages/oa/_utils/approvalModuleListSearch.js
+++ b/src/pages/oa/_utils/approvalModuleListSearch.js
@@ -1,3 +1,4 @@
+import dayjs from "dayjs";
import {
APPROVAL_MODULE_KEYS,
APPROVAL_MODULE_REGISTRY,
@@ -226,16 +227,27 @@
placeholder: "璇疯緭鍏ュ鎵瑰崟鍙�",
};
-/** 缁勮 approvalInstanceDto 鏌ヨ鐗囨锛堢敵璇蜂汉 + 瀹℃壒鍗曞彿锛� */
+/** 缁勮 approvalInstanceDto 鏌ヨ鐗囨锛堢敵璇蜂汉 + 瀹℃壒鍗曞彿 + 鐘舵�� + 鏃堕棿鑼冨洿锛屼笌 Web 涓�鑷达級 */
export function buildApprovalInstanceSearchDto(searchForm = {}, extraParams = {}) {
const dto = {
...(extraParams && typeof extraParams === "object" ? extraParams : {}),
};
Object.assign(dto, pickApplicantFromSearchForm(searchForm));
Object.assign(dto, pickInstanceNoFromSearchForm(searchForm));
+
+ if (searchForm?.status) {
+ dto.status = searchForm.status;
+ }
+
+ const range = searchForm?.createTimeRange;
+ if (Array.isArray(range) && range[0]) {
+ dto.createTimeStart = range[0] + (range[0].includes(":") ? "" : " 00:00:00");
+ }
+ if (Array.isArray(range) && range[1]) {
+ dto.createTimeEnd = range[1] + (range[1].includes(":") ? "" : " 23:59:59");
+ }
+
delete dto.createTime;
- delete dto.createTimeStart;
- delete dto.createTimeEnd;
return dto;
}
@@ -306,7 +318,12 @@
if ((sf.instanceNo || "").trim()) return true;
if ((sf.applicantKeyword || "").trim()) return true;
if ((sf.applicantName || "").trim()) return true;
- return sf.applicantId != null && sf.applicantId !== "";
+ if (sf.applicantId != null && sf.applicantId !== "") return true;
+ if (sf.status) return true;
+ if (Array.isArray(sf.createTimeRange) && sf.createTimeRange.length === 2) {
+ return true;
+ }
+ return false;
}
function matchApplicantKeyword(row, keyword) {
@@ -372,18 +389,35 @@
});
}
-/** 鎸夌敵璇蜂汉銆佸鎵瑰崟鍙峰仛鍓嶇鍏滃簳绛涢�� */
+/** 鎸夌敵璇蜂汉銆佸鎵瑰崟鍙枫�佺姸鎬併�佹椂闂磋寖鍥村仛鍓嶇鍏滃簳绛涢�� */
export function filterRowsByModuleSearch(moduleKey, rows, searchForm = {}) {
const sf = searchForm || {};
const list = Array.isArray(rows) ? rows : [];
if (!hasActiveModuleSearch(moduleKey, sf)) return list;
- return list.filter(
- row =>
- matchInstanceNo(row, sf.instanceNo) &&
- matchApplicantId(row, sf.applicantId) &&
- matchApplicantKeyword(row, sf.applicantKeyword || sf.applicantName)
- );
+ return list.filter(row => {
+ if (!matchInstanceNo(row, sf.instanceNo)) return false;
+ if (!matchApplicantId(row, sf.applicantId)) return false;
+ if (!matchApplicantKeyword(row, sf.applicantKeyword || sf.applicantName)) {
+ return false;
+ }
+ if (
+ sf.status &&
+ String(row.statusRaw || row.status).toUpperCase() !== String(sf.status).toUpperCase()
+ ) {
+ return false;
+ }
+ if (Array.isArray(sf.createTimeRange) && sf.createTimeRange.length === 2) {
+ const rowTime = row.createTime || row.applyTime;
+ if (rowTime) {
+ const t = dayjs(rowTime);
+ const start = dayjs(sf.createTimeRange[0] + " 00:00:00");
+ const end = dayjs(sf.createTimeRange[1] + " 23:59:59");
+ if (t.isBefore(start) || t.isAfter(end)) return false;
+ }
+ }
+ return true;
+ });
}
function prependInstanceNoField(fields, moduleKey) {
diff --git a/src/pages/oa/_utils/approvalTemplateType.js b/src/pages/oa/_utils/approvalTemplateType.js
index e554068..3ac785f 100644
--- a/src/pages/oa/_utils/approvalTemplateType.js
+++ b/src/pages/oa/_utils/approvalTemplateType.js
@@ -97,18 +97,11 @@
return 0;
}
-/** 鎷夊彇宸插惎鐢ㄦā鏉匡紙鑷畾涔� + 绯荤粺鍐呯疆锛屼笌 Web 瀵煎叆閫昏緫涓�鑷达級 */
+/** 鎷夊彇鍙彂璧峰鎵圭殑妯℃澘锛堜粎鑷畾涔夊凡鍚敤锛屼笌 Web loadSubmitTemplates 涓�鑷达級 */
export async function fetchEnabledApprovalTemplates() {
- const [customRes, builtinRes] = await Promise.all([
- listApprovalTemplateByType(CUSTOM_TEMPLATE_LIST_TYPE),
- listApprovalTemplateByType(SYSTEM_TEMPLATE_TYPE),
- ]);
- const merged = [
- ...unwrapTemplateList(customRes),
- ...unwrapTemplateList(builtinRes),
- ];
+ const customRes = await listApprovalTemplateByType(CUSTOM_TEMPLATE_LIST_TYPE);
const byId = new Map();
- merged.forEach(item => {
+ unwrapTemplateList(customRes).forEach(item => {
if (item?.id != null) byId.set(String(item.id), item);
});
return filterEnabledTemplates([...byId.values()]);
diff --git a/src/pages/oa/_utils/approveListUtils.js b/src/pages/oa/_utils/approveListUtils.js
index 538fec9..ad0bae9 100644
--- a/src/pages/oa/_utils/approveListUtils.js
+++ b/src/pages/oa/_utils/approveListUtils.js
@@ -180,6 +180,35 @@
.map(([, node]) => node);
}
+/** 妯℃澘 nodes 鈫� 瀹℃壒浠诲姟锛堜笌 Web mapFlowNodesToTasks 涓�鑷达級 */
+export function mapFlowNodesToTasks(nodes, { instanceId, templateId } = {}) {
+ const list = Array.isArray(nodes) ? nodes : [];
+ const tasks = [];
+ list.forEach((n, i) => {
+ const levelNo = n?.levelNo ?? i + 1;
+ const approveType =
+ String(n?.approveType || "AND").toUpperCase() === "OR" ? "OR" : "AND";
+ (n?.approvers || []).forEach((a, idx) => {
+ if (a?.approverId == null || a.approverId === "") return;
+ const task = {
+ levelNo,
+ approveType,
+ approverId: a.approverId,
+ approverName: a.approverName || "",
+ sortNo: a.sortNo ?? idx + 1,
+ };
+ if (a.id != null) task.id = a.id;
+ if (a.nodeId != null) task.nodeId = a.nodeId;
+ if (a.templateId != null) task.templateId = a.templateId;
+ else if (templateId) task.templateId = templateId;
+ if (instanceId) task.instanceId = instanceId;
+ if (a.status != null) task.status = a.status;
+ tasks.push(task);
+ });
+ });
+ return tasks;
+}
+
/** 缁勮瀹℃壒鎻愪氦 DTO锛堜笌 Web buildApproveInstanceDto 涓�鑷达級 */
export function buildApproveInstanceDto(id, uiResult, comment) {
const opinion = (comment || "").trim();
@@ -190,22 +219,9 @@
};
}
-/** 鏄惁鏈汉鍙戣捣鐨勫鎵� */
-export function isOwnApplication(item, userStore) {
- const uid = userStore?.id;
- if (item?.applicantId != null && uid != null && uid !== "") {
- return String(item.applicantId) === String(uid);
- }
- const loginName = userStore?.nickName || userStore?.name;
- if (loginName && item?.applicantName) {
- return String(item.applicantName).trim() === String(loginName).trim();
- }
- return false;
-}
-
-/** 浠呰繘琛屼腑涓旀湰浜哄彂璧锋椂鍙紪杈� */
-export function canModifyInstance(item, userStore) {
- return item?.status === "PENDING" && isOwnApplication(item, userStore);
+/** 浠呰繘琛屼腑鍙紪杈戯紙涓� Web openEditDialog 鍒ゅ畾涓�鑷达級 */
+export function canModifyInstance(item) {
+ return item?.status === "PENDING";
}
/** 寰呭綋鍓嶇敤鎴峰鎵� */
diff --git a/src/pages/procurementManagement/paymentLedger/detail.vue b/src/pages/procurementManagement/paymentLedger/detail.vue
index 26f5cf5..bd83734 100644
--- a/src/pages/procurementManagement/paymentLedger/detail.vue
+++ b/src/pages/procurementManagement/paymentLedger/detail.vue
@@ -11,16 +11,20 @@
<text class="summary-value">{{ tableData.length }}</text>
</view>
<view class="summary-item">
- <text class="summary-label">寮�绁ㄦ�婚噾棰�</text>
- <text class="summary-value">{{ formatAmount(invoiceTotal) }}</text>
+ <text class="summary-label">鍚堝悓鎬婚噾棰�</text>
+ <text class="summary-value">{{ formatAmount(contractTotal) }}</text>
</view>
<view class="summary-item">
- <text class="summary-label">鍥炴鎬婚噾棰�</text>
- <text class="summary-value highlight">{{ formatAmount(receiptTotal) }}</text>
+ <text class="summary-label">浠樻鎬婚噾棰�</text>
+ <text class="summary-value highlight">{{ formatAmount(paymentTotal) }}</text>
</view>
<view class="summary-item">
- <text class="summary-label">搴旀敹鎬婚噾棰�</text>
- <text class="summary-value danger">{{ formatAmount(unReceiptTotal) }}</text>
+ <text class="summary-label">閫�璐ф�婚噾棰�</text>
+ <text class="summary-value">{{ formatAmount(returnTotal) }}</text>
+ </view>
+ <view class="summary-item">
+ <text class="summary-label">搴斾粯鎬婚噾棰�</text>
+ <text class="summary-value danger">{{ formatAmount(payableTotal) }}</text>
</view>
</view>
<!-- 鍥炴璁板綍鏄庣粏鍒楄〃 -->
@@ -38,32 +42,36 @@
</view>
<text class="item-index">{{ index + 1 }}</text>
</view>
- <view class="item-date">{{ item.happenTime }}</view>
+ <view class="item-date">{{ item.executionDate }}</view>
</view>
<up-divider></up-divider>
<view class="item-details">
<view class="detail-row">
- <text class="detail-label">鍙戠エ閲戦(鍏�)</text>
- <text class="detail-value">{{ formatAmount(item.invoiceAmount) }}</text>
+ <text class="detail-label">閲囪喘鍚堝悓鍙�</text>
+ <text class="detail-value">{{ item.purchaseContractNumber || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍚堝悓閲戦(鍏�)</text>
+ <text class="detail-value">{{ formatAmount(item.contractAmount) }}</text>
</view>
<view class="detail-row">
<text class="detail-label">浠樻閲戦(鍏�)</text>
- <text class="detail-value highlight">{{ formatAmount(item.currentPaymentAmount) }}</text>
+ <text class="detail-value highlight">{{ formatAmount(item.paymentAmount) }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">閫�璐ч噾棰�(鍏�)</text>
+ <text class="detail-value">{{ formatAmount(item.returnAmount) }}</text>
</view>
<view class="detail-row">
<text class="detail-label">搴斾粯閲戦(鍏�)</text>
<text class="detail-value danger">{{ formatAmount(item.payableAmount) }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">鍙戠敓鏃ユ湡</text>
- <text class="detail-value">{{ item.paymentDate }}</text>
</view>
</view>
</view>
</view>
<view v-else
class="no-data">
- <text>鏆傛棤鍥炴璁板綍</text>
+ <text>鏆傛棤浠樻璁板綍</text>
</view>
</view>
</template>
@@ -71,32 +79,35 @@
<script setup>
import { ref, computed, onMounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
- import {
- paymentLedgerList,
- paymentRecordList,
- } from "@/api/procurementManagement/paymentLedger";
+ import { paymentRecordList } from "@/api/procurementManagement/paymentLedger";
- // 瀹㈡埛淇℃伅
+ // 渚涘簲鍟嗕俊鎭�
const supplierId = ref("");
// 琛ㄦ牸鏁版嵁
const tableData = ref([]);
- const invoiceTotal = computed(() => {
+ const contractTotal = computed(() => {
return tableData.value.reduce((sum, item) => {
- return sum + (parseFloat(item.invoiceAmount) || 0);
+ return sum + (parseFloat(item.contractAmount) || 0);
}, 0);
});
- const receiptTotal = computed(() => {
+ const paymentTotal = computed(() => {
return tableData.value.reduce((sum, item) => {
- return sum + (parseFloat(item.receiptAmount) || 0);
+ return sum + (parseFloat(item.paymentAmount) || 0);
}, 0);
});
- const unReceiptTotal = computed(() => {
+ const returnTotal = computed(() => {
return tableData.value.reduce((sum, item) => {
- return sum + (parseFloat(item.unReceiptAmount) || 0);
+ return sum + (parseFloat(item.returnAmount) || 0);
+ }, 0);
+ });
+
+ const payableTotal = computed(() => {
+ return tableData.value.reduce((sum, item) => {
+ return sum + (parseFloat(item.payableAmount) || 0);
}, 0);
});
@@ -119,15 +130,21 @@
const getList = () => {
if (!supplierId.value) {
uni.showToast({
- title: "瀹㈡埛淇℃伅缂哄け",
+ title: "渚涘簲鍟嗕俊鎭己澶�",
icon: "error",
});
return;
}
showLoadingToast("鍔犺浇涓�...");
- paymentRecordList({ supplierId: supplierId.value })
+ paymentRecordList({
+ supplierId: supplierId.value,
+ current: -1,
+ size: -1,
+ })
.then(res => {
- tableData.value = res.data;
+ // 鏄庣粏鎺ュ彛鍙兘杩斿洖鏁扮粍鎴栧垎椤靛璞★紙涓� Web 澶勭悊涓�鑷达級
+ const data = res?.data;
+ tableData.value = Array.isArray(data) ? data : data?.records || [];
closeToast();
})
.catch(() => {
diff --git a/src/pages/procurementManagement/paymentLedger/index.vue b/src/pages/procurementManagement/paymentLedger/index.vue
index 8bd7c6e..abeb73d 100644
--- a/src/pages/procurementManagement/paymentLedger/index.vue
+++ b/src/pages/procurementManagement/paymentLedger/index.vue
@@ -45,13 +45,17 @@
<view class="item-details">
<view class="detail-row">
<text class="detail-label">鍚堝悓閲戦(鍏�)</text>
- <text class="detail-value">{{ formattedNumber(item.invoiceAmount) }}</text>
+ <text class="detail-value">{{ formattedNumber(item.contractAmounts) }}</text>
</view>
<view class="detail-row">
<text class="detail-label">浠樻閲戦(鍏�)</text>
<text class="detail-value">{{ formattedNumber(item.paymentAmount) }}</text>
</view>
<view class="detail-row">
+ <text class="detail-label">閫�璐ч噾棰�(鍏�)</text>
+ <text class="detail-value">{{ formattedNumber(item.returnAmount) }}</text>
+ </view>
+ <view class="detail-row">
<text class="detail-label">搴斾粯閲戦(鍏�)</text>
<text class="detail-value highlight danger">{{ formattedNumber(item.payableAmount) }}</text>
</view>
diff --git a/src/pages/procurementManagement/purchaseReturnOrder/add.vue b/src/pages/procurementManagement/purchaseReturnOrder/add.vue
index fd5591e..0bc907d 100644
--- a/src/pages/procurementManagement/purchaseReturnOrder/add.vue
+++ b/src/pages/procurementManagement/purchaseReturnOrder/add.vue
@@ -547,11 +547,16 @@
};
const mergeSelectedProducts = selectedRows => {
- const existing = new Set((form.value.purchaseReturnOrderProductsDtos || []).map(i => String(i.salesLedgerProductId || i.id)));
+ const existing = new Set(
+ (form.value.purchaseReturnOrderProductsDtos || []).map(i =>
+ String(i.stockInRecordId || i.salesLedgerProductId || i.id)
+ )
+ );
const toAdd = (selectedRows || [])
.filter(i => !existing.has(String(i.id)))
.map(i => ({
...i,
+ stockInRecordId: i.stockInRecordId || i.id,
salesLedgerProductId: i.id,
returnQuantity: 0,
taxInclusiveTotalPrice: 0,
@@ -602,6 +607,7 @@
loading.value = true;
const rows = (form.value.purchaseReturnOrderProductsDtos || []).map(i => {
const cloned = { ...i };
+ cloned.stockInRecordId = cloned.stockInRecordId || cloned.id;
syncRowTotal(cloned);
return cloned;
});
diff --git a/src/pages/qualityManagement/finalInspection/index.vue b/src/pages/qualityManagement/finalInspection/index.vue
index 0e860ff..3c42877 100644
--- a/src/pages/qualityManagement/finalInspection/index.vue
+++ b/src/pages/qualityManagement/finalInspection/index.vue
@@ -121,7 +121,7 @@
<u-button type="primary"
size="small"
class="action-btn"
- :disabled="item.inspectState"
+ :disabled="!!item.inspectState"
@click.stop="startInspection(item)">
缂栬緫
</u-button>
@@ -134,7 +134,7 @@
<u-button type="success"
size="small"
class="action-btn"
- :disabled="item.inspectState"
+ :disabled="!!item.inspectState"
@click.stop="submitInspection(item)">
鎻愪氦
</u-button>
@@ -149,7 +149,7 @@
<u-button type="warning"
size="small"
class="action-btn"
- :disabled="item.inspectState || item.checkName !== ''"
+ :disabled="!!item.inspectState || item.checkName !== ''"
@click.stop="assignInspector(item)">
鍒嗛厤妫�楠屽憳
</u-button>
diff --git a/src/pages/qualityManagement/materialInspection/index.vue b/src/pages/qualityManagement/materialInspection/index.vue
index eb4a140..6b5fe19 100644
--- a/src/pages/qualityManagement/materialInspection/index.vue
+++ b/src/pages/qualityManagement/materialInspection/index.vue
@@ -121,7 +121,7 @@
<u-button type="primary"
size="small"
class="action-btn"
- :disabled="item.inspectState"
+ :disabled="!!item.inspectState"
@click.stop="startInspection(item)">
缂栬緫
</u-button>
@@ -134,7 +134,7 @@
<u-button type="success"
size="small"
class="action-btn"
- :disabled="item.inspectState"
+ :disabled="!!item.inspectState"
@click.stop="submitInspection(item)">
鎻愪氦
</u-button>
@@ -149,13 +149,18 @@
<u-button type="warning"
size="small"
class="action-btn"
- :disabled="item.inspectState || item.checkName !== ''"
+ :disabled="!!item.inspectState || item.checkName !== ''"
@click.stop="assignInspector(item)">
鍒嗛厤妫�楠屽憳
</u-button>
</view>
</view>
</view>
+ </view>
+ <view v-else-if="tableLoading"
+ class="loading-wrap">
+ <up-loading-icon mode="circle" />
+ <text class="loading-text">鍔犺浇涓�...</text>
</view>
<view v-else
class="no-data">
@@ -295,9 +300,14 @@
}));
});
- // 杩斿洖涓婁竴椤�
+ // 杩斿洖涓婁竴椤碉紙娑堟伅閫氱煡鐩存帴杩涘叆绛夋棤涓婄骇椤甸潰鏃跺厹搴曞洖宸ヤ綔鍙帮級
const goBack = () => {
- uni.navigateBack();
+ const pages = getCurrentPages();
+ if (pages.length > 1) {
+ uni.navigateBack();
+ return;
+ }
+ uni.reLaunch({ url: "/pages/works" });
};
// 鏍煎紡鍖栨棩鏈熸椂闂�
@@ -502,7 +512,6 @@
};
onMounted(() => {
- getList();
getUserList();
});
@@ -723,6 +732,20 @@
text-align: center;
}
+ // 鍔犺浇鐘舵��
+ .loading-wrap {
+ padding: 60px 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .loading-text {
+ font-size: 14px;
+ color: #909399;
+ }
+
// 娴姩鎸夐挳
.fab-button {
position: fixed;
diff --git a/src/pages/qualityManagement/processInspection/index.vue b/src/pages/qualityManagement/processInspection/index.vue
index 40358ef..e2503d9 100644
--- a/src/pages/qualityManagement/processInspection/index.vue
+++ b/src/pages/qualityManagement/processInspection/index.vue
@@ -121,7 +121,7 @@
<u-button type="primary"
size="small"
class="action-btn"
- :disabled="item.inspectState"
+ :disabled="!!item.inspectState"
@click.stop="startInspection(item)">
缂栬緫
</u-button>
@@ -134,7 +134,7 @@
<u-button type="success"
size="small"
class="action-btn"
- :disabled="item.inspectState"
+ :disabled="!!item.inspectState"
@click.stop="submitInspection(item)">
鎻愪氦
</u-button>
@@ -149,7 +149,7 @@
<u-button type="warning"
size="small"
class="action-btn"
- :disabled="item.inspectState || item.checkName !== ''"
+ :disabled="!!item.inspectState || item.checkName !== ''"
@click.stop="assignInspector(item)">
鍒嗛厤妫�楠屽憳
</u-button>
diff --git a/src/pages/sales/receiptPaymentLedger/detail.vue b/src/pages/sales/receiptPaymentLedger/detail.vue
index b494332..e5602e3 100644
--- a/src/pages/sales/receiptPaymentLedger/detail.vue
+++ b/src/pages/sales/receiptPaymentLedger/detail.vue
@@ -11,12 +11,16 @@
<text class="summary-value">{{ tableData.length }}</text>
</view>
<view class="summary-item">
- <text class="summary-label">寮�绁ㄦ�婚噾棰�</text>
- <text class="summary-value">{{ formatAmount(invoiceTotal) }}</text>
+ <text class="summary-label">鍚堝悓鎬婚噾棰�</text>
+ <text class="summary-value">{{ formatAmount(contractTotal) }}</text>
</view>
<view class="summary-item">
<text class="summary-label">鍥炴鎬婚噾棰�</text>
<text class="summary-value highlight">{{ formatAmount(receiptTotal) }}</text>
+ </view>
+ <view class="summary-item">
+ <text class="summary-label">閫�璐ф�婚噾棰�</text>
+ <text class="summary-value">{{ formatAmount(returnTotal) }}</text>
</view>
<view class="summary-item">
<text class="summary-label">搴旀敹鎬婚噾棰�</text>
@@ -38,25 +42,29 @@
</view>
<text class="item-index">{{ index + 1 }}</text>
</view>
- <view class="item-date">{{ item.happenTime }}</view>
+ <view class="item-date">{{ item.executionDate }}</view>
</view>
<up-divider></up-divider>
<view class="item-details">
<view class="detail-row">
- <text class="detail-label">寮�绁ㄩ噾棰�(鍏�)</text>
- <text class="detail-value">{{ formatAmount(item.invoiceTotal) }}</text>
+ <text class="detail-label">閿�鍞悎鍚屽彿</text>
+ <text class="detail-value">{{ item.salesContractNo || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍚堝悓閲戦(鍏�)</text>
+ <text class="detail-value">{{ formatAmount(item.contractAmount) }}</text>
</view>
<view class="detail-row">
<text class="detail-label">鍥炴閲戦(鍏�)</text>
<text class="detail-value highlight">{{ formatAmount(item.receiptPaymentAmount) }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">搴旀敹閲戦(鍏�)</text>
- <text class="detail-value danger">{{ formatAmount(item.unReceiptPaymentAmount) }}</text>
+ <text class="detail-label">閫�璐ч噾棰�(鍏�)</text>
+ <text class="detail-value">{{ formatAmount(item.returnAmount) }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">鍙戠敓鏃ユ湡</text>
- <text class="detail-value">{{ item.receiptPaymentDate }}</text>
+ <text class="detail-label">搴旀敹閲戦(鍏�)</text>
+ <text class="detail-value danger">{{ formatAmount(item.receiptableAmount) }}</text>
</view>
</view>
</view>
@@ -71,7 +79,7 @@
<script setup>
import { ref, computed, onMounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
- import { customerInteractions } from "@/api/salesManagement/receiptPayment.js";
+ import { customewTransactionsDetails } from "@/api/salesManagement/indicatorStats";
// 瀹㈡埛淇℃伅
const customerId = ref("");
@@ -79,9 +87,9 @@
// 琛ㄦ牸鏁版嵁
const tableData = ref([]);
- const invoiceTotal = computed(() => {
+ const contractTotal = computed(() => {
return tableData.value.reduce((sum, item) => {
- return sum + (parseFloat(item.invoiceTotal) || 0);
+ return sum + (parseFloat(item.contractAmount) || 0);
}, 0);
});
@@ -91,9 +99,15 @@
}, 0);
});
+ const returnTotal = computed(() => {
+ return tableData.value.reduce((sum, item) => {
+ return sum + (parseFloat(item.returnAmount) || 0);
+ }, 0);
+ });
+
const unReceiptTotal = computed(() => {
return tableData.value.reduce((sum, item) => {
- return sum + (parseFloat(item.unReceiptPaymentAmount) || 0);
+ return sum + (parseFloat(item.receiptableAmount) || 0);
}, 0);
});
@@ -129,9 +143,11 @@
size: -1,
};
- customerInteractions(param)
+ customewTransactionsDetails(param)
.then(res => {
- tableData.value = res.data;
+ // 鏄庣粏鎺ュ彛鍙兘杩斿洖鏁扮粍鎴栧垎椤靛璞★紙涓� Web 澶勭悊涓�鑷达級
+ const data = res?.data;
+ tableData.value = Array.isArray(data) ? data : data?.records || [];
closeToast();
})
.catch(() => {
diff --git a/src/pages/sales/receiptPaymentLedger/index.vue b/src/pages/sales/receiptPaymentLedger/index.vue
index 7daa076..6cd2b9c 100644
--- a/src/pages/sales/receiptPaymentLedger/index.vue
+++ b/src/pages/sales/receiptPaymentLedger/index.vue
@@ -26,7 +26,7 @@
<view class="customer-list"
v-if="tableData.length > 0">
<view v-for="(item, index) in tableData"
- :key="item.id"
+ :key="item.customerId ?? index"
class="customer-item"
@click="rowClickMethod(item)">
<view class="item-header">
@@ -47,16 +47,20 @@
<up-divider></up-divider>
<view class="item-details">
<view class="detail-row">
- <text class="detail-label">寮�绁ㄩ噾棰�(鍏�)</text>
- <text class="detail-value">{{ formattedNumber(item.invoiceTotal) }}</text>
+ <text class="detail-label">鍚堝悓閲戦(鍏�)</text>
+ <text class="detail-value">{{ formattedNumber(item.contractAmounts) }}</text>
</view>
<view class="detail-row">
<text class="detail-label">鍥炴閲戦(鍏�)</text>
<text class="detail-value">{{ formattedNumber(item.receiptPaymentAmount) }}</text>
</view>
<view class="detail-row">
+ <text class="detail-label">閫�璐ч噾棰�(鍏�)</text>
+ <text class="detail-value">{{ formattedNumber(item.returnAmount) }}</text>
+ </view>
+ <view class="detail-row">
<text class="detail-label">搴旀敹閲戦(鍏�)</text>
- <text class="detail-value highlight danger">{{ formattedNumber(item.unReceiptPaymentAmount) }}</text>
+ <text class="detail-value highlight danger">{{ formattedNumber(item.receiptableAmount) }}</text>
</view>
</view>
</view>
@@ -72,7 +76,7 @@
<script setup>
import { onMounted, ref, reactive, toRefs } from "vue";
import { onShow } from "@dcloudio/uni-app";
- import { invoiceLedgerSalesAccount } from "@/api/salesManagement/invoiceLedger";
+ import { customewTransactions } from "@/api/salesManagement/indicatorStats";
const tableData = ref([]);
@@ -106,7 +110,7 @@
const getList = () => {
showLoadingToast("鍔犺浇涓�...");
- invoiceLedgerSalesAccount({ ...searchForm.value, ...page })
+ customewTransactions({ ...searchForm.value, ...page })
.then(res => {
tableData.value = res.data.records;
closeToast();
@@ -139,7 +143,7 @@
const rowClickMethod = row => {
// 浣跨敤 uni.setStorageSync 瀛樺偍瀹㈡埛淇℃伅
- uni.setStorageSync("customerId", row.id);
+ uni.setStorageSync("customerId", row.customerId);
// 璺宠浆鍒板洖娆捐褰曟槑缁嗛〉闈�
uni.navigateTo({
url: "/pages/sales/receiptPaymentLedger/detail",
diff --git a/src/pages/sales/salesQuotation/edit.vue b/src/pages/sales/salesQuotation/edit.vue
index bfcb930..bea1354 100644
--- a/src/pages/sales/salesQuotation/edit.vue
+++ b/src/pages/sales/salesQuotation/edit.vue
@@ -267,6 +267,13 @@
.toFixed(2)
)
);
+ const webSubmitTotalAmount = computed(() =>
+ Number(
+ (form.value.products || [])
+ .reduce((sum, item) => sum + Number(item.unitPrice || 0), 0)
+ .toFixed(2)
+ )
+ );
const customerActions = computed(() =>
customerList.value.map(item => ({
name: item.customerName,
@@ -543,8 +550,8 @@
if (!valid || !validateProducts()) return;
loading.value = true;
- // 鍚屾鏈�鏂扮殑鎬婚
- form.value.totalAmount = totalAmount.value;
+ // Web 绔彁浜ゆ椂鎸変骇鍝佸崟浠锋眹鎬� totalAmount锛宻ubtotal 浠嶄繚鐣欐槑缁嗛噾棰濆悎璁$敤浜庡睍绀恒��
+ form.value.totalAmount = webSubmitTotalAmount.value;
form.value.subtotal = totalAmount.value;
const payload = {
diff --git a/src/pages/works.vue b/src/pages/works.vue
index 9e89ddf..8622a8f 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -559,12 +559,12 @@
{
icon: "/static/images/icon/guizhangzhidu.svg",
label: "瀹℃壒绠$悊",
- path: "/pages/oa/ApproveManage/approve-template/index",
+ path: "/pages/cooperativeOffice/collaborativeApproval/approvalManagement",
},
{
icon: "/static/images/icon/xietongshenpi.svg",
label: "鍗忓悓瀹℃壒",
- path: "/pages/oa/ApproveManage/approve-list/index",
+ path: "/pages/cooperativeOffice/collaborativeApproval/index",
},
{
icon: "/static/images/icon/huiyiguanli.svg",
--
Gitblit v1.9.3