From 4039f4dce566ffddf444bed260906acdff0b164f Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期六, 16 五月 2026 15:58:08 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' into dev_NEW_pro_鹤壁
---
src/pages/equipmentManagement/upkeep/index.vue | 2
src/pages/sales/salesAccount/goOut.vue | 976 +++------
src/api/procurementManagement/procurementLedger.js | 10
src/pages/productionManagement/processRoute/items.vue | 10
src/pages/qualityManagement/finalInspection/add.vue | 15
src/pages/qualityManagement/materialInspection/add.vue | 30
src/api/inventoryManagement/stockInventory.js | 101
src/pages/procurementManagement/procurementLedger/detail.vue | 52
src/api/collaborativeApproval/approvalProcess.js | 89
src/pages/productionManagement/productionTraceability/index.vue | 159 +
src/pages/qualityManagement/processInspection/index.vue | 19
src/pages/productionManagement/productionAccounting/index.vue | 22
src/pages/equipmentManagement/repair/add.vue | 47
src/pages/qualityManagement/materialInspection/detail.vue | 3
src/pages/sales/salesQuotation/detail.vue | 54
src/components/CommonUpload.vue | 164 +
src/pages/qualityManagement/processInspection/add.vue | 29
src/pages/qualityManagement/finalInspection/detail.vue | 3
src/pages/cooperativeOffice/collaborativeApproval/approve.vue | 812 ++++----
src/pages/cooperativeOffice/collaborativeApproval/index.vue | 24
src/pages/qualityManagement/finalInspection/index.vue | 19
src/pages/equipmentManagement/upkeep/add.vue | 778 ++++---
src/pages/cooperativeOffice/collaborativeApproval/detail.vue | 1113 ++++------
src/pages/inventoryManagement/stockManagement/index.vue | 133
src/pages/sales/salesQuotation/index.vue | 118
/dev/null | 134 -
src/pages/qualityManagement/processInspection/detail.vue | 5
src/pages/productionManagement/productionReport/index.vue | 56
src/pages/equipmentManagement/repair/index.vue | 6
src/pages/productionManagement/productionOrder/pickingDetail.vue | 2
src/pages/productionManagement/productionReporting/ledger.vue | 26
src/pages/qualityManagement/materialInspection/index.vue | 19
src/pages/sales/salesQuotation/edit.vue | 547 +++--
src/pages/inventoryManagement/stockManagement/Record.vue | 292 ++
34 files changed, 3,077 insertions(+), 2,792 deletions(-)
diff --git a/src/api/collaborativeApproval/approvalProcess.js b/src/api/collaborativeApproval/approvalProcess.js
index 47cf3ec..0d2b129 100644
--- a/src/api/collaborativeApproval/approvalProcess.js
+++ b/src/api/collaborativeApproval/approvalProcess.js
@@ -2,63 +2,72 @@
import request from "@/utils/request";
export function approveProcessListPage(query) {
- return request({
- url: '/approveProcess/list',
- method: 'get',
- params: query,
- })
+ return request({
+ url: "/approveProcess/list",
+ method: "get",
+ params: query,
+ });
}
export function getDept(query) {
- return request({
- url: '/approveProcess/getDept',
- method: 'get',
- params: query,
- })
+ return request({
+ url: "/approveProcess/getDept",
+ method: "get",
+ params: query,
+ });
}
export function approveProcessGetInfo(query) {
- return request({
- url: '/approveProcess/get',
- method: 'get',
- params: query,
- })
+ return request({
+ url: "/approveProcess/get",
+ method: "get",
+ params: query,
+ });
}
// 鏂板瀹℃壒娴佺▼
export function approveProcessAdd(query) {
- return request({
- url: '/approveProcess/add',
- method: 'post',
- data: query,
- })
+ return request({
+ url: "/approveProcess/add",
+ method: "post",
+ data: query,
+ });
}
// 淇敼瀹℃壒娴佺▼
export function approveProcessUpdate(query) {
- return request({
- url: '/approveProcess/update',
- method: 'post',
- data: query,
- })
+ return request({
+ url: "/approveProcess/update",
+ method: "post",
+ data: query,
+ });
}
// 鎻愪氦瀹℃壒
export function updateApproveNode(query) {
- return request({
- url: '/approveNode/updateApproveNode',
- method: 'post',
- data: query,
- })
+ return request({
+ url: "/approveNode/updateApproveNode",
+ method: "post",
+ data: query,
+ });
}
// 鍒犻櫎瀹℃壒娴佺▼
export function approveProcessDelete(query) {
- return request({
- url: '/approveProcess/deleteIds',
- method: 'delete',
- data: query,
- })
+ return request({
+ url: "/approveProcess/deleteIds",
+ method: "delete",
+ data: query,
+ });
}
// 鏌ヨ瀹℃壒娴佺▼
export function approveProcessDetails(query) {
- return request({
- url: '/approveNode/details/' + query,
- method: 'get',
- })
-}
\ No newline at end of file
+ return request({
+ url: "/approveNode/details/" + query,
+ method: "get",
+ });
+}
+
+// 瀹℃壒璇︽儏
+export function getDeliveryDetailByShippingNo(query) {
+ return request({
+ url: "/shippingInfo/getDateilByShippingNo",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/inventoryManagement/stockInventory.js b/src/api/inventoryManagement/stockInventory.js
index dfa5f46..328657c 100644
--- a/src/api/inventoryManagement/stockInventory.js
+++ b/src/api/inventoryManagement/stockInventory.js
@@ -1,61 +1,78 @@
import request from "@/utils/request";
// 鍒嗛〉鏌ヨ搴撳瓨璁板綍鍒楄〃
-export const getStockInventoryListPage = (params) => {
- return request({
- url: "/stockInventory/pagestockInventory",
- method: "get",
- params,
- });
+export const getStockInventoryListPage = params => {
+ return request({
+ url: "/stockInventory/pagestockInventory",
+ method: "get",
+ params,
+ });
+};
+
+// 鍒嗛〉鏌ヨ鑱斿悎搴撳瓨璁板綍鍒楄〃锛堝寘鍚晢鍝佷俊鎭級
+export const getStockInventoryListPageCombined = params => {
+ return request({
+ url: "/stockInventory/pageListCombinedStockInventory",
+ method: "get",
+ params,
+ });
};
// 鍒涘缓搴撳瓨璁板綍
-export const createStockInventory = (params) => {
- return request({
- url: "/stockInventory/addstockInventory",
- method: "post",
- data: params,
- });
+export const createStockInventory = params => {
+ return request({
+ url: "/stockInventory/addstockInventory",
+ method: "post",
+ data: params,
+ });
};
// 鍑忓皯搴撳瓨璁板綍
-export const subtractStockInventory = (params) => {
- return request({
- url: "/stockInventory/subtractStockInventory",
- method: "post",
- data: params,
- });
+export const subtractStockInventory = params => {
+ return request({
+ url: "/stockInventory/subtractStockInventory",
+ method: "post",
+ data: params,
+ });
};
-export const getStockInventoryReportList = (params) => {
- return request({
- url: "/stockInventory/stockInventoryPage",
- method: "get",
- params,
- });
+export const getStockInventoryReportList = params => {
+ return request({
+ url: "/stockInventory/stockInventoryPage",
+ method: "get",
+ params,
+ });
};
-export const getStockInventoryInAndOutReportList = (params) => {
- return request({
- url: "/stockInventory/stockInAndOutRecord",
- method: "get",
- params,
- });
+export const getStockInventoryInAndOutReportList = params => {
+ return request({
+ url: "/stockInventory/stockInAndOutRecord",
+ method: "get",
+ params,
+ });
};
// 鍐荤粨搴撳瓨璁板綍
-export const frozenStockInventory = (params) => {
- return request({
- url: "/stockInventory/frozenStock",
- method: "post",
- data: params,
- });
+export const frozenStockInventory = params => {
+ return request({
+ url: "/stockInventory/frozenStock",
+ method: "post",
+ data: params,
+ });
};
// 瑙e喕搴撳瓨璁板綍
-export const thawStockInventory = (params) => {
- return request({
- url: "/stockInventory/thawStock",
- method: "post",
- data: params,
- });
+export const thawStockInventory = params => {
+ return request({
+ url: "/stockInventory/thawStock",
+ method: "post",
+ data: params,
+ });
+};
+
+export const getStockInventoryByModelId = productModelId => {
+ return request({
+ url: "/stockInventory/getByModelId",
+ method: "get",
+ params: { productModelId },
+ });
};
diff --git a/src/api/procurementManagement/procurementLedger.js b/src/api/procurementManagement/procurementLedger.js
index 0a05d2e..5c0bb83 100644
--- a/src/api/procurementManagement/procurementLedger.js
+++ b/src/api/procurementManagement/procurementLedger.js
@@ -72,6 +72,16 @@
method: "get",
});
}
+
+// 鏌ヨ閲囪喘璇︽儏
+export function getPurchaseByCode(query) {
+ return request({
+ url: "/purchase/ledger/getPurchaseByCode",
+ method: "get",
+ params: query,
+ });
+}
+
export function approveProcessGetInfo(query) {
return request({
url: '/approveProcess/get',
diff --git a/src/components/CommonUpload.vue b/src/components/CommonUpload.vue
new file mode 100644
index 0000000..8910206
--- /dev/null
+++ b/src/components/CommonUpload.vue
@@ -0,0 +1,164 @@
+<template>
+ <view class="common-upload">
+ <u-upload
+ :fileList="internalFileList"
+ @afterRead="afterRead"
+ @delete="deleteFile"
+ :name="name"
+ :multiple="multiple"
+ :maxCount="maxCount"
+ :accept="accept"
+ :disabled="disabled"
+ ></u-upload>
+ </view>
+</template>
+
+<script setup>
+import { ref, watch, onMounted } from 'vue';
+import { getToken } from "@/utils/auth";
+import config from "@/config";
+
+const props = defineProps({
+ // 鐖剁粍浠朵紶鍏ョ殑鏂囦欢鍒楄〃锛堝搴斿悗绔瓨鍌ㄧ殑瀵硅薄鍒楄〃锛�
+ modelValue: {
+ type: Array,
+ default: () => []
+ },
+ // 鏈�澶т笂浼犳暟閲�
+ maxCount: {
+ type: Number,
+ default: 9
+ },
+ // 鏄惁鏀寔澶氶��
+ multiple: {
+ type: Boolean,
+ default: true
+ },
+ // 鎺ュ彈鐨勬枃浠剁被鍨�
+ accept: {
+ type: String,
+ default: 'image'
+ },
+ // 涓婁紶鎺ュ彛瀵瑰簲鐨勫弬鏁板悕
+ name: {
+ type: String,
+ default: 'file'
+ },
+ // 鏄惁绂佺敤
+ disabled: {
+ type: Boolean,
+ default: false
+ }
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+// 鐢ㄤ簬 u-upload 鏄剧ず鐨勫唴閮ㄥ垪琛�
+const internalFileList = ref([]);
+
+// 鐩戝惉澶栭儴 modelValue 鍙樺寲锛屽悓姝ュ埌鍐呴儴鏄剧ず鍒楄〃
+watch(() => props.modelValue, (newVal) => {
+ if (newVal) {
+ internalFileList.value = newVal.map(item => ({
+ ...item,
+ url: item.url || item.previewURL,
+ status: 'success',
+ message: ''
+ }));
+ }
+}, { immediate: true, deep: true });
+
+const showToast = (message) => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+};
+
+// 涓婁紶閫昏緫
+const uploadFilePromise = (url) => {
+ return new Promise((resolve, reject) => {
+ uni.uploadFile({
+ url: config.baseUrl + "/common/upload",
+ filePath: url,
+ name: "files", // 娉ㄦ剰锛氳繖閲屾牴鎹師浠g爜鏄� "files"
+ header: {
+ Authorization: "Bearer " + getToken(),
+ },
+ success: (res) => {
+ try {
+ const data = JSON.parse(res.data);
+ if (data.code === 200) {
+ // 濡傛灉杩斿洖鐨勬槸鏁扮粍锛屽彇绗竴涓厓绱�
+ const resultData = Array.isArray(data.data) ? data.data[0] : data.data;
+
+ // 澶勭悊 url 璧嬪��
+ if (!resultData.url && resultData.previewURL) {
+ resultData.url = resultData.previewURL;
+ }
+ // 鍏煎鍘熶唬鐮佷腑鐨� name 璧嬪��
+ if (!resultData.name && resultData.originalFilename) {
+ resultData.name = resultData.originalFilename;
+ }
+
+ resolve(resultData);
+ } else {
+ reject(data.msg || "涓婁紶澶辫触");
+ }
+ } catch (e) {
+ reject("瑙f瀽鍝嶅簲澶辫触");
+ }
+ },
+ fail: (err) => {
+ reject(err);
+ },
+ });
+ });
+};
+
+// 涓婁紶鍚庣殑澶勭悊
+const afterRead = async (event) => {
+ let lists = [].concat(event.file);
+ let currentModelValue = [...props.modelValue];
+
+ // 鍏堝湪鍐呴儴鍒楄〃涓坊鍔犲崰浣嶏紙涓婁紶涓姸鎬侊級
+ lists.forEach(item => {
+ internalFileList.value.push({
+ ...item,
+ status: 'uploading',
+ message: '涓婁紶涓�'
+ });
+ });
+
+ for (let i = 0; i < lists.length; i++) {
+ try {
+ const result = await uploadFilePromise(lists[i].url);
+
+ // 鏇存柊 modelValue
+ currentModelValue.push(result);
+ emit('update:modelValue', currentModelValue);
+
+ } catch (e) {
+ // 濡傛灉涓婁紶澶辫触锛屼粠鍐呴儴鍒楄〃涓Щ闄ゅ垰鎵嶆坊鍔犵殑椤�
+ const errorIndex = internalFileList.value.findIndex(item => item.status === 'uploading');
+ if (errorIndex > -1) {
+ internalFileList.value.splice(errorIndex, 1);
+ }
+ showToast(typeof e === "string" ? e : "涓婁紶澶辫触");
+ }
+ }
+};
+
+// 鍒犻櫎澶勭悊
+const deleteFile = (event) => {
+ const newList = [...props.modelValue];
+ newList.splice(event.index, 1);
+ emit('update:modelValue', newList);
+};
+</script>
+
+<style scoped lang="scss">
+.common-upload {
+ width: 100%;
+}
+</style>
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/approve.vue b/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
index aaad83e..b3c8687 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
@@ -1,8 +1,7 @@
<template>
<view class="approve-page">
-
- <PageHeader title="瀹℃牳" @back="goBack" />
-
+ <PageHeader title="瀹℃牳"
+ @back="goBack" />
<!-- 鐢宠淇℃伅 -->
<view class="application-info">
<view class="info-header">
@@ -25,7 +24,6 @@
<text class="info-label">鐢宠鏃ユ湡</text>
<text class="info-value">{{ approvalData.approveTime }}</text>
</view>
-
<!-- approveType=2 璇峰亣鐩稿叧瀛楁 -->
<template v-if="approvalData.approveType === 2">
<view class="info-row">
@@ -37,462 +35,472 @@
<text class="info-value">{{ approvalData.endDate || '-' }}</text>
</view>
</template>
-
<!-- approveType=3 鍑哄樊鐩稿叧瀛楁 -->
- <view v-if="approvalData.approveType === 3" class="info-row">
+ <view v-if="approvalData.approveType === 3"
+ class="info-row">
<text class="info-label">鍑哄樊鍦扮偣</text>
<text class="info-value">{{ approvalData.location || '-' }}</text>
</view>
-
<!-- approveType=4 鎶ラ攢鐩稿叧瀛楁 -->
- <view v-if="approvalData.approveType === 4" class="info-row">
+ <view v-if="approvalData.approveType === 4"
+ class="info-row">
<text class="info-label">鎶ラ攢閲戦</text>
<text class="info-value">{{ approvalData.price ? `楼${approvalData.price}` : '-' }}</text>
</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="{
+ <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>
+ <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 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>
+ <text v-if="step.approveTime"
+ class="step-time">{{ step.approveTime }}</text>
</view>
-
- <view v-if="step.opinion" class="step-opinion">
+ <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;">
+ <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;" />
+ <image :src="step.urlTem"
+ mode="widthFix"
+ style="width:180px;border-radius:6px;border:1px solid #eee;" />
</view>
</view>
</view>
</view>
</view>
-
<!-- 瀹℃牳鎰忚杈撳叆 -->
- <view v-if="canApprove" class="approval-input">
+ <view v-if="canApprove"
+ class="approval-input">
<view class="input-header">
<text class="input-title">瀹℃牳鎰忚</text>
</view>
-
<view class="input-content">
- <u-textarea
- v-model="approvalOpinion"
- rows="4"
- placeholder="璇疯緭鍏ュ鏍告剰瑙�"
- maxlength="200"
- count
- />
+ <u-textarea v-model="approvalOpinion"
+ rows="4"
+ placeholder="璇疯緭鍏ュ鏍告剰瑙�"
+ maxlength="200"
+ count />
</view>
</view>
-
<!-- 搴曢儴鎿嶄綔鎸夐挳 -->
- <view v-if="canApprove" class="footer-actions">
- <u-button class="reject-btn" @click="handleReject">椹冲洖</u-button>
- <u-button class="approve-btn" @click="handleApprove">閫氳繃</u-button>
+ <view v-if="canApprove"
+ class="footer-actions">
+ <u-button class="reject-btn"
+ @click="handleReject">椹冲洖</u-button>
+ <u-button class="approve-btn"
+ @click="handleApprove">閫氳繃</u-button>
</view>
</view>
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
-import { approveProcessGetInfo, approveProcessDetails, updateApproveNode } from '@/api/collaborativeApproval/approvalProcess'
-import useUserStore from '@/store/modules/user'
-const showToast = (message) => {
- uni.showToast({
- title: message,
- icon: 'none'
- })
-}
-import PageHeader from "@/components/PageHeader.vue";
+ import { ref, onMounted, computed } from "vue";
+ import {
+ approveProcessGetInfo,
+ approveProcessDetails,
+ updateApproveNode,
+ } from "@/api/collaborativeApproval/approvalProcess";
+ import useUserStore from "@/store/modules/user";
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+ import PageHeader from "@/components/PageHeader.vue";
-const userStore = useUserStore()
-const approvalData = ref({})
-const approvalSteps = ref([])
-const approvalOpinion = ref('')
-const approveId = ref('')
+ const userStore = useUserStore();
+ const approvalData = ref({});
+ const approvalSteps = ref([]);
+ const approvalOpinion = ref("");
+ const approveId = ref("");
-// 浠庤鎯呮帴鍙e瓧娈靛榻� canApprove锛氫粎褰撴湁 isShen 鐨勮妭鐐规椂鍙鎵�
-const canApprove = computed(() => {
- return approvalSteps.value.some(step => step.isShen === true)
-})
+ // 浠庤鎯呮帴鍙e瓧娈靛榻� canApprove锛氫粎褰撴湁 isShen 鐨勮妭鐐规椂鍙鎵�
+ const canApprove = computed(() => {
+ return approvalSteps.value.some(step => step.isShen === true);
+ });
-onMounted(() => {
- approveId.value = uni.getStorageSync('approveId')
- if (approveId.value) {
- loadApprovalData()
- }
-})
-
-const loadApprovalData = () => {
- // 鍩烘湰鐢宠淇℃伅
- approveProcessGetInfo({ id: approveId.value }).then(res => {
- approvalData.value = res.data || {}
- })
- // 瀹℃壒鑺傜偣璇︽儏
- approveProcessDetails(approveId.value).then(res => {
- 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
- }
- })
- })
-}
-
-const goBack = () => {
- uni.removeStorageSync('approveId');
- uni.navigateBack()
-}
-
-const submitForm = (status) => {
- // 鍙�夛細鏍¢獙瀹℃牳鎰忚
- if (!approvalOpinion.value?.trim()) {
- showToast('璇疯緭鍏ュ鏍告剰瑙�')
- return
- }
- // 鎵惧埌褰撳墠鍙鎵硅妭鐐�
- const filteredActivities = activities.value.filter(activity => activity.isShen)
- if (!filteredActivities.length) {
- showToast('褰撳墠鏃犲彲瀹℃壒鑺傜偣')
- return
- }
- // 鍐欏叆鐘舵�佸拰鎰忚
- filteredActivities[0].approveNodeStatus = status
- filteredActivities[0].approveNodeReason = approvalOpinion.value || ''
- // 璁$畻鏄惁涓烘渶鍚庝竴姝�
- const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length - 1
- // 璋冪敤鍚庣
- updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
- const msg = status === 1 ? '瀹℃壒閫氳繃' : '瀹℃壒宸查┏鍥�'
- showToast(msg)
- // 鎻愮ず鍚庤繑鍥炰笂涓�涓〉闈�
- setTimeout(() => {
- goBack() // 鍐呴儴鏄� uni.navigateBack()
- }, 800)
- })
-}
-
-const handleApprove = () => {
- uni.showModal({
- title: '纭鎿嶄綔',
- content: '纭畾瑕侀�氳繃姝ゅ鎵瑰悧锛�',
- success: (res) => {
- if (res.confirm) submitForm(1)
+ onMounted(() => {
+ approveId.value = uni.getStorageSync("approveId");
+ if (approveId.value) {
+ loadApprovalData();
}
- })
-}
+ });
-const handleReject = () => {
- uni.showModal({
- title: '纭鎿嶄綔',
- content: '纭畾瑕侀┏鍥炴瀹℃壒鍚楋紵',
- success: (res) => {
- if (res.confirm) submitForm(2)
+ const loadApprovalData = () => {
+ // 鍩烘湰鐢宠淇℃伅
+ approveProcessGetInfo({ id: approveId.value }).then(res => {
+ approvalData.value = res.data || {};
+ });
+ // 瀹℃壒鑺傜偣璇︽儏
+ approveProcessDetails(approveId.value).then(res => {
+ 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,
+ };
+ });
+ });
+ };
+
+ const goBack = () => {
+ uni.removeStorageSync("approveId");
+ uni.navigateBack();
+ };
+
+ const submitForm = status => {
+ // 鍙�夛細鏍¢獙瀹℃牳鎰忚
+ if (!approvalOpinion.value?.trim()) {
+ showToast("璇疯緭鍏ュ鏍告剰瑙�");
+ return;
}
- })
-}
-// 鍘熷鑺傜偣鏁版嵁锛堢敤浜庢彁浜ら�昏緫锛�
-const activities = ref([])
+ // 鎵惧埌褰撳墠鍙鎵硅妭鐐�
+ const filteredActivities = activities.value.filter(
+ activity => activity.isShen
+ );
+ if (!filteredActivities.length) {
+ showToast("褰撳墠鏃犲彲瀹℃壒鑺傜偣");
+ return;
+ }
+ // 鍐欏叆鐘舵�佸拰鎰忚
+ filteredActivities[0].approveNodeStatus = status;
+ filteredActivities[0].approveNodeReason = approvalOpinion.value || "";
+ // 璁$畻鏄惁涓烘渶鍚庝竴姝�
+ const isLast =
+ activities.value.findIndex(a => a.isShen) === activities.value.length - 1;
+ // 璋冪敤鍚庣
+ updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
+ const msg = status === 1 ? "瀹℃壒閫氳繃" : "瀹℃壒宸查┏鍥�";
+ showToast(msg);
+ // 鎻愮ず鍚庤繑鍥炰笂涓�涓〉闈�
+ setTimeout(() => {
+ goBack(); // 鍐呴儴鏄� uni.navigateBack()
+ }, 800);
+ });
+ };
+
+ const handleApprove = () => {
+ uni.showModal({
+ title: "纭鎿嶄綔",
+ content: "纭畾瑕侀�氳繃姝ゅ鎵瑰悧锛�",
+ success: res => {
+ if (res.confirm) submitForm(1);
+ },
+ });
+ };
+
+ const handleReject = () => {
+ uni.showModal({
+ title: "纭鎿嶄綔",
+ content: "纭畾瑕侀┏鍥炴瀹℃壒鍚楋紵",
+ success: res => {
+ if (res.confirm) submitForm(2);
+ },
+ });
+ };
+ // 鍘熷鑺傜偣鏁版嵁锛堢敤浜庢彁浜ら�昏緫锛�
+ const activities = ref([]);
</script>
<style scoped lang="scss">
-.approve-page {
- min-height: 100vh;
- background: #f8f9fa;
- padding-bottom: 80px;
-}
-
-.header {
- display: flex;
- align-items: center;
- background: #fff;
- padding: 16px 20px;
- border-bottom: 1px solid #f0f0f0;
- position: sticky;
- top: 0;
- z-index: 100;
-}
-
-.title {
- flex: 1;
- text-align: center;
- font-size: 18px;
- font-weight: 600;
- color: #333;
-}
-
-.application-info {
- background: #fff;
- margin: 16px;
- border-radius: 12px;
- overflow: hidden;
-}
-
-.info-header {
- padding: 16px;
- border-bottom: 1px solid #f0f0f0;
- background: #f8f9fa;
-}
-
-.info-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
-}
-
-.info-content {
- padding: 16px;
-}
-
-.info-row {
- display: flex;
- align-items: center;
- margin-bottom: 12px;
-
- &:last-child {
- margin-bottom: 0;
+ .approve-page {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 80px;
}
-}
-.info-label {
- font-size: 14px;
- color: #666;
- width: 80px;
- flex-shrink: 0;
-}
+ .header {
+ display: flex;
+ align-items: center;
+ background: #fff;
+ padding: 16px 20px;
+ border-bottom: 1px solid #f0f0f0;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ }
-.info-value {
- font-size: 14px;
- color: #333;
- flex: 1;
-}
+ .title {
+ flex: 1;
+ text-align: center;
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
-.approval-process {
- background: #fff;
- margin: 16px;
- border-radius: 12px;
- overflow: hidden;
-}
+ .application-info {
+ background: #fff;
+ margin: 16px;
+ border-radius: 12px;
+ overflow: hidden;
+ }
-.process-header {
- padding: 16px;
- border-bottom: 1px solid #f0f0f0;
- background: #f8f9fa;
-}
+ .info-header {
+ padding: 16px;
+ border-bottom: 1px solid #f0f0f0;
+ background: #f8f9fa;
+ }
-.process-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
-}
+ .info-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
-.process-steps {
- padding: 20px;
-}
+ .info-content {
+ padding: 16px;
+ }
-.process-step {
- display: flex;
- position: relative;
- margin-bottom: 24px;
-
- &:last-child {
- margin-bottom: 0;
-
- .step-line {
- display: none;
+ .info-row {
+ display: flex;
+ align-items: center;
+ margin-bottom: 12px;
+
+ &:last-child {
+ margin-bottom: 0;
}
}
-}
-.step-indicator {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-right: 16px;
-}
+ .info-label {
+ font-size: 14px;
+ color: #666;
+ width: 80px;
+ flex-shrink: 0;
+ }
-.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;
-}
+ .info-value {
+ font-size: 14px;
+ color: #333;
+ flex: 1;
+ }
-.process-step.completed .step-dot {
- background: #52c41a;
- color: #fff;
-}
+ .approval-process {
+ background: #fff;
+ margin: 16px;
+ border-radius: 12px;
+ overflow: hidden;
+ }
-.process-step.current .step-dot {
- background: #1890ff;
- color: #fff;
- animation: pulse 2s infinite;
-}
+ .process-header {
+ padding: 16px;
+ border-bottom: 1px solid #f0f0f0;
+ background: #f8f9fa;
+ }
-.process-step.pending .step-dot {
- background: #d9d9d9;
- color: #999;
-}
+ .process-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
-.step-line {
- width: 2px;
- height: 40px;
- background: #d9d9d9;
- margin-top: 8px;
-}
+ .process-steps {
+ padding: 20px;
+ }
-.process-step.completed .step-line {
- background: #52c41a;
-}
+ .process-step {
+ display: flex;
+ position: relative;
+ margin-bottom: 24px;
-.process-step.rejected .step-dot {
- background: #ff4d4f;
- color: #fff;
-}
-.process-step.rejected .step-line {
- background: #ff4d4f;
-}
+ &:last-child {
+ margin-bottom: 0;
-.step-content {
- flex: 1;
- padding-top: 4px;
-}
+ .step-line {
+ display: none;
+ }
+ }
+ }
-.step-info {
- margin-bottom: 8px;
-}
+ .step-indicator {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin-right: 16px;
+ }
-.step-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- display: block;
- margin-bottom: 4px;
-}
+ .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;
+ }
-.step-approver {
- font-size: 14px;
- color: #666;
- display: block;
- margin-bottom: 4px;
-}
+ .process-step.completed .step-dot {
+ background: #52c41a;
+ color: #fff;
+ }
-.step-time {
- font-size: 12px;
- color: #999;
- display: block;
-}
+ .process-step.current .step-dot {
+ background: #1890ff;
+ color: #fff;
+ animation: pulse 2s infinite;
+ }
-.step-opinion {
- background: #f8f9fa;
- padding: 12px;
- border-radius: 8px;
- border-left: 4px solid #52c41a;
-}
+ .process-step.pending .step-dot {
+ background: #d9d9d9;
+ color: #999;
+ }
-.opinion-label {
- font-size: 12px;
- color: #666;
- display: block;
- margin-bottom: 4px;
-}
+ .step-line {
+ width: 2px;
+ height: 40px;
+ background: #d9d9d9;
+ margin-top: 8px;
+ }
-.opinion-content {
- font-size: 14px;
- color: #333;
- line-height: 1.5;
-}
+ .process-step.completed .step-line {
+ background: #52c41a;
+ }
-.approval-input {
- background: #fff;
- margin: 16px;
- border-radius: 12px;
- overflow: hidden;
-}
+ .process-step.rejected .step-dot {
+ background: #ff4d4f;
+ color: #fff;
+ }
+ .process-step.rejected .step-line {
+ background: #ff4d4f;
+ }
-.input-header {
- padding: 16px;
- border-bottom: 1px solid #f0f0f0;
- background: #f8f9fa;
-}
+ .step-content {
+ flex: 1;
+ padding-top: 4px;
+ }
-.input-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
-}
+ .step-info {
+ margin-bottom: 8px;
+ }
-.input-content {
- padding: 16px;
-}
+ .step-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ display: block;
+ margin-bottom: 4px;
+ }
-.footer-actions {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- display: flex;
- justify-content: space-around;
- align-items: center;
- padding: 16px;
- box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
- z-index: 1000;
-}
+ .step-approver {
+ font-size: 14px;
+ color: #666;
+ display: block;
+ margin-bottom: 4px;
+ }
-.reject-btn {
+ .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 {
+ background: #fff;
+ margin: 16px;
+ border-radius: 12px;
+ overflow: hidden;
+ }
+
+ .input-header {
+ padding: 16px;
+ border-bottom: 1px solid #f0f0f0;
+ background: #f8f9fa;
+ }
+
+ .input-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .input-content {
+ padding: 16px;
+ }
+
+ .footer-actions {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: #fff;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ padding: 16px;
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
+ z-index: 1000;
+ }
+
+ .reject-btn {
width: 120px;
background: #ff4d4f;
color: #fff;
@@ -503,47 +511,47 @@
background: #52c41a;
color: #fff;
}
-
+
/* 閫傞厤u-button鏍峰紡 */
:deep(.u-button) {
border-radius: 6px;
}
-@keyframes pulse {
- 0% {
- box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
+ @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);
+ }
}
- 70% {
- box-shadow: 0 0 0 10px rgba(24, 144, 255, 0);
+ .signature-section {
+ background: #fff;
+ padding: 12px 16px 16px;
+ border-top: 1px solid #f0f0f0;
}
- 100% {
- box-shadow: 0 0 0 0 rgba(24, 144, 255, 0);
+ .signature-header {
+ margin-bottom: 8px;
}
-}
-.signature-section {
- background: #fff;
- padding: 12px 16px 16px;
- border-top: 1px solid #f0f0f0;
-}
-.signature-header {
- margin-bottom: 8px;
-}
-.signature-title {
- font-size: 14px;
- font-weight: 600;
- color: #333;
-}
-.signature-box {
- width: 100%;
- height: 180px;
- background: #fff;
- border: 1px dashed #d9d9d9;
- border-radius: 8px;
- overflow: hidden;
-}
-.signature-actions {
- margin-top: 8px;
- display: flex;
- justify-content: flex-end;
-}
+ .signature-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ }
+ .signature-box {
+ width: 100%;
+ height: 180px;
+ background: #fff;
+ border: 1px dashed #d9d9d9;
+ border-radius: 8px;
+ overflow: hidden;
+ }
+ .signature-actions {
+ margin-top: 8px;
+ display: flex;
+ justify-content: flex-end;
+ }
</style>
\ No newline at end of file
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/detail.vue b/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
index 04a4c18..16f9923 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
@@ -1,6 +1,6 @@
<template>
<view class="account-detail">
- <PageHeader title="瀹℃壒娴佺▼"
+ <PageHeader :title="operationType === 'detail' ? '璇︽儏' : '瀹℃壒娴佺▼'"
@back="goBack" />
<!-- 琛ㄥ崟鍖哄煙 -->
<u-form ref="formRef"
@@ -8,38 +8,39 @@
:rules="rules"
:model="form"
label-width="140rpx">
- <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"
+ <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-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"
label="鐢宠浜�"
required>
<u-input v-model="form.approveUserName"
@@ -57,141 +58,277 @@
<up-icon name="arrow-right"
@click="showDatePicker"></up-icon>
</template>
- </u-form-item>
- <!-- approveType=2 璇峰亣鐩稿叧瀛楁 -->
- <template v-if="approveType === 2">
- <u-form-item prop="startDate"
- label="寮�濮嬫椂闂�"
+ </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>
+ </template>
+ <!-- approveType=3 鍑哄樊鐩稿叧瀛楁 -->
+ <u-form-item v-if="approveType === 3"
+ prop="location"
+ 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-input v-model="form.location"
+ placeholder="璇疯緭鍏ュ嚭宸湴鐐�"
+ clearable />
</u-form-item>
- <u-form-item prop="endDate"
- label="缁撴潫鏃堕棿"
+ <!-- approveType=4 鎶ラ攢鐩稿叧瀛楁 -->
+ <u-form-item v-if="approveType === 4"
+ prop="price"
+ 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-input v-model="form.price"
+ type="number"
+ placeholder="璇疯緭鍏ユ姤閿�閲戦"
+ clearable />
</u-form-item>
</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 />
+ <!-- 鎶ヤ环瀹℃壒璇︽儏 -->
+ <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 v-if="operationType !== 'detail'"
+ label="鍥剧墖闄勪欢"
+ prop="storageBlobDTOS"
+ border-bottom>
+ <CommonUpload v-model="form.storageBlobDTOS" />
</u-form-item>
</u-form>
<!-- 閫夋嫨鍣ㄥ脊绐� -->
- <up-action-sheet :show="showPicker"
- :actions="productOptions"
- title="閫夋嫨閮ㄩ棬"
- @select="onConfirm"
- @close="showPicker = false" />
- <!-- 鏃ユ湡閫夋嫨鍣� -->
- <up-popup :show="showDate"
- mode="bottom"
- @close="showDate = false">
- <up-datetime-picker :show="true"
- v-model="currentDate"
- @confirm="onDateConfirm"
- @cancel="showDate = false"
- mode="date" />
- </up-popup>
- <!-- 璇峰亣寮�濮嬫椂闂撮�夋嫨鍣� -->
- <up-popup :show="showStartDate"
- mode="bottom"
- @close="showStartDate = false">
- <up-datetime-picker :show="true"
- v-model="startDateValue"
- @confirm="onStartDateConfirm"
- @cancel="showStartDate = false"
- mode="date" />
- </up-popup>
- <!-- 璇峰亣缁撴潫鏃堕棿閫夋嫨鍣� -->
- <up-popup :show="showEndDate"
- mode="bottom"
- @close="showEndDate = false">
- <up-datetime-picker :show="true"
- v-model="endDateValue"
- @confirm="onEndDateConfirm"
- @cancel="showEndDate = false"
- mode="date" />
- </up-popup>
- <!-- 瀹℃牳娴佺▼鍖哄煙 -->
- <view class="approval-process">
- <view class="approval-header">
- <text class="approval-title">瀹℃牳娴佺▼</text>
- <text class="approval-desc">姣忎釜姝ラ鍙兘閫夋嫨涓�涓鎵逛汉</text>
- </view>
- <view class="approval-steps">
- <view v-for="(step, stepIndex) in approverNodes"
- :key="stepIndex"
- class="approval-step">
- <view class="step-dot"></view>
- <view class="step-title">
- <text>瀹℃壒浜�</text>
- </view>
- <view class="approver-container">
- <view v-if="step.nickName"
- class="approver-item">
- <view class="approver-avatar">
- <text class="avatar-text">{{ step.nickName.charAt(0) }}</text>
- <view class="status-dot"></view>
- </view>
- <view class="approver-info">
- <text class="approver-name">{{ step.nickName }}</text>
- </view>
- <view class="delete-approver-btn"
- @click="removeApprover(stepIndex)">脳</view>
- </view>
- <view v-else
- class="add-approver-btn"
- @click="addApprover(stepIndex)">
- <view class="add-circle">+</view>
- <text class="add-label">閫夋嫨瀹℃壒浜�</text>
- </view>
- </view>
- <view class="step-line"
- v-if="stepIndex < approverNodes.length - 1"></view>
- <view class="delete-step-btn"
- v-if="approverNodes.length > 1"
- @click="removeApprovalStep(stepIndex)">鍒犻櫎鑺傜偣</view>
- </view>
- </view>
- <view class="add-step-btn">
- <u-button icon="plus"
- plain
- type="primary"
- style="width: 100%"
- @click="addApprovalStep">鏂板鑺傜偣</u-button>
- </view>
- </view>
+ <template v-if="operationType !== 'detail'">
+ <up-action-sheet :show="showPicker"
+ :actions="productOptions"
+ title="閫夋嫨閮ㄩ棬"
+ @select="onConfirm"
+ @close="showPicker = false" />
+ <!-- 鏃ユ湡閫夋嫨鍣� -->
+ <up-popup :show="showDate"
+ mode="bottom"
+ @close="showDate = false">
+ <up-datetime-picker :show="true"
+ v-model="currentDate"
+ @confirm="onDateConfirm"
+ @cancel="showDate = false"
+ mode="date" />
+ </up-popup>
+ <!-- 璇峰亣寮�濮嬫椂闂撮�夋嫨鍣� -->
+ <up-popup :show="showStartDate"
+ mode="bottom"
+ @close="showStartDate = false">
+ <up-datetime-picker :show="true"
+ v-model="startDateValue"
+ @confirm="onStartDateConfirm"
+ @cancel="showStartDate = false"
+ mode="date" />
+ </up-popup>
+ <!-- 璇峰亣缁撴潫鏃堕棿閫夋嫨鍣� -->
+ <up-popup :show="showEndDate"
+ mode="bottom"
+ @close="showEndDate = false">
+ <up-datetime-picker :show="true"
+ v-model="endDateValue"
+ @confirm="onEndDateConfirm"
+ @cancel="showEndDate = false"
+ mode="date" />
+ </up-popup>
+ </template>
<!-- 搴曢儴鎸夐挳 -->
- <view class="footer-btns">
+ <view class="footer-btns"
+ v-if="operationType !== 'detail'">
<u-button class="cancel-btn"
@click="goBack">鍙栨秷</u-button>
<u-button class="save-btn"
@@ -201,8 +338,17 @@
</template>
<script setup>
- import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
+ import {
+ ref,
+ onMounted,
+ onUnmounted,
+ reactive,
+ toRefs,
+ computed,
+ watch,
+ } from "vue";
import PageHeader from "@/components/PageHeader.vue";
+ import CommonUpload from "@/components/CommonUpload.vue";
import useUserStore from "@/store/modules/user";
import { formatDateToYMD } from "@/utils/ruoyi";
import {
@@ -210,14 +356,16 @@
approveProcessGetInfo,
approveProcessAdd,
approveProcessUpdate,
+ getDeliveryDetailByShippingNo,
} from "@/api/collaborativeApproval/approvalProcess";
+ import { getQuotationList } from "@/api/salesManagement/salesQuotation";
+ import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger";
const showToast = message => {
uni.showToast({
title: message,
icon: "none",
});
};
- import { userListNoPageByTenantId } from "@/api/system/user";
const data = reactive({
form: {
@@ -229,8 +377,7 @@
approveDeptId: "",
approveReason: "",
checkResult: "",
- tempFileIds: [],
- approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
+ storageBlobDTOS: [],
startDate: "",
endDate: "",
location: "",
@@ -258,8 +405,6 @@
const productOptions = ref([]);
const operationType = ref("");
const currentApproveStatus = ref("");
- const approverNodes = ref([]);
- const userList = ref([]);
const formRef = ref(null);
const message = ref("");
const showDate = ref(false);
@@ -270,6 +415,19 @@
const endDateValue = 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 => {
@@ -279,20 +437,133 @@
}));
});
};
- const fileList = ref([]);
- let nextApproverId = 2;
const getCurrentinfo = () => {
userStore.getInfo().then(res => {
form.value.approveDeptId = res.user.tenantId;
console.log(res.user.tenantId, "res.user.tenantId");
});
};
+
+ // 鏄剧ず鏃ユ湡閫夋嫨鍣�
+ const showDatePicker = () => {
+ showDate.value = true;
+ };
+
+ // 纭鏃ユ湡閫夋嫨
+ const onDateConfirm = e => {
+ form.value.approveTime = formatDateToYMD(e.value);
+ currentDate.value = formatDateToYMD(e.value);
+ showDate.value = false;
+ };
+
+ // 鏄剧ず璇峰亣寮�濮嬫椂闂撮�夋嫨鍣�
+ const showStartDatePicker = () => {
+ showStartDate.value = true;
+ };
+
+ // 纭璇峰亣寮�濮嬫椂闂撮�夋嫨
+ const onStartDateConfirm = e => {
+ form.value.startDate = formatDateToYMD(e.value);
+ showStartDate.value = false;
+ };
+
+ const showEndDatePicker = () => {
+ showEndDate.value = true;
+ };
+
+ // 纭璇峰亣缁撴潫鏃堕棿閫夋嫨
+ const onEndDateConfirm = e => {
+ form.value.endDate = formatDateToYMD(e.value);
+ 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;
+ });
+ }
+ }
+ };
+
+ // 鐩戝惉瀹℃壒浜嬬敱鍙樺寲锛屽鏋滄槸鐗瑰畾瀹℃壒绫诲瀷鍒欏皾璇曡幏鍙栬鎯�
+ watch(
+ () => form.value.approveReason,
+ newVal => {
+ if (isInitialLoading.value) return;
+ if (
+ newVal &&
+ (isQuotationApproval.value ||
+ isPurchaseApproval.value ||
+ isDeliveryApproval.value)
+ ) {
+ // 寤惰繜涓�浼氬啀璇锋眰锛岄伩鍏嶈緭鍏ヨ繃绋嬩腑棰戠箒瑙﹀彂
+ debounceFetchDetail();
+ }
+ }
+ );
+
+ let timer = null;
+ const debounceFetchDetail = () => {
+ if (timer) clearTimeout(timer);
+ timer = setTimeout(() => {
+ fetchDetailData(form.value);
+ }, 800);
+ };
+
onMounted(async () => {
try {
getProductOptions();
- userListNoPageByTenantId().then(res => {
- userList.value = res.data;
- });
form.value.approveUser = userStore.id;
form.value.approveUserName = userStore.nickName;
form.value.approveTime = getCurrentDate();
@@ -302,57 +573,39 @@
approveType.value = uni.getStorageSync("approveType") || 0;
// 濡傛灉鏄紪杈戞ā寮忥紝浠庢湰鍦板瓨鍌ㄨ幏鍙栨暟鎹�
- if (operationType.value === "edit") {
+ if (operationType.value === "edit" || operationType.value === "detail") {
const storedData = uni.getStorageSync("invoiceLedgerEditRow");
if (storedData) {
const row = JSON.parse(storedData);
- fileList.value = row.commonFileList || [];
- form.value.tempFileIds = fileList.value.map(file => file.id);
currentApproveStatus.value = row.approveStatus;
- approveProcessGetInfo({ id: row.approveId, approveReason: "1" }).then(
- res => {
+ isInitialLoading.value = true;
+ approveProcessGetInfo({ id: row.approveId, approveReason: "1" })
+ .then(res => {
form.value = { ...res.data };
- // 鍙嶆樉瀹℃壒浜�
- if (res.data && res.data.approveUserIds) {
- const userIds = res.data.approveUserIds.split(",");
- approverNodes.value = userIds.map((userId, idx) => {
- const userIdNum = parseInt(userId.trim());
- // 浠巙serList涓壘鍒板搴旂殑鐢ㄦ埛淇℃伅
- const userInfo = userList.value.find(
- user => user.userId === userIdNum
- );
- return {
- id: idx + 1,
- userId: userIdNum,
- nickName: userInfo ? userInfo.nickName : null,
- };
- });
- nextApproverId = userIds.length + 1;
- } else {
- // 鏂板妯″紡锛屽垵濮嬪寲涓�涓┖鐨勫鎵硅妭鐐�
- approverNodes.value = [{ id: 1, userId: null, nickName: null }];
- nextApproverId = 2;
+ // 璁剧疆鍥剧墖鍒楄〃鏄剧ず
+ const fileData =
+ res.data.storageBlobVOS || res.data.commonFileList || [];
+ if (fileData.length > 0) {
+ form.value.storageBlobDTOS = fileData;
}
- }
- );
+ // 鑾峰彇棰濆璇︽儏
+ fetchDetailData(res.data);
+ })
+ .finally(() => {
+ // 寤惰繜涓�浼氶噸缃紝纭繚 watch 涓嶄細琚Е鍙�
+ setTimeout(() => {
+ isInitialLoading.value = false;
+ }, 100);
+ });
}
- } else {
- // 鏂板妯″紡锛屽垵濮嬪寲涓�涓┖鐨勫鎵硅妭鐐�
- approverNodes.value = [{ id: 1, userId: null }];
}
-
- // 鐩戝惉鑱旂郴浜洪�夋嫨浜嬩欢
- uni.$on("selectContact", handleSelectContact);
} catch (error) {
- console.error("鑾峰彇閮ㄩ棬鏁版嵁澶辫触:", error);
+ console.error("鑾峰彇鏁版嵁澶辫触:", error);
}
});
- onUnmounted(() => {
- // 绉婚櫎浜嬩欢鐩戝惉
- uni.$off("selectContact", handleSelectContact);
- });
+ onUnmounted(() => {});
const onConfirm = item => {
// 璁剧疆閫変腑鐨勯儴闂�
@@ -375,13 +628,6 @@
};
const submitForm = () => {
- // 妫�鏌ユ瘡涓鎵规楠ゆ槸鍚﹂兘鏈夊鎵逛汉
- const hasEmptyStep = approverNodes.value.some(step => !step.nickName);
- if (hasEmptyStep) {
- showToast("璇蜂负姣忎釜瀹℃壒姝ラ閫夋嫨瀹℃壒浜�");
- return;
- }
-
// 鎵嬪姩妫�鏌ュ繀濉瓧娈碉紝闃叉鍥犳暟鎹被鍨嬮棶棰樺鑷寸殑鏍¢獙澶辫触
if (!form.value.approveReason || !form.value.approveReason.trim()) {
showToast("璇疯緭鍏ョ敵璇蜂簨鐢�");
@@ -406,26 +652,8 @@
.then(valid => {
if (valid) {
// 琛ㄥ崟鏍¢獙閫氳繃锛屽彲浠ユ彁浜ゆ暟鎹�
- // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
- console.log("approverNodes---", approverNodes.value);
- form.value.approveUserIds = approverNodes.value
- .map(node => node.userId)
- .join(",");
form.value.approveType = approveType.value;
form.value.approveDeptId = Number(form.value.approveDeptId);
- // const submitForm = {
- // approveDeptId: form.value.approveDeptId,
- // approveDeptName: form.value.approveDeptName,
- // approveReason: form.value.approveReason,
- // approveTime: form.value.approveTime,
- // approveType: form.value.approveType,
- // approveUser: form.value.approveUser,
- // approveUserIds: form.value.approveUserIds,
- // endDate: form.value.endDate,
- // startDate: form.value.startDate,
- // };
- // console.log("form.value---", form.value);
- // console.log("submitForm", submitForm);
if (operationType.value === "add" || currentApproveStatus.value == 3) {
approveProcessAdd(form.value).then(res => {
@@ -461,77 +689,6 @@
});
};
- // 澶勭悊鑱旂郴浜洪�夋嫨缁撴灉
- const handleSelectContact = data => {
- const { stepIndex, contact } = data;
- // 灏嗛�変腑鐨勮仈绯讳汉璁剧疆涓哄搴斿鎵规楠ょ殑瀹℃壒浜�
- approverNodes.value[stepIndex].userId = contact.userId;
- approverNodes.value[stepIndex].nickName = contact.nickName;
- };
-
- const addApprover = stepIndex => {
- // 璺宠浆鍒拌仈绯讳汉閫夋嫨椤甸潰
- uni.setStorageSync("stepIndex", stepIndex);
- uni.navigateTo({
- url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect",
- });
- };
-
- const addApprovalStep = () => {
- // 娣诲姞鏂扮殑瀹℃壒姝ラ
- approverNodes.value.push({ userId: null, nickName: null });
- };
-
- const removeApprover = stepIndex => {
- // 绉婚櫎瀹℃壒浜�
- approverNodes.value[stepIndex].userId = null;
- approverNodes.value[stepIndex].nickName = null;
- };
-
- const removeApprovalStep = stepIndex => {
- // 纭繚鑷冲皯淇濈暀涓�涓鎵规楠�
- if (approverNodes.value.length > 1) {
- approverNodes.value.splice(stepIndex, 1);
- } else {
- uni.showToast({
- title: "鑷冲皯闇�瑕佷竴涓鎵规楠�",
- icon: "none",
- });
- }
- };
- // 鏄剧ず鏃ユ湡閫夋嫨鍣�
- const showDatePicker = () => {
- showDate.value = true;
- };
-
- // 纭鏃ユ湡閫夋嫨
- const onDateConfirm = e => {
- form.value.approveTime = formatDateToYMD(e.value);
- currentDate.value = formatDateToYMD(e.value);
- showDate.value = false;
- };
-
- // 鏄剧ず璇峰亣寮�濮嬫椂闂撮�夋嫨鍣�
- const showStartDatePicker = () => {
- showStartDate.value = true;
- };
-
- // 纭璇峰亣寮�濮嬫椂闂撮�夋嫨
- const onStartDateConfirm = e => {
- form.value.startDate = formatDateToYMD(e.value);
- showStartDate.value = false;
- };
-
- const showEndDatePicker = () => {
- showEndDate.value = true;
- };
-
- // 纭璇峰亣缁撴潫鏃堕棿閫夋嫨
- const onEndDateConfirm = e => {
- form.value.endDate = formatDateToYMD(e.value);
- showEndDate.value = false;
- };
-
// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
function getCurrentDate() {
const today = new Date();
@@ -544,238 +701,8 @@
<style scoped lang="scss">
@import "@/static/scss/form-common.scss";
-
- .approval-process {
- background: #fff;
- margin: 16px;
- border-radius: 16px;
- padding: 16px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
- }
-
- .approval-header {
- margin-bottom: 16px;
- }
-
- .approval-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- display: block;
- margin-bottom: 4px;
- }
-
- .approval-desc {
- font-size: 12px;
- color: #999;
- }
-
- /* 鏍峰紡澧炲己涓衡�滅畝娲佸皬鍦嗗湀椋庢牸鈥� */
- .approval-steps {
- padding-left: 22px;
- position: relative;
-
- &::before {
- content: "";
- position: absolute;
- left: 11px;
- top: 40px;
- bottom: 40px;
- width: 2px;
- background: linear-gradient(
- to bottom,
- #e6f7ff 0%,
- #bae7ff 50%,
- #91d5ff 100%
- );
- border-radius: 1px;
- }
- }
-
- .approval-step {
- position: relative;
- margin-bottom: 24px;
-
- &::before {
- content: "";
- position: absolute;
- left: -18px;
- top: 14px; // 浠� 8px 璋冩暣涓� 14px锛屼笌鏂囧瓧涓績瀵归綈
- width: 12px;
- height: 12px;
- background: #fff;
- border: 3px solid #006cfb;
- border-radius: 50%;
- z-index: 2;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
- }
- }
-
- .step-title {
- top: 12px;
- margin-bottom: 12px;
- position: relative;
- margin-left: 6px;
- }
-
- .step-title text {
- font-size: 14px;
- color: #666;
- background: #f0f0f0;
- padding: 4px 12px;
- border-radius: 12px;
- position: relative;
- line-height: 1.4; // 纭繚鏂囧瓧琛岄珮涓�鑷�
- }
-
- .approver-item {
- display: flex;
- align-items: center;
- background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
- border-radius: 16px;
- padding: 16px;
- gap: 12px;
- position: relative;
- border: 1px solid #e6f7ff;
- box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08);
- transition: all 0.3s ease;
- }
-
- .approver-avatar {
- width: 48px;
- height: 48px;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- position: relative;
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
- }
-
- .avatar-text {
- color: #fff;
- font-size: 18px;
- font-weight: 600;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
- }
-
- .approver-info {
- flex: 1;
- position: relative;
- }
-
- .approver-name {
- display: block;
- font-size: 16px;
- color: #333;
- font-weight: 500;
- position: relative;
- }
-
- .approver-dept {
- font-size: 12px;
- color: #999;
- background: rgba(0, 108, 251, 0.05);
- padding: 2px 8px;
- border-radius: 8px;
- display: inline-block;
- position: relative;
-
- &::before {
- content: "";
- position: absolute;
- left: 4px;
- top: 50%;
- transform: translateY(-50%);
- width: 2px;
- height: 2px;
- background: #006cfb;
- border-radius: 50%;
- }
- }
-
- .delete-approver-btn {
- font-size: 16px;
- color: #ff4d4f;
- background: linear-gradient(
- 135deg,
- rgba(255, 77, 79, 0.1) 0%,
- rgba(255, 77, 79, 0.05) 100%
- );
- width: 28px;
- height: 28px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.3s ease;
- position: relative;
- }
-
- .add-approver-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
- border: 2px dashed #006cfb;
- border-radius: 16px;
- padding: 20px;
- color: #006cfb;
- font-size: 14px;
- position: relative;
- transition: all 0.3s ease;
-
- &::before {
- content: "";
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- width: 32px;
- height: 32px;
- border: 2px solid #006cfb;
- border-radius: 50%;
- opacity: 0;
- transition: all 0.3s ease;
- }
- }
-
- .delete-step-btn {
- color: #ff4d4f;
- font-size: 12px;
- background: linear-gradient(
- 135deg,
- rgba(255, 77, 79, 0.1) 0%,
- rgba(255, 77, 79, 0.05) 100%
- );
- padding: 6px 12px;
- border-radius: 12px;
- display: inline-block;
- position: relative;
- transition: all 0.3s ease;
-
- &::before {
- content: "";
- position: absolute;
- left: 6px;
- top: 50%;
- transform: translateY(-50%);
- width: 4px;
- height: 4px;
- background: #ff4d4f;
- border-radius: 50%;
- }
- }
-
- .step-line {
- display: none; // 闅愯棌鍘熸潵鐨勭嚎鏉★紝浣跨敤浼厓绱犱唬鏇�
- }
-
- .add-step-btn {
- display: flex;
- align-items: center;
- justify-content: center;
+ .account-detail {
+ background-color: #fff;
}
.footer-btns {
position: fixed;
@@ -809,121 +736,5 @@
background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
- }
-
- // 鍔ㄧ敾瀹氫箟
- @keyframes pulse {
- 0% {
- transform: scale(1);
- opacity: 1;
- }
- 50% {
- transform: scale(1.2);
- opacity: 0.7;
- }
- 100% {
- transform: scale(1);
- opacity: 1;
- }
- }
-
- @keyframes rotate {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
-
- @keyframes ripple {
- 0% {
- transform: translate(-50%, -50%) scale(0.8);
- opacity: 1;
- }
- 100% {
- transform: translate(-50%, -50%) scale(1.6);
- opacity: 0;
- }
- }
-
- /* 濡傛灉宸叉湁 .step-line锛岃繖閲屾洿绮惧噯瀹氫綅鍒板乏渚т笌灏忓渾鐐瑰榻� */
- .step-line {
- position: absolute;
- left: 4px;
- top: 48px;
- width: 2px;
- height: calc(100% - 48px);
- background: #e5e7eb;
- }
-
- .approver-container {
- display: flex;
- align-items: center;
- background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
- border-radius: 16px;
- gap: 12px;
- padding: 10px 0;
- background: transparent;
- border: none;
- box-shadow: none;
- }
-
- .approver-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 8px 10px;
- background: transparent;
- border: none;
- box-shadow: none;
- border-radius: 0;
- }
-
- .approver-avatar {
- position: relative;
- width: 40px;
- height: 40px;
- border-radius: 50%;
- background: #f3f4f6;
- border: 2px solid #e5e7eb;
- display: flex;
- align-items: center;
- justify-content: center;
- animation: none; /* 绂佺敤鏃嬭浆绛夊姩鐢伙紝鍥炲綊绠�娲� */
- }
-
- .avatar-text {
- font-size: 14px;
- color: #374151;
- font-weight: 600;
- }
-
- .add-approver-btn {
- display: flex;
- align-items: center;
- gap: 8px;
- background: transparent;
- border: none;
- box-shadow: none;
- padding: 0;
- }
-
- .add-approver-btn .add-circle {
- width: 40px;
- height: 40px;
- border: 2px dashed #a0aec0;
- border-radius: 50%;
- color: #6b7280;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22px;
- line-height: 1;
- }
-
- .add-approver-btn .add-label {
- color: #3b82f6;
- font-size: 14px;
}
</style>
\ No newline at end of file
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/index.vue b/src/pages/cooperativeOffice/collaborativeApproval/index.vue
index 3c374ef..910cdc3 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/index.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/index.vue
@@ -97,13 +97,20 @@
</view>
<view class="detail-row">
<view class="actions">
- <!-- <u-button type="primary"
+ <u-button type="primary"
size="small"
class="action-btn edit"
- :disabled="item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4 || item.approveStatus == 8"
+ v-if="!(item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4 || item.approveStatus == 8 || item.approveType == 5 || item.approveType == 6 || item.approveType == 7)"
@click="handleItemClick(item)">
缂栬緫
- </u-button> -->
+ </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)">
+ 璇︽儏
+ </u-button>
<u-button type="success"
size="small"
class="action-btn approve"
@@ -262,6 +269,17 @@
});
};
+ // 鏌ョ湅璇︽儏
+ const handleDetailClick = item => {
+ uni.setStorageSync("invoiceLedgerEditRow", JSON.stringify(item));
+ uni.setStorageSync("operationType", "detail");
+ uni.setStorageSync("approveId", item.approveId);
+ uni.setStorageSync("approveType", props.approveType);
+ uni.navigateTo({
+ url: "/pages/cooperativeOffice/collaborativeApproval/detail",
+ });
+ };
+
// 娣诲姞鏂拌褰�
const handleAdd = () => {
uni.setStorageSync("operationType", "add");
diff --git a/src/pages/equipmentManagement/repair/add.vue b/src/pages/equipmentManagement/repair/add.vue
index e931494..73c4eba 100644
--- a/src/pages/equipmentManagement/repair/add.vue
+++ b/src/pages/equipmentManagement/repair/add.vue
@@ -77,9 +77,9 @@
clearable />
</u-form-item>
<u-form-item label="缁翠慨椤圭洰"
- prop="maintenanceProject"
+ prop="machineryCategory"
border-bottom>
- <u-input v-model="form.maintenanceProject"
+ <u-input v-model="form.machineryCategory"
placeholder="璇疯緭鍏ョ淮淇」鐩�"
clearable />
</u-form-item>
@@ -93,6 +93,11 @@
clearable
count
maxlength="200" />
+ </u-form-item>
+ <u-form-item label="鍥剧墖闄勪欢"
+ prop="storageBlobDTOs"
+ border-bottom>
+ <CommonUpload v-model="form.storageBlobDTOs" />
</u-form-item>
</u-cell-group>
<!-- 鎻愪氦鎸夐挳 -->
@@ -122,8 +127,9 @@
<script setup>
import { ref, computed, onMounted, onUnmounted } from "vue";
- import { onShow } from "@dcloudio/uni-app";
+ import { onShow, onLoad } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
+ import CommonUpload from "@/components/CommonUpload.vue";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import {
addRepair,
@@ -146,10 +152,18 @@
// 琛ㄥ崟寮曠敤
const formRef = ref(null);
const operationType = ref("add");
+ const repairId = ref("");
const loading = ref(false);
const showDevice = ref(false);
const showDate = ref(false);
const pickerDateValue = ref(Date.now());
+
+ onLoad(options => {
+ if (options.id) {
+ repairId.value = options.id;
+ }
+ getPageParams();
+ });
// 璁惧閫夐」
const deviceOptions = ref([]);
@@ -184,8 +198,9 @@
repairTime: dayjs().format("YYYY-MM-DD"), // 鎶ヤ慨鏃ユ湡
repairName: undefined, // 鎶ヤ慨浜�
maintenanceName: undefined, // 缁翠慨浜�
- maintenanceProject: undefined, // 缁翠慨椤圭洰
+ machineryCategory: undefined, // 缁翠慨椤圭洰
remark: undefined, // 鏁呴殰鐜拌薄
+ storageBlobDTOs: [], // 鍥剧墖闄勪欢
});
// 鎶ヤ慨鐘舵�侀�夐」
@@ -238,8 +253,9 @@
form.value.repairTime = dayjs(data.repairTime).format("YYYY-MM-DD");
form.value.repairName = data.repairName;
form.value.maintenanceName = data.maintenanceName;
- form.value.maintenanceProject = data.maintenanceProject;
+ form.value.machineryCategory = data.machineryCategory;
form.value.remark = data.remark;
+ form.value.storageBlobDTOs = data.storageBlobVOs || [];
repairStatusText.value =
repairStatusOptions.value.find(item => item.value == data.status)
?.name || "";
@@ -346,14 +362,12 @@
};
onShow(() => {
- // 椤甸潰鏄剧ず鏃惰幏鍙栧弬鏁�
- getPageParams();
+ // 椤甸潰鏄剧ず鏃堕�昏緫
});
onMounted(() => {
- // 椤甸潰鍔犺浇鏃惰幏鍙栬澶囧垪琛ㄥ拰鍙傛暟
+ // 椤甸潰鍔犺浇鏃惰幏鍙栬澶囧垪琛�
loadDeviceName();
- getPageParams();
});
// 缁勪欢鍗歌浇鏃舵竻鐞嗗畾鏃跺櫒
@@ -393,7 +407,6 @@
// 鍑嗗鎻愪氦鏁版嵁
const submitData = { ...form.value };
-
const { code } = id
? await editRepair({ id: id, ...submitData })
: await addRepair(submitData);
@@ -414,21 +427,15 @@
// 杩斿洖涓婁竴椤�
const goBack = () => {
- uni.removeStorageSync("repairId");
uni.navigateBack();
};
// 鑾峰彇椤甸潰鍙傛暟
const getPageParams = () => {
- // 浣跨敤uni.getStorageSync鑾峰彇id
- const id = uni.getStorageSync("repairId");
-
// 鏍规嵁鏄惁鏈塱d鍙傛暟鏉ュ垽鏂槸鏂板杩樻槸缂栬緫
- if (id) {
+ if (repairId.value) {
// 缂栬緫妯″紡锛岃幏鍙栬鎯�
- loadForm(id);
- // 鍙�夛細鑾峰彇鍚庢竻闄ゅ瓨鍌ㄧ殑id锛岄伩鍏嶅奖鍝嶅悗缁搷浣�
- uni.removeStorageSync("repairId");
+ loadForm(repairId.value);
} else {
// 鏂板妯″紡
loadForm();
@@ -437,9 +444,7 @@
// 鑾峰彇椤甸潰ID
const getPageId = () => {
- // 浣跨敤uni.getStorageSync鑾峰彇id
- const id = uni.getStorageSync("repairId");
- return id;
+ return repairId.value;
};
</script>
diff --git a/src/pages/equipmentManagement/repair/index.vue b/src/pages/equipmentManagement/repair/index.vue
index 5543e43..8c41ab1 100644
--- a/src/pages/equipmentManagement/repair/index.vue
+++ b/src/pages/equipmentManagement/repair/index.vue
@@ -63,7 +63,7 @@
</view>
<view class="detail-row">
<text class="detail-label">缁翠慨椤圭洰</text>
- <text class="detail-value">{{ item.maintenanceProject || '-' }}</text>
+ <text class="detail-value">{{ item.machineryCategory || '-' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">鏁呴殰鐜拌薄</text>
@@ -212,9 +212,9 @@
const edit = id => {
if (!id) return;
// 浣跨敤uni.setStorageSync瀛樺偍id
- uni.setStorageSync("repairId", id);
+ // uni.setStorageSync("repairId", id);
uni.navigateTo({
- url: "/pages/equipmentManagement/repair/add",
+ url: "/pages/equipmentManagement/repair/add?id=" + id,
});
};
diff --git a/src/pages/equipmentManagement/upkeep/add.vue b/src/pages/equipmentManagement/upkeep/add.vue
index 2b5d3da..5173510 100644
--- a/src/pages/equipmentManagement/upkeep/add.vue
+++ b/src/pages/equipmentManagement/upkeep/add.vue
@@ -1,413 +1,435 @@
<template>
- <view class="upkeep-add">
- <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
- <PageHeader :title="operationType === 'edit' ? '缂栬緫淇濆吇璁″垝' : '鏂板淇濆吇璁″垝'" @back="goBack" />
-
- <!-- 琛ㄥ崟鍐呭 -->
- <u-form ref="formRef" :model="form" :rules="formRules" label-width="110px">
- <!-- 鍩烘湰淇℃伅 -->
- <u-form-item label="璁惧鍚嶇О" prop="deviceNameText" required border-bottom>
- <u-input
- v-model="form.deviceNameText"
- placeholder="璇烽�夋嫨璁惧鍚嶇О"
- readonly
- @click="showDevicePicker"
- clearable
- />
- <template #right>
- <u-icon name="scan" @click="startScan" class="scan-icon" />
- </template>
- </u-form-item>
-
- <u-form-item label="瑙勬牸鍨嬪彿" prop="deviceModel" border-bottom>
- <u-input
- v-model="form.deviceModel"
- placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
- readonly
- clearable
- />
- </u-form-item>
-
- <u-form-item label="璁″垝淇濆吇鏃ユ湡" prop="maintenancePlanTime" required border-bottom>
- <u-input
- v-model="form.maintenancePlanTime"
- placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡"
- readonly
- @click="showDatePicker"
- clearable
- />
- <template #right>
- <u-icon name="arrow-right" @click="showDatePicker" />
- </template>
- </u-form-item>
-
- <u-form-item label="淇濆吇浜�" prop="maintenancePerson" border-bottom>
- <u-input
- v-model="form.maintenancePerson"
- placeholder="璇疯緭鍏ヤ繚鍏讳汉"
- clearable
- />
- </u-form-item>
-
- <u-form-item label="淇濆吇椤圭洰" prop="maintenanceProject" border-bottom>
- <u-input
- v-model="form.maintenanceProject"
- placeholder="璇疯緭鍏ヤ繚鍏婚」鐩�"
- clearable
- />
- </u-form-item>
-
- <!-- 鎻愪氦鎸夐挳 -->
- <view class="footer-btns">
- <u-button class="cancel-btn" @click="goBack">鍙栨秷</u-button>
- <u-button class="save-btn" @click="sendForm" :loading="loading">淇濆瓨</u-button>
- </view>
- </u-form>
-
- <!-- 璁惧閫夋嫨鍣� -->
- <up-action-sheet
- :show="showDevice"
- :actions="deviceActions"
- title="閫夋嫨璁惧"
- @select="onDeviceConfirm"
- @close="showDevice = false"
- />
-<up-datetime-picker
- :show="showDate"
- v-model="pickerDateValue"
- @confirm="onDateConfirm"
- @cancel="showDate = false"
- mode="date"
- />
-
- </view>
+ <view class="upkeep-add">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader :title="operationType === 'edit' ? '缂栬緫淇濆吇璁″垝' : '鏂板淇濆吇璁″垝'"
+ @back="goBack" />
+ <!-- 琛ㄥ崟鍐呭 -->
+ <u-form ref="formRef"
+ :model="form"
+ :rules="formRules"
+ label-width="110px">
+ <!-- 鍩烘湰淇℃伅 -->
+ <u-form-item label="璁惧鍚嶇О"
+ prop="deviceNameText"
+ required
+ border-bottom>
+ <u-input v-model="form.deviceNameText"
+ placeholder="璇烽�夋嫨璁惧鍚嶇О"
+ readonly
+ @click="showDevicePicker"
+ clearable />
+ <template #right>
+ <u-icon name="scan"
+ @click="startScan"
+ class="scan-icon" />
+ </template>
+ </u-form-item>
+ <u-form-item label="瑙勬牸鍨嬪彿"
+ prop="deviceModel"
+ border-bottom>
+ <u-input v-model="form.deviceModel"
+ placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+ readonly
+ clearable />
+ </u-form-item>
+ <u-form-item label="璁″垝淇濆吇鏃ユ湡"
+ prop="maintenancePlanTime"
+ required
+ border-bottom>
+ <u-input v-model="form.maintenancePlanTime"
+ placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡"
+ readonly
+ @click="showDatePicker"
+ clearable />
+ <template #right>
+ <u-icon name="arrow-right"
+ @click="showDatePicker" />
+ </template>
+ </u-form-item>
+ <u-form-item label="淇濆吇浜�"
+ prop="maintenancePerson"
+ border-bottom>
+ <u-input v-model="form.maintenancePerson"
+ placeholder="璇疯緭鍏ヤ繚鍏讳汉"
+ clearable />
+ </u-form-item>
+ <u-form-item label="淇濆吇椤圭洰"
+ prop="machineryCategory"
+ border-bottom>
+ <u-input v-model="form.machineryCategory"
+ placeholder="璇疯緭鍏ヤ繚鍏婚」鐩�"
+ clearable />
+ </u-form-item>
+ <!-- 鎻愪氦鎸夐挳 -->
+ <view class="footer-btns">
+ <u-button class="cancel-btn"
+ @click="goBack">鍙栨秷</u-button>
+ <u-button class="save-btn"
+ @click="sendForm"
+ :loading="loading">淇濆瓨</u-button>
+ </view>
+ </u-form>
+ <!-- 璁惧閫夋嫨鍣� -->
+ <up-action-sheet :show="showDevice"
+ :actions="deviceActions"
+ title="閫夋嫨璁惧"
+ @select="onDeviceConfirm"
+ @close="showDevice = false" />
+ <up-datetime-picker :show="showDate"
+ v-model="pickerDateValue"
+ @confirm="onDateConfirm"
+ @cancel="showDate = false"
+ mode="date" />
+ </view>
</template>
<script setup>
-import { ref, computed, onMounted, onUnmounted } from 'vue';
-import { onShow } from '@dcloudio/uni-app';
-import PageHeader from '@/components/PageHeader.vue';
-import { getDeviceLedger } from '@/api/equipmentManagement/ledger';
-import { addUpkeep, editUpkeep, getUpkeepById } from '@/api/equipmentManagement/upkeep';
-import dayjs from "dayjs";
-import { formatDateToYMD } from '@/utils/ruoyi';
+ import { ref, computed, onMounted, onUnmounted } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+ import {
+ addUpkeep,
+ editUpkeep,
+ getUpkeepById,
+ } from "@/api/equipmentManagement/upkeep";
+ import dayjs from "dayjs";
+ import { formatDateToYMD } from "@/utils/ruoyi";
-defineOptions({
- name: "璁惧淇濆吇璁″垝琛ㄥ崟",
-});
-const showToast = (message) => {
- uni.showToast({
- title: message,
- icon: 'none'
- })
-}
+ defineOptions({
+ name: "璁惧淇濆吇璁″垝琛ㄥ崟",
+ });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
-// 琛ㄥ崟寮曠敤
-const formRef = ref(null);
-const operationType = ref('add');
-const loading = ref(false);
-const showDevice = ref(false);
-const showDate = ref(false);
-const pickerDateValue = ref(Date.now());
-const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]);
+ // 琛ㄥ崟寮曠敤
+ const formRef = ref(null);
+ const operationType = ref("add");
+ const loading = ref(false);
+ const showDevice = ref(false);
+ const showDate = ref(false);
+ const pickerDateValue = ref(Date.now());
+ const currentDate = ref([
+ new Date().getFullYear(),
+ new Date().getMonth() + 1,
+ new Date().getDate(),
+ ]);
-// 璁惧閫夐」
-const deviceOptions = ref([]);
-const deviceNameText = ref('');
-// 杞崲涓� action-sheet 闇�瑕佺殑鏍煎紡
-const deviceActions = computed(() => {
- return deviceOptions.value.map(item => ({
- text: item.deviceName,
- value: item.id,
- data: item
- }));
-});
+ // 璁惧閫夐」
+ const deviceOptions = ref([]);
+ const deviceNameText = ref("");
+ // 杞崲涓� action-sheet 闇�瑕佺殑鏍煎紡
+ const deviceActions = computed(() => {
+ return deviceOptions.value.map(item => ({
+ text: item.deviceName,
+ value: item.id,
+ data: item,
+ }));
+ });
-// 鎵爜鐩稿叧鐘舵��
-const isScanning = ref(false);
-const scanTimer = ref(null);
+ // 鎵爜鐩稿叧鐘舵��
+ const isScanning = ref(false);
+ const scanTimer = ref(null);
-// 琛ㄥ崟楠岃瘉瑙勫垯
-const formRules = {
- deviceLedgerId: [{ required: true, trigger: "change", message: "璇烽�夋嫨璁惧鍚嶇О" }],
- maintenancePlanTime: [{ required: true, trigger: "change", message: "璇烽�夋嫨璁″垝淇濆吇鏃ユ湡" }],
-};
+ // 琛ㄥ崟楠岃瘉瑙勫垯
+ const formRules = {
+ deviceLedgerId: [
+ { required: true, trigger: "change", message: "璇烽�夋嫨璁惧鍚嶇О" },
+ ],
+ maintenancePlanTime: [
+ { required: true, trigger: "change", message: "璇烽�夋嫨璁″垝淇濆吇鏃ユ湡" },
+ ],
+ };
-// 浣跨敤 ref 澹版槑琛ㄥ崟鏁版嵁
-const form = ref({
- deviceLedgerId: undefined, // 璁惧ID
- deviceModel: undefined, // 瑙勬牸鍨嬪彿
- maintenancePlanTime: dayjs().format("YYYY-MM-DD"), // 璁″垝淇濆吇鏃ユ湡
- maintenancePerson: undefined, // 淇濆吇浜�
- maintenanceProject: undefined, // 淇濆吇椤圭洰
-});
+ // 浣跨敤 ref 澹版槑琛ㄥ崟鏁版嵁
+ const form = ref({
+ deviceLedgerId: undefined, // 璁惧ID
+ deviceModel: undefined, // 瑙勬牸鍨嬪彿
+ maintenancePlanTime: dayjs().format("YYYY-MM-DD"), // 璁″垝淇濆吇鏃ユ湡
+ maintenancePerson: undefined, // 淇濆吇浜�
+ machineryCategory: undefined, // 淇濆吇椤圭洰
+ });
-// 鍔犺浇璁惧鍒楄〃
-const loadDeviceName = async () => {
- try {
- const { data } = await getDeviceLedger();
- deviceOptions.value = data || [];
- } catch (e) {
- showToast('鑾峰彇璁惧鍒楄〃澶辫触');
- }
-};
+ // 鍔犺浇璁惧鍒楄〃
+ const loadDeviceName = async () => {
+ try {
+ const { data } = await getDeviceLedger();
+ deviceOptions.value = data || [];
+ } catch (e) {
+ showToast("鑾峰彇璁惧鍒楄〃澶辫触");
+ }
+ };
-// 鍔犺浇琛ㄥ崟鏁版嵁锛堢紪杈戞ā寮忥級
-const loadForm = async (id) => {
- if (id) {
- operationType.value = 'edit';
- try {
- const { code, data } = await getUpkeepById(id);
- if (code == 200) {
- form.value.deviceLedgerId = data.deviceLedgerId;
- form.value.deviceModel = data.deviceModel;
- form.value.maintenancePlanTime = dayjs(data.maintenancePlanTime).format("YYYY-MM-DD");
- form.value.maintenancePerson = data.maintenancePerson;
- form.value.maintenanceProject = data.maintenanceProject;
- // 璁剧疆璁惧鍚嶇О鏄剧ず
- const device = deviceOptions.value.find(item => item.id === data.deviceLedgerId);
- if (device) {
- form.value.deviceNameText = device.deviceName;
- }
- }
- } catch (e) {
- showToast('鑾峰彇璇︽儏澶辫触');
- }
- } else {
- // 鏂板妯″紡
- operationType.value = 'add';
- }
-};
+ // 鍔犺浇琛ㄥ崟鏁版嵁锛堢紪杈戞ā寮忥級
+ const loadForm = async id => {
+ if (id) {
+ operationType.value = "edit";
+ try {
+ const { code, data } = await getUpkeepById(id);
+ if (code == 200) {
+ form.value.deviceLedgerId = data.deviceLedgerId;
+ form.value.deviceModel = data.deviceModel;
+ form.value.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
+ "YYYY-MM-DD"
+ );
+ form.value.maintenancePerson = data.maintenancePerson;
+ form.value.machineryCategory = data.machineryCategory;
+ // 璁剧疆璁惧鍚嶇О鏄剧ず
+ const device = deviceOptions.value.find(
+ item => item.id === data.deviceLedgerId
+ );
+ if (device) {
+ form.value.deviceNameText = device.deviceName;
+ }
+ }
+ } catch (e) {
+ showToast("鑾峰彇璇︽儏澶辫触");
+ }
+ } else {
+ // 鏂板妯″紡
+ operationType.value = "add";
+ }
+ };
-// 鎵弿浜岀淮鐮佸姛鑳�
-const startScan = () => {
- if (isScanning.value) {
- showToast('姝e湪鎵弿涓紝璇风◢鍊�...');
- return;
- }
-
- // 璋冪敤uni-app鐨勬壂鐮丄PI
- uni.scanCode({
- scanType: ['qrCode', 'barCode'],
- success: (res) => {
- handleScanResult(res.result);
- },
- fail: (err) => {
- console.error('鎵爜澶辫触:', err);
- showToast('鎵爜澶辫触锛岃閲嶈瘯');
- }
- });
-};
+ // 鎵弿浜岀淮鐮佸姛鑳�
+ const startScan = () => {
+ if (isScanning.value) {
+ showToast("姝e湪鎵弿涓紝璇风◢鍊�...");
+ return;
+ }
-// 澶勭悊鎵爜缁撴灉
-const handleScanResult = (scanResult) => {
- if (!scanResult) {
- showToast('鎵爜缁撴灉涓虹┖');
- return;
- }
-
- isScanning.value = true;
- showToast('鎵爜鎴愬姛');
-
- // 3绉掑悗澶勭悊鎵爜缁撴灉
- scanTimer.value = setTimeout(() => {
- processScanResult(scanResult);
- isScanning.value = false;
- }, 1000);
-};
-function getDeviceIdByRegExp(url) {
- // 鍖归厤deviceId=鍚庨潰鐨勬暟瀛�
- const reg = /deviceId=(\d+)/;
- const match = url.match(reg);
- // 濡傛灉鍖归厤鍒扮粨鏋滐紝杩斿洖鏁板瓧绫诲瀷锛屽惁鍒欒繑鍥瀗ull
- return match ? Number(match[1]) : null;
-}
-// 澶勭悊鎵爜缁撴灉骞跺尮閰嶈澶�
-const processScanResult = (scanResult) => {
- const deviceId = getDeviceIdByRegExp(scanResult);
- const matchedDevice = deviceOptions.value.find(item => item.id == deviceId);
-
- if (matchedDevice) {
- // 鎵惧埌鍖归厤鐨勮澶囷紝鑷姩濉厖
- form.value.deviceLedgerId = matchedDevice.id;
- form.value.deviceNameText = matchedDevice.deviceName;
- form.value.deviceModel = matchedDevice.deviceModel;
- showToast('璁惧淇℃伅宸茶嚜鍔ㄥ~鍏�');
- } else {
- // 鏈壘鍒板尮閰嶇殑璁惧
- showToast('鏈壘鍒板尮閰嶇殑璁惧锛岃鎵嬪姩閫夋嫨');
- }
-};
+ // 璋冪敤uni-app鐨勬壂鐮丄PI
+ uni.scanCode({
+ scanType: ["qrCode", "barCode"],
+ success: res => {
+ handleScanResult(res.result);
+ },
+ fail: err => {
+ console.error("鎵爜澶辫触:", err);
+ showToast("鎵爜澶辫触锛岃閲嶈瘯");
+ },
+ });
+ };
-// 鏄剧ず璁惧閫夋嫨鍣�
-const showDevicePicker = () => {
- showDevice.value = true;
-};
+ // 澶勭悊鎵爜缁撴灉
+ const handleScanResult = scanResult => {
+ if (!scanResult) {
+ showToast("鎵爜缁撴灉涓虹┖");
+ return;
+ }
-// 纭璁惧閫夋嫨
-const onDeviceConfirm = (selected) => {
- // selected 杩斿洖鐨勬槸閫変腑椤�
- form.value.deviceLedgerId = selected.value;
- form.value.deviceNameText = selected.name;
- const selectedDevice = deviceOptions.value.find(item => item.id === selected.value);
- if (selectedDevice) {
- form.value.deviceModel = selectedDevice.deviceModel;
- }
- showDevice.value = false;
-};
+ isScanning.value = true;
+ showToast("鎵爜鎴愬姛");
-// 鏄剧ず鏃ユ湡閫夋嫨鍣�
-const showDatePicker = () => {
- showDate.value = true;
-};
+ // 3绉掑悗澶勭悊鎵爜缁撴灉
+ scanTimer.value = setTimeout(() => {
+ processScanResult(scanResult);
+ isScanning.value = false;
+ }, 1000);
+ };
+ function getDeviceIdByRegExp(url) {
+ // 鍖归厤deviceId=鍚庨潰鐨勬暟瀛�
+ const reg = /deviceId=(\d+)/;
+ const match = url.match(reg);
+ // 濡傛灉鍖归厤鍒扮粨鏋滐紝杩斿洖鏁板瓧绫诲瀷锛屽惁鍒欒繑鍥瀗ull
+ return match ? Number(match[1]) : null;
+ }
+ // 澶勭悊鎵爜缁撴灉骞跺尮閰嶈澶�
+ const processScanResult = scanResult => {
+ const deviceId = getDeviceIdByRegExp(scanResult);
+ const matchedDevice = deviceOptions.value.find(item => item.id == deviceId);
-// 纭鏃ユ湡閫夋嫨
-const onDateConfirm = (e) => {
- form.value.maintenancePlanTime = formatDateToYMD(e.value);
- showDate.value = false;
-};
+ if (matchedDevice) {
+ // 鎵惧埌鍖归厤鐨勮澶囷紝鑷姩濉厖
+ form.value.deviceLedgerId = matchedDevice.id;
+ form.value.deviceNameText = matchedDevice.deviceName;
+ form.value.deviceModel = matchedDevice.deviceModel;
+ showToast("璁惧淇℃伅宸茶嚜鍔ㄥ~鍏�");
+ } else {
+ // 鏈壘鍒板尮閰嶇殑璁惧
+ showToast("鏈壘鍒板尮閰嶇殑璁惧锛岃鎵嬪姩閫夋嫨");
+ }
+ };
-onShow(() => {
- // 椤甸潰鏄剧ず鏃惰幏鍙栧弬鏁�
- getPageParams();
-});
+ // 鏄剧ず璁惧閫夋嫨鍣�
+ const showDevicePicker = () => {
+ showDevice.value = true;
+ };
-onMounted(() => {
- // 椤甸潰鍔犺浇鏃惰幏鍙栬澶囧垪琛ㄥ拰鍙傛暟
- loadDeviceName();
- getPageParams();
-});
+ // 纭璁惧閫夋嫨
+ const onDeviceConfirm = selected => {
+ // selected 杩斿洖鐨勬槸閫変腑椤�
+ form.value.deviceLedgerId = selected.value;
+ form.value.deviceNameText = selected.name;
+ const selectedDevice = deviceOptions.value.find(
+ item => item.id === selected.value
+ );
+ if (selectedDevice) {
+ form.value.deviceModel = selectedDevice.deviceModel;
+ }
+ showDevice.value = false;
+ };
-// 缁勪欢鍗歌浇鏃舵竻鐞嗗畾鏃跺櫒
-onUnmounted(() => {
- if (scanTimer.value) {
- clearTimeout(scanTimer.value);
- }
-});
+ // 鏄剧ず鏃ユ湡閫夋嫨鍣�
+ const showDatePicker = () => {
+ showDate.value = true;
+ };
-// 鎻愪氦琛ㄥ崟
-const sendForm = async () => {
- try {
- // 鎵嬪姩楠岃瘉琛ㄥ崟
- const valid = await formRef.value.validate();
- if (!valid) return;
-
- loading.value = true;
- const id = getPageId();
-
- // 鍑嗗鎻愪氦鏁版嵁
- const submitData = { ...form.value };
- // 纭繚鏃ユ湡鏍煎紡姝g‘
- if (submitData.maintenancePlanTime && !submitData.maintenancePlanTime.includes(':')) {
- submitData.maintenancePlanTime = submitData.maintenancePlanTime + ' 00:00:00';
- }
-
- const { code } = id
- ? await editUpkeep({ id: id, ...submitData })
- : await addUpkeep(submitData);
-
- if (code == 200) {
- showToast(`${id ? "缂栬緫" : "鏂板"}璁″垝鎴愬姛`);
- setTimeout(() => {
- uni.navigateBack();
- }, 1500);
- } else {
- loading.value = false;
- }
- } catch (e) {
- loading.value = false;
- showToast('琛ㄥ崟楠岃瘉澶辫触');
- }
-};
+ // 纭鏃ユ湡閫夋嫨
+ const onDateConfirm = e => {
+ form.value.maintenancePlanTime = formatDateToYMD(e.value);
+ showDate.value = false;
+ };
-// 杩斿洖涓婁竴椤�
-const goBack = () => {
- // 娓呴櫎瀛樺偍鐨刬d
- uni.removeStorageSync('repairId');
- uni.navigateBack();
-};
+ onShow(() => {
+ // 椤甸潰鏄剧ず鏃惰幏鍙栧弬鏁�
+ getPageParams();
+ });
-// 鑾峰彇椤甸潰鍙傛暟
-const getPageParams = () => {
- // 浠庢湰鍦板瓨鍌ㄨ幏鍙杋d
- const id = uni.getStorageSync('repairId');
-
- // 鏍规嵁鏄惁鏈塱d鍙傛暟鏉ュ垽鏂槸鏂板杩樻槸缂栬緫
- if (id) {
- // 缂栬緫妯″紡锛岃幏鍙栬鎯�
- loadForm(id);
- } else {
- // 鏂板妯″紡
- loadForm();
- }
-};
+ onMounted(() => {
+ // 椤甸潰鍔犺浇鏃惰幏鍙栬澶囧垪琛ㄥ拰鍙傛暟
+ loadDeviceName();
+ getPageParams();
+ });
-// 鑾峰彇椤甸潰ID
-const getPageId = () => {
- // 浠庢湰鍦板瓨鍌ㄨ幏鍙杋d
- return uni.getStorageSync('repairId');
-};
+ // 缁勪欢鍗歌浇鏃舵竻鐞嗗畾鏃跺櫒
+ onUnmounted(() => {
+ if (scanTimer.value) {
+ clearTimeout(scanTimer.value);
+ }
+ });
+
+ // 鎻愪氦琛ㄥ崟
+ const sendForm = async () => {
+ try {
+ // 鎵嬪姩楠岃瘉琛ㄥ崟
+ const valid = await formRef.value.validate();
+ if (!valid) return;
+
+ loading.value = true;
+ const id = getPageId();
+
+ // 鍑嗗鎻愪氦鏁版嵁
+ const submitData = { ...form.value };
+ // 纭繚鏃ユ湡鏍煎紡姝g‘
+ if (
+ submitData.maintenancePlanTime &&
+ !submitData.maintenancePlanTime.includes(":")
+ ) {
+ submitData.maintenancePlanTime =
+ submitData.maintenancePlanTime + " 00:00:00";
+ }
+
+ const { code } = id
+ ? await editUpkeep({ id: id, ...submitData })
+ : await addUpkeep(submitData);
+
+ if (code == 200) {
+ showToast(`${id ? "缂栬緫" : "鏂板"}璁″垝鎴愬姛`);
+ setTimeout(() => {
+ uni.navigateBack();
+ }, 1500);
+ } else {
+ loading.value = false;
+ }
+ } catch (e) {
+ loading.value = false;
+ showToast("琛ㄥ崟楠岃瘉澶辫触");
+ }
+ };
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ // 娓呴櫎瀛樺偍鐨刬d
+ uni.removeStorageSync("repairId");
+ uni.navigateBack();
+ };
+
+ // 鑾峰彇椤甸潰鍙傛暟
+ const getPageParams = () => {
+ // 浠庢湰鍦板瓨鍌ㄨ幏鍙杋d
+ const id = uni.getStorageSync("repairId");
+
+ // 鏍规嵁鏄惁鏈塱d鍙傛暟鏉ュ垽鏂槸鏂板杩樻槸缂栬緫
+ if (id) {
+ // 缂栬緫妯″紡锛岃幏鍙栬鎯�
+ loadForm(id);
+ } else {
+ // 鏂板妯″紡
+ loadForm();
+ }
+ };
+
+ // 鑾峰彇椤甸潰ID
+ const getPageId = () => {
+ // 浠庢湰鍦板瓨鍌ㄨ幏鍙杋d
+ return uni.getStorageSync("repairId");
+ };
</script>
<style scoped lang="scss">
-@import '@/static/scss/form-common.scss';
-.upkeep-add {
- min-height: 100vh;
- background: #f8f9fa;
- padding-bottom: 5rem;
-}
+ @import "@/static/scss/form-common.scss";
+ .upkeep-add {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 5rem;
+ }
-.footer-btns {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- display: flex;
- justify-content: space-around;
- align-items: center;
- padding: 0.75rem 0;
- box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05);
- z-index: 1000;
-}
+ .footer-btns {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: #fff;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ padding: 0.75rem 0;
+ box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05);
+ z-index: 1000;
+ }
-.cancel-btn {
- font-weight: 400;
- font-size: 1rem;
- color: #FFFFFF;
- width: 6.375rem;
- background: #C7C9CC;
- box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
- border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
-}
+ .cancel-btn {
+ font-weight: 400;
+ font-size: 1rem;
+ color: #ffffff;
+ width: 6.375rem;
+ background: #c7c9cc;
+ box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
+ border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
+ }
-.save-btn {
- font-weight: 400;
- font-size: 1rem;
- color: #FFFFFF;
- width: 14rem;
- background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%);
- box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
- border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
-}
+ .save-btn {
+ font-weight: 400;
+ font-size: 1rem;
+ color: #ffffff;
+ width: 14rem;
+ background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
+ box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
+ border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
+ }
-// 鍝嶅簲寮忚皟鏁�
-@media (max-width: 768px) {
- .submit-section {
- padding: 12px;
- }
-}
+ // 鍝嶅簲寮忚皟鏁�
+ @media (max-width: 768px) {
+ .submit-section {
+ padding: 12px;
+ }
+ }
-.tip-text {
- padding: 4px 16px 0 16px;
- font-size: 12px;
- color: #888;
-}
+ .tip-text {
+ padding: 4px 16px 0 16px;
+ font-size: 12px;
+ color: #888;
+ }
-.scan-icon {
- color: #1989fa;
- font-size: 18px;
- margin-left: 8px;
- cursor: pointer;
-}
+ .scan-icon {
+ color: #1989fa;
+ font-size: 18px;
+ margin-left: 8px;
+ cursor: pointer;
+ }
</style>
\ No newline at end of file
diff --git a/src/pages/equipmentManagement/upkeep/index.vue b/src/pages/equipmentManagement/upkeep/index.vue
index 8646554..a9ecd0c 100644
--- a/src/pages/equipmentManagement/upkeep/index.vue
+++ b/src/pages/equipmentManagement/upkeep/index.vue
@@ -68,7 +68,7 @@
</view>
<view class="detail-row">
<text class="detail-label">淇濆吇椤圭洰</text>
- <text class="detail-value">{{ item.maintenanceProject || '-' }}</text>
+ <text class="detail-value">{{ item.machineryCategory || '-' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">瀹為檯淇濆吇浜�</text>
diff --git a/src/pages/inventoryManagement/stockManagement/Qualified.vue b/src/pages/inventoryManagement/stockManagement/Qualified.vue
deleted file mode 100644
index 12b9060..0000000
--- a/src/pages/inventoryManagement/stockManagement/Qualified.vue
+++ /dev/null
@@ -1,151 +0,0 @@
-<template>
- <view class="qualified-record-container">
- <view class="search-section">
- <view class="search-bar">
- <view class="search-input">
- <up-input
- class="search-text"
- placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
- v-model="searchForm.productName"
- @confirm="handleQuery"
- clearable
- />
- </view>
- <view class="filter-button" @click="handleQuery">
- <up-icon name="search" size="24" color="#999"></up-icon>
- </view>
- </view>
- </view>
-
- <scroll-view scroll-y class="ledger-list" v-if="tableData.length > 0" @scrolltolower="loadMore">
- <view v-for="item in tableData" :key="item.id" class="ledger-item" :class="{ 'low-stock': isLowStock(item) }">
- <view class="item-header">
- <view class="item-left">
- <view class="document-icon">
- <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
- </view>
- <text class="item-id">{{ item.productName }}</text>
- </view>
- <view class="item-right">
- <text class="item-tag tag-type">鍚堟牸搴撳瓨</text>
- </view>
- </view>
-
- <up-divider></up-divider>
-
- <view class="item-details">
- <view class="detail-row">
- <text class="detail-label">瑙勬牸鍨嬪彿</text>
- <text class="detail-value">{{ item.model }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">搴撳瓨鏁伴噺</text>
- <text class="detail-value">{{ item.qualitity }} {{ item.unit }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">閿佸畾/鍐荤粨</text>
- <text class="detail-value">{{ item.lockedQuantity }} {{ item.unit }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">鍙敤搴撳瓨</text>
- <text class="detail-value" style="color: #2979ff; font-weight: bold;">{{ item.unLockedQuantity }} {{ item.unit }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">搴撳瓨棰勮</text>
- <text class="detail-value">{{ item.warnNum }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">鏇存柊鏃堕棿</text>
- <text class="detail-value">{{ item.updateTime }}</text>
- </view>
- </view>
- </view>
- <up-loadmore :status="loadStatus" />
- </scroll-view>
- <view v-else-if="!loading" class="no-data">
- <up-empty mode="data" text="鏆傛棤搴撳瓨鏁版嵁"></up-empty>
- </view>
- </view>
-</template>
-
-<script setup>
-import { ref, reactive, onMounted } from 'vue';
-import { getStockInventoryListPage } from "@/api/inventoryManagement/stockInventory.js";
-
-const tableData = ref([]);
-const loading = ref(false);
-const loadStatus = ref('loadmore');
-const page = reactive({ current: 1, size: 10 });
-const total = ref(0);
-const searchForm = reactive({ productName: '' });
-
-const handleQuery = () => {
- page.current = 1;
- tableData.value = [];
- getList();
-};
-
-const getList = () => {
- if (loading.value) return;
- loading.value = true;
- loadStatus.value = 'loading';
- getStockInventoryListPage({ ...searchForm, ...page, type: 'qualified' }).then(res => {
- loading.value = false;
- const records = res.data.records || [];
- tableData.value = page.current === 1 ? records : [...tableData.value, ...records];
- total.value = res.data.total;
- loadStatus.value = tableData.value.length >= total.value ? 'nomore' : 'loadmore';
- }).catch(() => {
- loading.value = false;
- loadStatus.value = 'loadmore';
- });
-};
-
-const loadMore = () => {
- if (loadStatus.value === 'nomore' || loading.value) return;
- page.current++;
- getList();
-};
-
-const isLowStock = (row) => {
- const stock = Number(row?.unLockedQuantity ?? 0);
- const warn = Number(row?.warnNum ?? 0);
- return Number.isFinite(stock) && Number.isFinite(warn) && stock < warn;
-};
-
-onMounted(() => {
- getList();
-});
-</script>
-
-<style scoped lang="scss">
-@import '@/styles/sales-common.scss';
-
-.qualified-record-container {
- height: 100%;
- display: flex;
- flex-direction: column;
-}
-
-.tag-type {
- background-color: #e3f2fd;
- color: #2196f3;
- padding: 2px 8px;
- border-radius: 4px;
- font-size: 12px;
-}
-
-.ledger-list {
- flex: 1;
- overflow-y: auto;
-}
-
-.low-stock {
- background-color: #fde2e2;
- color: #c45656;
-}
-
-.no-data {
- padding-top: 100px;
-}
-</style>
diff --git a/src/pages/inventoryManagement/stockManagement/Record.vue b/src/pages/inventoryManagement/stockManagement/Record.vue
new file mode 100644
index 0000000..e4542a8
--- /dev/null
+++ b/src/pages/inventoryManagement/stockManagement/Record.vue
@@ -0,0 +1,292 @@
+<template>
+ <view class="record-container">
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input
+ class="search-text"
+ placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
+ v-model="searchForm.productName"
+ @confirm="handleQuery"
+ clearable
+ />
+ </view>
+ <view class="filter-button" @click="handleQuery">
+ <up-icon name="search" size="24" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+
+ <scroll-view scroll-y class="ledger-list" v-if="tableData.length > 0" @scrolltolower="loadMore">
+ <view v-for="item in tableData" :key="item.id" class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.productName }}</text>
+ </view>
+ </view>
+
+ <up-divider></up-divider>
+
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">瑙勬牸鍨嬪彿</text>
+ <text class="detail-value">{{ item.model }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍗曚綅</text>
+ <text class="detail-value">{{ item.unit }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎵瑰彿</text>
+ <text class="detail-value">{{ item.batchNo }}</text>
+ </view>
+
+ <view class="quantity-section">
+ <view class="quantity-box qualified">
+ <text class="q-label">鍚堟牸搴撳瓨</text>
+ <text class="q-value">{{ item.qualifiedQuantity }}</text>
+ </view>
+ <view class="quantity-box unqualified">
+ <text class="q-label">涓嶅悎鏍煎簱瀛�</text>
+ <text class="q-value">{{ item.unQualifiedQuantity }}</text>
+ </view>
+ </view>
+
+ <view class="quantity-section">
+ <view class="quantity-box locked">
+ <text class="q-label">鍚堟牸鍐荤粨</text>
+ <text class="q-value">{{ item.qualifiedLockedQuantity }}</text>
+ </view>
+ <view class="quantity-box locked">
+ <text class="q-label">涓嶅悎鏍煎喕缁�</text>
+ <text class="q-value">{{ item.unQualifiedLockedQuantity }}</text>
+ </view>
+ </view>
+
+ <view class="detail-row">
+ <text class="detail-label">搴撳瓨棰勮</text>
+ <text class="detail-value">{{ item.warnNum }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏇存柊鏃堕棿</text>
+ <text class="detail-value">{{ item.updateTime }}</text>
+ </view>
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus" />
+ </scroll-view>
+ <view v-else-if="!loading" class="no-data">
+ <up-empty mode="data" text="鏆傛棤搴撳瓨鏁版嵁"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue';
+import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js";
+
+const props = defineProps({
+ productId: {
+ type: Number,
+ required: true
+ }
+});
+
+const tableData = ref([]);
+const loading = ref(false);
+const loadStatus = ref('loadmore');
+const page = reactive({ current: 1, size: 10 });
+const total = ref(0);
+const searchForm = reactive({
+ productName: '',
+ topParentProductId: props.productId
+});
+
+const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
+ getList();
+};
+
+const getList = () => {
+ if (loading.value) return;
+ loading.value = true;
+ loadStatus.value = 'loading';
+
+ getStockInventoryListPageCombined({
+ ...searchForm,
+ current: page.current,
+ size: page.size
+ }).then(res => {
+ loading.value = false;
+ const records = res.data.records || [];
+ tableData.value = page.current === 1 ? records : [...tableData.value, ...records];
+ total.value = res.data.total;
+ loadStatus.value = tableData.value.length >= total.value ? 'nomore' : 'loadmore';
+ }).catch(() => {
+ loading.value = false;
+ loadStatus.value = 'loadmore';
+ });
+};
+
+const loadMore = () => {
+ if (loadStatus.value === 'loadmore') {
+ page.current++;
+ getList();
+ }
+};
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+.record-container {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ background-color: #f5f7fa;
+}
+
+.search-section {
+ padding: 20rpx;
+ background-color: #ffffff;
+ position: sticky;
+ top: 0;
+ z-index: 10;
+}
+
+.search-bar {
+ display: flex;
+ align-items: center;
+ background-color: #f2f2f2;
+ border-radius: 40rpx;
+ padding: 0 30rpx;
+ height: 80rpx;
+}
+
+.search-input {
+ flex: 1;
+}
+
+.search-text {
+ font-size: 28rpx;
+}
+
+.filter-button {
+ padding-left: 20rpx;
+}
+
+.ledger-list {
+ flex: 1;
+ padding: 20rpx;
+ box-sizing: border-box;
+}
+
+.ledger-item {
+ background-color: #ffffff;
+ border-radius: 16rpx;
+ padding: 30rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+}
+
+.item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20rpx;
+}
+
+.item-left {
+ display: flex;
+ align-items: center;
+}
+
+.document-icon {
+ width: 40rpx;
+ height: 40rpx;
+ background: linear-gradient(135deg, #2979ff, #1565c0);
+ border-radius: 8rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 16rpx;
+}
+
+.item-id {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #303133;
+}
+
+.item-details {
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 16rpx;
+ font-size: 26rpx;
+
+ .detail-label {
+ color: #909399;
+ }
+
+ .detail-value {
+ color: #303133;
+ font-weight: 500;
+ }
+ }
+}
+
+.quantity-section {
+ display: flex;
+ gap: 20rpx;
+ margin: 20rpx 0;
+
+ .quantity-box {
+ flex: 1;
+ padding: 16rpx;
+ border-radius: 8rpx;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ .q-label {
+ font-size: 22rpx;
+ margin-bottom: 8rpx;
+ }
+
+ .q-value {
+ font-size: 32rpx;
+ font-weight: bold;
+ }
+
+ &.qualified {
+ background-color: #ecf5ff;
+ color: #409eff;
+ }
+
+ &.unqualified {
+ background-color: #fef0f0;
+ color: #f56c6c;
+ }
+
+ &.locked {
+ background-color: #f4f4f5;
+ color: #909399;
+ }
+ }
+}
+
+.no-data {
+ padding-top: 200rpx;
+}
+</style>
diff --git a/src/pages/inventoryManagement/stockManagement/Unqualified.vue b/src/pages/inventoryManagement/stockManagement/Unqualified.vue
deleted file mode 100644
index 48dafc4..0000000
--- a/src/pages/inventoryManagement/stockManagement/Unqualified.vue
+++ /dev/null
@@ -1,134 +0,0 @@
-<template>
- <view class="unqualified-record-container">
- <view class="search-section">
- <view class="search-bar">
- <view class="search-input">
- <up-input
- class="search-text"
- placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
- v-model="searchForm.productName"
- @confirm="handleQuery"
- clearable
- />
- </view>
- <view class="filter-button" @click="handleQuery">
- <up-icon name="search" size="24" color="#999"></up-icon>
- </view>
- </view>
- </view>
-
- <scroll-view scroll-y class="ledger-list" v-if="tableData.length > 0" @scrolltolower="loadMore">
- <view v-for="item in tableData" :key="item.id" class="ledger-item">
- <view class="item-header">
- <view class="item-left">
- <view class="document-icon">
- <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
- </view>
- <text class="item-id">{{ item.productName }}</text>
- </view>
- <view class="item-right">
- <text class="item-tag tag-type" style="background-color: #fde2e2; color: #f56c6c;">涓嶅悎鏍煎簱瀛�</text>
- </view>
- </view>
-
- <up-divider></up-divider>
-
- <view class="item-details">
- <view class="detail-row">
- <text class="detail-label">瑙勬牸鍨嬪彿</text>
- <text class="detail-value">{{ item.model }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">搴撳瓨鏁伴噺</text>
- <text class="detail-value">{{ item.qualitity }} {{ item.unit }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">閿佸畾/鍐荤粨</text>
- <text class="detail-value">{{ item.lockedQuantity }} {{ item.unit }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">鍙敤搴撳瓨</text>
- <text class="detail-value" style="color: #f56c6c; font-weight: bold;">{{ item.unLockedQuantity }} {{ item.unit }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">鏇存柊鏃堕棿</text>
- <text class="detail-value">{{ item.updateTime }}</text>
- </view>
- </view>
- </view>
- <up-loadmore :status="loadStatus" />
- </scroll-view>
- <view v-else-if="!loading" class="no-data">
- <up-empty mode="data" text="鏆傛棤涓嶅悎鏍煎簱瀛樻暟鎹�"></up-empty>
- </view>
- </view>
-</template>
-
-<script setup>
-import { ref, reactive, onMounted } from 'vue';
-import { getStockUninventoryListPage } from "@/api/inventoryManagement/stockUninventory.js";
-
-const tableData = ref([]);
-const loading = ref(false);
-const loadStatus = ref('loadmore');
-const page = reactive({ current: 1, size: 10 });
-const total = ref(0);
-const searchForm = reactive({ productName: '' });
-
-const handleQuery = () => {
- page.current = 1;
- tableData.value = [];
- getList();
-};
-
-const getList = () => {
- if (loading.value) return;
- loading.value = true;
- loadStatus.value = 'loading';
- getStockUninventoryListPage({ ...searchForm, ...page, type: 'unqualified' }).then(res => {
- loading.value = false;
- const records = res.data.records || [];
- tableData.value = page.current === 1 ? records : [...tableData.value, ...records];
- total.value = res.data.total;
- loadStatus.value = tableData.value.length >= total.value ? 'nomore' : 'loadmore';
- }).catch(() => {
- loading.value = false;
- loadStatus.value = 'loadmore';
- });
-};
-
-const loadMore = () => {
- if (loadStatus.value === 'nomore' || loading.value) return;
- page.current++;
- getList();
-};
-
-onMounted(() => {
- getList();
-});
-</script>
-
-<style scoped lang="scss">
-@import '@/styles/sales-common.scss';
-
-.unqualified-record-container {
- height: 100%;
- display: flex;
- flex-direction: column;
-}
-
-.tag-type {
- padding: 2px 8px;
- border-radius: 4px;
- font-size: 12px;
-}
-
-.ledger-list {
- flex: 1;
- overflow-y: auto;
-}
-
-.no-data {
- padding-top: 100px;
-}
-</style>
diff --git a/src/pages/inventoryManagement/stockManagement/index.vue b/src/pages/inventoryManagement/stockManagement/index.vue
index 98ebf44..45d27ad 100644
--- a/src/pages/inventoryManagement/stockManagement/index.vue
+++ b/src/pages/inventoryManagement/stockManagement/index.vue
@@ -1,57 +1,104 @@
<template>
<view class="app-container">
- <PageHeader title="搴撳瓨绠$悊" @back="goBack" />
- <up-tabs :list="tabs" @click="handleTabClick" :current="activeTab"/>
- <swiper class="swiper-box" :current="activeTab" @change="handleSwiperChange">
- <swiper-item class="swiper-item">
- <qualified-record />
- </swiper-item>
- <swiper-item class="swiper-item">
- <unqualified-record />
- </swiper-item>
- </swiper>
+ <PageHeader title="搴撳瓨绠$悊"
+ @back="goBack" />
+ <view v-if="loading"
+ class="loading-state">
+ <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+ </view>
+ <template v-else>
+ <up-tabs :list="tabs"
+ @click="handleTabClick"
+ :current="activeTab" />
+ <swiper class="swiper-box"
+ :current="activeTab"
+ @change="handleSwiperChange">
+ <swiper-item class="swiper-item"
+ v-for="tab in products"
+ :key="tab.id">
+ <record :product-id="tab.id"
+ v-if="activeTab === products.indexOf(tab) || initializedTabs.includes(tab.id)" />
+ </swiper-item>
+ </swiper>
+ </template>
</view>
</template>
<script setup>
-import { ref } from 'vue';
-import PageHeader from "@/components/PageHeader.vue";
-import QualifiedRecord from "./Qualified.vue";
-import UnqualifiedRecord from "./Unqualified.vue";
+ import { ref, onMounted } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import Record from "./Record.vue";
+ import { productTreeList } from "@/api/basicData/product.js";
-const activeTab = ref(0);
-const tabs = ref([
- { name: '鍚堟牸搴撳瓨' },
- { name: '涓嶅悎鏍煎簱瀛�' }
-]);
+ const activeTab = ref(0);
+ const tabs = ref([]);
+ const products = ref([]);
+ const loading = ref(false);
+ const initializedTabs = ref([]);
-const handleTabClick = (item) => {
- activeTab.value = item.index;
-};
+ const handleTabClick = item => {
+ activeTab.value = item.index;
+ if (!initializedTabs.value.includes(products.value[item.index].id)) {
+ initializedTabs.value.push(products.value[item.index].id);
+ }
+ };
-const handleSwiperChange = (e) => {
- activeTab.value = e.detail.current;
-};
+ const handleSwiperChange = e => {
+ const index = e.detail.current;
+ activeTab.value = index;
+ if (!initializedTabs.value.includes(products.value[index].id)) {
+ initializedTabs.value.push(products.value[index].id);
+ }
+ };
-const goBack = () => {
- uni.navigateBack();
-};
+ const fetchProducts = async () => {
+ loading.value = true;
+ try {
+ const res = await productTreeList();
+ // 杩囨护鏍硅妭鐐逛骇鍝�
+ products.value = res
+ .filter(item => item.parentId === null)
+ .map(({ id, productName }) => ({ id, productName }));
+ tabs.value = products.value.map(p => ({ name: p.productName }));
+
+ if (products.value.length > 0) {
+ activeTab.value = 0;
+ initializedTabs.value = [products.value[0].id];
+ }
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ onMounted(() => {
+ fetchProducts();
+ });
</script>
<style scoped lang="scss">
-.app-container {
- display: flex;
- flex-direction: column;
- height: 100vh;
- background-color: #f8f9fa;
-}
-.swiper-box {
- flex: 1;
-}
-.swiper-item {
- height: 100%;
-}
-:deep(.up-tabs) {
- background-color: #fff;
-}
+ .app-container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ background-color: #f8f9fa;
+ }
+ .loading-state {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ .swiper-box {
+ flex: 1;
+ }
+ .swiper-item {
+ height: 100%;
+ }
+ :deep(.up-tabs) {
+ background-color: #fff;
+ }
</style>
diff --git a/src/pages/procurementManagement/procurementLedger/detail.vue b/src/pages/procurementManagement/procurementLedger/detail.vue
index d7693cc..397d863 100644
--- a/src/pages/procurementManagement/procurementLedger/detail.vue
+++ b/src/pages/procurementManagement/procurementLedger/detail.vue
@@ -31,8 +31,7 @@
</up-form-item>
<up-form-item label="渚涘簲鍟嗗悕绉�"
prop="supplierName"
- required
- >
+ required>
<up-input v-model="form.supplierName"
readonly
:disabled="isReadOnly"
@@ -82,55 +81,6 @@
placeholder="璇疯緭鍏�"
disabled />
</up-form-item>
- <view class="approval-process">
- <view class="approval-header">
- <text class="approval-title">瀹℃牳娴佺▼</text>
- <text class="approval-desc">姣忎釜姝ラ鍙兘閫夋嫨涓�涓鎵逛汉</text>
- </view>
- <view class="approval-steps">
- <view v-for="(step, stepIndex) in approverNodes"
- :key="stepIndex"
- class="approval-step">
- <view class="step-dot"></view>
- <view class="step-title">
- <text>瀹℃壒浜�</text>
- </view>
- <view class="approver-container">
- <view v-if="step.nickName"
- class="approver-item">
- <view class="approver-avatar">
- <text class="avatar-text">{{ step.nickName.charAt(0) }}</text>
- <view class="status-dot"></view>
- </view>
- <view class="approver-info">
- <text class="approver-name">{{ step.nickName }}</text>
- </view>
- <view class="delete-approver-btn"
- v-if="!isReadOnly"
- @click="removeApprover(stepIndex)">脳</view>
- </view>
- <view v-else-if="!isReadOnly"
- class="add-approver-btn"
- @click="addApprover(stepIndex)">
- <view class="add-circle">+</view>
- <text class="add-label">閫夋嫨瀹℃壒浜�</text>
- </view>
- </view>
- <view class="step-line"
- v-if="stepIndex < approverNodes.length - 1"></view>
- <view class="delete-step-btn"
- v-if="approverNodes.length > 1 && !isReadOnly"
- @click="removeApprovalStep(stepIndex)">鍒犻櫎鑺傜偣</view>
- </view>
- </view>
- <view class="add-step-btn" v-if="!isReadOnly">
- <u-button icon="plus"
- plain
- type="primary"
- style="width: 100%"
- @click="addApprovalStep">鏂板鑺傜偣</u-button>
- </view>
- </view>
<up-popup :show="showTimePicker"
mode="bottom"
@close="showTimePicker = false">
diff --git a/src/pages/productionManagement/processRoute/items.vue b/src/pages/productionManagement/processRoute/items.vue
index d555526..9c2b266 100644
--- a/src/pages/productionManagement/processRoute/items.vue
+++ b/src/pages/productionManagement/processRoute/items.vue
@@ -63,6 +63,16 @@
type="success"
size="mini"
plain />
+ <up-tag v-if="item.type==0"
+ text="璁℃椂"
+ type="info"
+ size="mini"
+ plain />
+ <up-tag v-else
+ text="璁′欢"
+ type="warning"
+ size="mini"
+ plain />
</view>
</view>
<view class="card-footer"
diff --git a/src/pages/productionManagement/productionAccounting/index.vue b/src/pages/productionManagement/productionAccounting/index.vue
index b20b407..87ec382 100644
--- a/src/pages/productionManagement/productionAccounting/index.vue
+++ b/src/pages/productionManagement/productionAccounting/index.vue
@@ -2,7 +2,6 @@
<view class="production-accounting">
<PageHeader title="鐢熶骇鏍哥畻"
@back="goBack" />
-
<!-- 绛涢�夊尯鍩� -->
<view class="filter-section">
<view class="date-type-selector">
@@ -13,7 +12,6 @@
lineWidth="30"
lineHeight="3" />
</view>
-
<view class="date-picker-bar"
@click="showDatePicker = true">
<view class="date-display">
@@ -27,7 +25,6 @@
color="#999"></up-icon>
</view>
</view>
-
<!-- 姹囨�诲垪琛� -->
<view class="summary-section"
v-if="!showDetail">
@@ -80,7 +77,6 @@
text="鏆傛棤姹囨�绘暟鎹�" />
</view>
</view>
-
<!-- 鏄庣粏鍒楄〃 (鐐瑰嚮姹囨�昏鍚庢樉绀�) -->
<view class="detail-section"
v-else>
@@ -91,7 +87,6 @@
color="#2979ff"></up-icon>
<text class="back-text">杩斿洖姹囨�� ({{ currentUserName }})</text>
</view>
-
<view class="ledger-list"
v-if="detailList.length > 0">
<view v-for="(item, index) in detailList"
@@ -113,6 +108,14 @@
<up-divider></up-divider>
<view class="item-details">
<view class="detail-row">
+ <text class="detail-label">鐢熶骇鏃ユ湡</text>
+ <text class="detail-value">{{ item.schedulingDate || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鐢熶骇浜�</text>
+ <text class="detail-value">{{ item.schedulingUserName || '-' }}</text>
+ </view>
+ <view class="detail-row">
<text class="detail-label">瑙勬牸鍨嬪彿</text>
<text class="detail-value">{{ item.productModelName }}</text>
</view>
@@ -123,6 +126,10 @@
<view class="detail-row">
<text class="detail-label">鐢熶骇鏁伴噺</text>
<text class="detail-value">{{ item.quantity }} {{ item.unit }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">宸ユ椂(h)</text>
+ <text class="detail-value">{{ item.workHour || 0 }}</text>
</view>
<view class="detail-row">
<text class="detail-label">宸ユ椂瀹氶</text>
@@ -143,7 +150,6 @@
text="鏆傛棤鏄庣粏鏁版嵁" />
</view>
</view>
-
<!-- 鏃ユ湡閫夋嫨鍣� -->
<up-datetime-picker :show="showDatePicker"
v-model="pickerValue"
@@ -281,7 +287,9 @@
salesLedgerProductionAccountingListProductionDetails(params)
.then(res => {
const records = res.data.records || [];
- detailList.value = isLoadMore ? [...detailList.value, ...records] : records;
+ detailList.value = isLoadMore
+ ? [...detailList.value, ...records]
+ : records;
page1.total = res.data.total || 0;
if (detailList.value.length >= page1.total) {
diff --git a/src/pages/productionManagement/productionOrder/pickingDetail.vue b/src/pages/productionManagement/productionOrder/pickingDetail.vue
index 9ed5f61..7aad62d 100644
--- a/src/pages/productionManagement/productionOrder/pickingDetail.vue
+++ b/src/pages/productionManagement/productionOrder/pickingDetail.vue
@@ -1,6 +1,6 @@
<template>
<view class="picking-detail">
- <PageHeader :title="棰嗘枡璇︽儏"
+ <PageHeader title="棰嗘枡璇︽儏"
@back="goBack" />
<scroll-view scroll-y
class="detail-list"
diff --git a/src/pages/productionManagement/productionReport/index.vue b/src/pages/productionManagement/productionReport/index.vue
index f5e5b4f..4687c62 100644
--- a/src/pages/productionManagement/productionReport/index.vue
+++ b/src/pages/productionManagement/productionReport/index.vue
@@ -40,6 +40,15 @@
@click="openProducerPicker"
suffix-icon="arrow-down" />
</u-form-item>
+ <!-- 宸ユ椂 -->
+ <u-form-item label="宸ユ椂"
+ v-if="form.type == 0"
+ prop="workHour">
+ <u-input v-model="form.workHour"
+ placeholder="璇疯緭鍏ュ伐鏃�"
+ type="number" />
+ <text class="param-unit">h</text>
+ </u-form-item>
</view>
<!-- 鍔ㄦ�佸弬鏁板尯鍩� -->
<view class="form-section"
@@ -135,7 +144,10 @@
import { addProductMain } from "@/api/productionManagement/productionReporting";
import { getInfo } from "@/api/login";
import { userListNoPageByTenantId } from "@/api/system/user";
- import { findProcessParamListOrder } from "@/api/productionManagement/productProcessRoute.js";
+ import {
+ findProcessParamListOrder,
+ listMaterialPickingDetail,
+ } from "@/api/productionManagement/productionOrder.js";
import { getDicts } from "@/api/system/dict/data";
import { formatDateToYMD, parseTime } from "@/utils/ruoyi";
@@ -147,13 +159,13 @@
scrapQty: "",
userName: "",
workOrderId: "",
- productProcessRouteItemId: "",
userId: "",
schedulingUserId: "",
reportWork: "",
- productMainId: null,
productionOrderRoutingOperationId: "",
productionOrderId: "",
+ workHour: 0,
+ type: null,
paramGroups: {},
});
@@ -344,12 +356,11 @@
userId: form.value.userId,
userName: form.value.userName,
productionOperationTaskId: form.value.workOrderId,
- productProcessRouteItemId: form.value.productProcessRouteItemId,
reportWork: form.value.reportWork,
- productMainId: form.value.productMainId,
productionOrderRoutingOperationId:
form.value.productionOrderRoutingOperationId,
productionOrderId: form.value.productionOrderId,
+ workHour: form.value.workHour,
productionOperationParamList: productionOperationParamList,
};
@@ -374,7 +385,7 @@
});
};
- onLoad(options => {
+ onLoad(async options => {
console.log(options, "options");
if (!options.orderRow) {
console.log("浠庨椤佃烦杞紝鏃犺鍗曟暟鎹�");
@@ -389,16 +400,43 @@
const orderRow = JSON.parse(decodeURIComponent(options.orderRow));
console.log("鏋勯�犵殑orderRow:", orderRow);
+ // 鍙傜収 PC 绔�昏緫锛氭湭棰嗘枡鏃犳硶鎶ュ伐
+ if (orderRow.productionOrderId) {
+ try {
+ const res = await listMaterialPickingDetail(orderRow.productionOrderId);
+ const records = Array.isArray(res.data)
+ ? res.data
+ : res.data?.records || [];
+ if (res.code === 200 && records.length === 0) {
+ uni.showModal({
+ title: "鎻愮ず",
+ content: "鏈鏂欐棤娉曟姤宸�",
+ showCancel: false,
+ success: () => {
+ goBack();
+ },
+ });
+ return;
+ }
+ } catch (error) {
+ console.error("鏌ヨ棰嗘枡璇︽儏澶辫触:", error);
+ }
+ }
+
form.value.planQuantity =
orderRow.planQuantity != null ? String(orderRow.planQuantity) : "";
- form.value.productProcessRouteItemId =
- orderRow.productProcessRouteItemId || "";
form.value.workOrderId = orderRow.id || "";
form.value.reportWork = orderRow.reportWork || "";
- form.value.productMainId = orderRow.productMainId || null;
form.value.productionOrderRoutingOperationId =
orderRow.productionOrderRoutingOperationId || "";
form.value.productionOrderId = orderRow.productionOrderId || "";
+ form.value.type = orderRow.type;
+
+ if (orderRow.type == 0) {
+ form.value.workHour = orderRow.workHour || 0;
+ } else {
+ form.value.workHour = 0;
+ }
getInfo().then(res => {
form.value.userId = res.user.userId;
diff --git a/src/pages/productionManagement/productionReporting/ledger.vue b/src/pages/productionManagement/productionReporting/ledger.vue
index b71a9a3..fb5d1dd 100644
--- a/src/pages/productionManagement/productionReporting/ledger.vue
+++ b/src/pages/productionManagement/productionReporting/ledger.vue
@@ -49,6 +49,10 @@
<text class="detail-value highlight">{{ item.nickName || '-' }}</text>
</view>
<view class="detail-row">
+ <text class="detail-label">宸ユ椂(h)</text>
+ <text class="detail-value">{{ item.workHour || 0 }}</text>
+ </view>
+ <view class="detail-row">
<text class="detail-label">鎵�灞炲伐搴�</text>
<view class="detail-value">
<up-tag :text="item.process || '-'"
@@ -60,6 +64,10 @@
<view class="detail-row">
<text class="detail-label">宸ュ崟缂栧彿</text>
<text class="detail-value">{{ item.workOrderNo || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">閿�鍞悎鍚屽彿</text>
+ <text class="detail-value">{{ item.salesContractNo || '-' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">浜у搧鍚嶇О</text>
@@ -91,11 +99,11 @@
plain
text="鍙傛暟璇︽儏"
@click="handleShowParams(item)"></up-button>
- <up-button type="error"
+ <!-- <up-button type="error"
size="small"
plain
text="鍒犻櫎"
- @click="handleDelete(item)"></up-button>
+ @click="handleDelete(item)"></up-button> -->
</view>
</view>
</view>
@@ -120,16 +128,20 @@
:key="idx"
class="detail-item">
<view class="detail-row">
- <text class="detail-label">鎶曞叆浜у搧</text>
- <text class="detail-value font-bold">{{ input.productName }}</text>
+ <text class="detail-label">鎶ュ伐鍗曞彿</text>
+ <text class="detail-value">{{ input.productNo || '-' }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">瑙勬牸鍨嬪彿</text>
- <text class="detail-value">{{ input.model }}</text>
+ <text class="detail-label">鎶曞叆浜у搧鍚嶇О</text>
+ <text class="detail-value font-bold">{{ input.productName || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎶曞叆浜у搧鍨嬪彿</text>
+ <text class="detail-value">{{ input.model || '-' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">鎶曞叆鏁伴噺</text>
- <text class="detail-value highlight">{{ input.quantity }} {{ input.unit }}</text>
+ <text class="detail-value highlight">{{ input.quantity || 0 }} {{ input.unit || '' }}</text>
</view>
<up-divider></up-divider>
</view>
diff --git a/src/pages/productionManagement/productionTraceability/index.vue b/src/pages/productionManagement/productionTraceability/index.vue
index b761cb6..d187543 100644
--- a/src/pages/productionManagement/productionTraceability/index.vue
+++ b/src/pages/productionManagement/productionTraceability/index.vue
@@ -25,45 +25,45 @@
<!-- 鍩虹淇℃伅 -->
<view class="info-card">
<view class="card-title">鍩虹淇℃伅</view>
- <view class="info-grid">
- <view class="info-item">
- <text class="label">鐢熶骇璁㈠崟鍙�</text>
- <text class="value">{{ rowData.productionOrderDto.npsNo || '-' }}</text>
+ <view class="base-info">
+ <view class="info-row">
+ <text class="label">鐢熶骇璁㈠崟鍙凤細</text>
+ <text class="value">{{ rowData.productionOrderDto?.npsNo || '-' }}</text>
</view>
- <view class="info-item">
- <text class="label">浜у搧鍚嶇О</text>
- <text class="value">{{ rowData.productionOrderDto.productName || '-' }}</text>
+ <view class="info-row">
+ <text class="label">浜у搧鍚嶇О锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.productName || '-' }}</text>
</view>
- <view class="info-item">
- <text class="label">浜у搧瑙勬牸</text>
- <text class="value">{{ rowData.productionOrderDto.model || '-' }}</text>
+ <view class="info-row">
+ <text class="label">瑙勬牸鍨嬪彿锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.model || '-' }}</text>
</view>
- <view class="info-item">
- <text class="label">璁″垝鏁伴噺</text>
- <text class="value">{{ rowData.productionOrderDto.quantity || 0 }} {{ rowData.productionOrderDto.unit || '' }}</text>
+ <view class="info-row">
+ <text class="label">璁″垝鏁伴噺锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.quantity || 0 }} {{ rowData.productionOrderDto?.unit }}</text>
</view>
- <view class="info-item">
- <text class="label">褰撳墠鐘舵��</text>
- <up-tag :text="getStatusText(rowData.productionOrderDto.status)"
- style="width:100rpx"
- :type="getStatusType(rowData.productionOrderDto.status)"
- size="mini" />
+ <view class="info-row">
+ <text class="label">褰撳墠鐘舵�侊細</text>
+ <view class="value">
+ <up-tag :text="getStatusText(rowData.productionOrderDto?.status)"
+ :type="getStatusType(rowData.productionOrderDto?.status)"
+ size="mini" />
+ </view>
</view>
- <view class="info-item">
- <text class="label">瀹㈡埛鍚嶇О</text>
- <text class="value">{{ rowData.productionOrderDto.customerName || '-' }}</text>
+ <view class="info-row">
+ <text class="label">瀹㈡埛鍚嶇О锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.customerName || '-' }}</text>
</view>
- <view class="info-item">
- <text class="label">寮�濮嬫棩鏈�</text>
- <text class="value">{{ formatDate(rowData.productionOrderDto.startTime) }}</text>
+ <view class="info-row">
+ <text class="label">寮�濮嬫棩鏈燂細</text>
+ <text class="value">{{ formatDate(rowData.productionOrderDto?.startTime) }}</text>
</view>
- <view class="info-item full-width">
- <text class="label">瀹屾垚杩涘害</text>
- <view class="progress-container">
- <up-line-progress :percentage="formatProgress(rowData.productionOrderDto.completionStatus)"
- :activeColor="progressColor(rowData.productionOrderDto.completionStatus)"
- height="8"></up-line-progress>
- <text class="progress-text">{{ formatProgress(rowData.productionOrderDto.completionStatus) }}%</text>
+ <view class="info-row">
+ <text class="label">瀹屾垚杩涘害锛�</text>
+ <view class="value progress-box">
+ <up-line-progress :percentage="formatProgress(rowData.productionOrderDto?.completionStatus)"
+ :activeColor="progressColor(formatProgress(rowData.productionOrderDto?.completionStatus))"
+ :showText="true" />
</view>
</view>
</view>
@@ -86,8 +86,16 @@
<text class="value">{{ item.workOrder.productName }} / {{ item.workOrder.model }}</text>
</view>
<view class="content-row">
+ <text class="label">褰撳墠宸ュ簭锛�</text>
+ <text class="value">{{ item.workOrder.operationName || '-' }}</text>
+ </view>
+ <view class="content-row">
<text class="label">闇�姹�/瀹屾垚锛�</text>
<text class="value">{{ item.workOrder.planQuantity }} / {{ item.workOrder.completeQuantity }}</text>
+ </view>
+ <view class="content-row">
+ <text class="label">鎶ュ簾鏁伴噺锛�</text>
+ <text class="value error-text">{{ item.workOrder.scrapQty || 0 }}</text>
</view>
</view>
<view class="card-footer">
@@ -183,6 +191,9 @@
class="detail-item">
<view class="item-main">
<view class="item-row"><text class="label">鎶ュ伐鍗曞彿锛�</text><text class="value">{{ report.productNo }}</text></view>
+ <view class="item-row"><text class="label">浜у嚭鏁伴噺锛�</text><text class="value">{{ report.quantity || 0 }}</text></view>
+ <view class="item-row"><text class="label">鎶ュ簾鏁伴噺锛�</text><text class="value error-text">{{ report.scrapQty || 0 }}</text></view>
+ <view class="item-row"><text class="label">宸ユ椂(h)锛�</text><text class="value">{{ report.workHour || 0 }}</text></view>
<view class="item-row"><text class="label">鍒涘缓浜猴細</text><text class="value">{{ report.userName }}</text></view>
<view class="item-row"><text class="label">鍒涘缓鏃堕棿锛�</text><text class="value">{{ formatDate(report.createTime, '{y}-{m}-{d} {h}:{i}') }}</text></view>
</view>
@@ -215,11 +226,14 @@
<view class="input-list-popup">
<view v-for="(item, idx) in inputListData"
:key="idx"
- class="input-item">
- <view class="input-row"><text class="label">鐗╂枡鍚嶇О锛�</text><text class="value">{{ item.materialName }}</text></view>
- <view class="input-row"><text class="label">瑙勬牸鍨嬪彿锛�</text><text class="value">{{ item.model }}</text></view>
- <view class="input-row"><text class="label">鎶曞叆鏁伴噺锛�</text><text class="value">{{ item.quantity }} {{ item.unit }}</text></view>
- <view class="input-row"><text class="label">鎵规鍙凤細</text><text class="value">{{ item.batchNo }}</text></view>
+ class="quality-record">
+ <view class="record-title">鎶曞叆璁板綍 {{ idx + 1 }}</view>
+ <view class="info-grid">
+ <view class="info-item"><text class="label">鎶ュ伐鍗曞彿</text><text class="value">{{ item.productNo || '-' }}</text></view>
+ <view class="info-item"><text class="label">鎶曞叆鏁伴噺</text><text class="value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text></view>
+ <view class="info-item full-width"><text class="label">鎶曞叆浜у搧鍚嶇О</text><text class="value">{{ item.productName || '-' }}</text></view>
+ <view class="info-item full-width"><text class="label">鎶曞叆浜у搧鍨嬪彿</text><text class="value">{{ item.model || '-' }}</text></view>
+ </view>
</view>
<view v-if="!inputListData || inputListData.length === 0"
class="no-data-minor">{{ inputLoading ? '鍔犺浇涓�...' : '鏆傛棤鎶曞叆璁板綍' }}</view>
@@ -253,18 +267,26 @@
size="mini" /></view>
<view class="info-item"><text class="label">妫�楠屽憳</text><text class="value">{{ record.userName }}</text></view>
<view class="info-item"><text class="label">鏁伴噺</text><text class="value">{{ record.quantity }} {{ record.unit }}</text></view>
+ <view class="info-item"><text class="label">鎶ュ伐鍗曞彿</text><text class="value">{{ record.reportNo || '-' }}</text></view>
+ <view class="info-item"><text class="label">浜у搧鍚嶇О</text><text class="value">{{ record.productName || '-' }}</text></view>
+ <view class="info-item"><text class="label">瑙勬牸鍨嬪彿</text><text class="value">{{ record.model || '-' }}</text></view>
+ <view class="info-item"><text class="label">妫�娴嬪崟浣�</text><text class="value">{{ record.checkCompany || '-' }}</text></view>
</view>
<view class="params-table">
<view class="table-header">
<text class="col">鎸囨爣</text>
+ <text class="col">鍗曚綅</text>
<text class="col">鏍囧噯鍊�</text>
+ <text class="col">鍐呮帶鍊�</text>
<text class="col">瀹為檯鍊�</text>
</view>
<view v-for="(param, pIdx) in record.inspectParamList"
:key="pIdx"
class="table-row">
<text class="col">{{ param.parameterItem }}</text>
+ <text class="col">{{ param.unit || '-' }}</text>
<text class="col">{{ param.standardValue }}</text>
+ <text class="col">{{ param.controlValue || '-' }}</text>
<text class="col"
:class="{ 'error-text': param.testValue != param.standardValue }">{{ param.testValue }}</text>
</view>
@@ -440,6 +462,7 @@
workOrder: row.workOrder || {},
reports: (row.reportList || []).map(r => ({
...r.reportMain,
+ ...(r.reportOutputList ? r.reportOutputList[0] : {}),
id: r.reportMain.id,
productionOperationParamList: r.reportParamList || [],
})),
@@ -471,6 +494,8 @@
qualityRecords.value = inspects.map(i => ({
...i.inspect,
reportNo: i.reportNo,
+ productName: row.workOrder?.productName || "-",
+ model: row.workOrder?.model || "-",
userName: i.reportMain?.userName || "-",
inspectParamList: i.inspectParamList || [],
}));
@@ -708,6 +733,66 @@
}
}
+ .base-info {
+ background: #fff;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ margin-bottom: 30rpx;
+
+ .info-row {
+ margin-bottom: 16rpx;
+ font-size: 28rpx;
+ display: flex;
+ align-items: center;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .label {
+ color: #999;
+ min-width: 180rpx;
+ }
+ .value {
+ color: #333;
+ flex: 1;
+ font-weight: 500;
+
+ &.progress-box {
+ flex: 1;
+ }
+ }
+ }
+ }
+
+ .info-grid {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 10rpx 0;
+
+ .info-item {
+ width: 50%;
+ margin-bottom: 20rpx;
+ display: flex;
+ flex-direction: column;
+
+ &.full-width {
+ width: 100%;
+ }
+
+ .label {
+ font-size: 24rpx;
+ color: #999;
+ margin-bottom: 4rpx;
+ }
+ .value {
+ font-size: 28rpx;
+ color: #333;
+ font-weight: 500;
+ }
+ }
+ }
+
.popup-content {
background: #fff;
padding: 30rpx;
diff --git a/src/pages/qualityManagement/finalInspection/add.vue b/src/pages/qualityManagement/finalInspection/add.vue
index 9b605c7..94321dd 100644
--- a/src/pages/qualityManagement/finalInspection/add.vue
+++ b/src/pages/qualityManagement/finalInspection/add.vue
@@ -51,6 +51,7 @@
</up-form-item>
<up-form-item label="鎸囨爣閫夋嫨"
prop="testStandardId"
+ required
border-bottom>
<up-input v-model="testStandardDisplay"
placeholder="璇烽�夋嫨鎸囨爣"
@@ -442,7 +443,7 @@
{ required: true, message: "璇烽�夋嫨浜у搧鍨嬪彿", trigger: "change" },
],
testStandardId: [
- { required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change" },
+ { required: true, message: "璇烽�夋嫨鎸囨爣", trigger: "change" },
],
unit: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -602,9 +603,9 @@
// 鑾峰彇宸ュ簭鍒楄〃
const getprocessList = () => {
- list().then(res => {
- processList.value = res.data;
- });
+ // list().then(res => {
+ // processList.value = res.data;
+ // });
};
// 鑾峰彇浜у搧閫夐」
@@ -691,6 +692,10 @@
showToast("璇烽�夋嫨浜у搧");
return;
}
+ if (!form.value.testStandardId) {
+ showToast("璇烽�夋嫨鎸囨爣");
+ return;
+ }
if (!form.value.checkResult) {
showToast("璇烽�夋嫨妫�娴嬬粨鏋�");
return;
@@ -699,7 +704,7 @@
loading.value = true;
form.value.inspectType = 2;
- if (isEdit.value) {
+ if (!isEdit.value) {
tableData.value.forEach(item => {
delete item.id;
});
diff --git a/src/pages/qualityManagement/finalInspection/detail.vue b/src/pages/qualityManagement/finalInspection/detail.vue
index 0e5bba6..d274a28 100644
--- a/src/pages/qualityManagement/finalInspection/detail.vue
+++ b/src/pages/qualityManagement/finalInspection/detail.vue
@@ -15,7 +15,8 @@
</view>
<text class="header-title">{{ detailData.productName || '-' }}</text>
<view class="status-tags">
- <u-tag :type="getTagType(detailData.checkResult)"
+ <u-tag v-if="detailData.checkResult"
+ :type="getTagType(detailData.checkResult)"
size="small"
class="status-tag">
{{ detailData.checkResult || '-' }}
diff --git a/src/pages/qualityManagement/finalInspection/index.vue b/src/pages/qualityManagement/finalInspection/index.vue
index 180d08d..0e860ff 100644
--- a/src/pages/qualityManagement/finalInspection/index.vue
+++ b/src/pages/qualityManagement/finalInspection/index.vue
@@ -76,7 +76,8 @@
</view>
</view>
<view class="status-tags">
- <u-tag :type="getTagType(item.checkResult)"
+ <u-tag v-if="item.checkResult"
+ :type="getTagType(item.checkResult)"
size="mini"
class="status-tag">
{{ item.checkResult }}
@@ -117,29 +118,29 @@
</view>
<!-- 鎿嶄綔鎸夐挳 -->
<view class="action-buttons">
- <!-- <u-button type="primary"
+ <u-button type="primary"
size="small"
class="action-btn"
:disabled="item.inspectState"
@click.stop="startInspection(item)">
缂栬緫
- </u-button> -->
+ </u-button>
<u-button type="info"
size="small"
class="action-btn"
@click.stop="viewDetail(item)">
璇︽儏
</u-button>
- <!-- <u-button type="success"
+ <u-button type="success"
size="small"
class="action-btn"
:disabled="item.inspectState"
@click.stop="submitInspection(item)">
鎻愪氦
- </u-button> -->
+ </u-button>
</view>
<view class="action-buttons">
- <!-- <u-button type="info"
+ <u-button type="info"
size="small"
class="action-btn"
@click.stop="viewFileList(item)">
@@ -151,7 +152,7 @@
:disabled="item.inspectState || item.checkName !== ''"
@click.stop="assignInspector(item)">
鍒嗛厤妫�楠屽憳
- </u-button> -->
+ </u-button>
</view>
</view>
</view>
@@ -163,12 +164,12 @@
</view>
<!-- 鍒嗛〉缁勪欢 -->
<!-- 娴姩鏂板鎸夐挳 -->
- <!-- <view class="fab-button"
+ <view class="fab-button"
@click="addInspection">
<up-icon name="plus"
size="24"
color="#ffffff"></up-icon>
- </view> -->
+ </view>
<!-- 鏃ユ湡閫夋嫨鍣� -->
<up-popup v-model:show="showDate"
mode="date"
diff --git a/src/pages/qualityManagement/materialInspection/add.vue b/src/pages/qualityManagement/materialInspection/add.vue
index 63a94ef..e04391f 100644
--- a/src/pages/qualityManagement/materialInspection/add.vue
+++ b/src/pages/qualityManagement/materialInspection/add.vue
@@ -51,6 +51,7 @@
</up-form-item>
<up-form-item label="鎸囨爣閫夋嫨"
prop="testStandardId"
+ required
border-bottom>
<up-input v-model="testStandardDisplay"
placeholder="璇烽�夋嫨鎸囨爣"
@@ -114,9 +115,10 @@
border-bottom>
<up-input v-model="form.checkTime"
placeholder="璇烽�夋嫨妫�娴嬫棩鏈�"
- readonly />
+ readonly
+ @click="showDatePicker" />
<!-- <template #right>
- <up-icon name="calendar"
+ <up-icon name="arrow-right"
@click="showDatePicker"></up-icon>
</template> -->
</up-form-item>
@@ -191,11 +193,15 @@
</up-button>
</view>
<!-- 鏃ユ湡閫夋嫨鍣� -->
- <up-popup v-model:show="showDate"
- mode="date"
- :start-year="2020"
- :end-year="2030"
- @confirm="confirmDate" />
+ <up-popup :show="showDate"
+ mode="bottom"
+ @close="showDate = false">
+ <up-datetime-picker :show="true"
+ v-model="pickerValue"
+ @confirm="confirmDate"
+ @cancel="showDate = false"
+ mode="date" />
+ </up-popup>
<!-- 渚涘簲鍟嗛�夋嫨 -->
<up-action-sheet :show="showSupplierSheet"
:actions="supplierOptions"
@@ -337,6 +343,7 @@
const loading = ref(false);
// 鏃ユ湡閫夋嫨鍣�
const showDate = ref(false);
+ const pickerValue = ref(Date.now());
// 渚涘簲鍟嗛�夋嫨
const showSupplierSheet = ref(false);
// 浜у搧閫夋嫨
@@ -448,7 +455,7 @@
{ required: true, message: "璇烽�夋嫨浜у搧鍨嬪彿", trigger: "change" },
],
testStandardId: [
- { required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change" },
+ { required: true, message: "璇烽�夋嫨鎸囨爣", trigger: "change" },
],
unit: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -483,6 +490,7 @@
// 纭鏃ユ湡閫夋嫨
const confirmDate = e => {
form.value.checkTime = dayjs(e.value).format("YYYY-MM-DD");
+ showDate.value = false;
};
// 閫夋嫨渚涘簲鍟�
@@ -697,6 +705,10 @@
showToast("璇烽�夋嫨浜у搧");
return;
}
+ if (!form.value.testStandardId) {
+ showToast("璇烽�夋嫨鎸囨爣");
+ return;
+ }
if (!form.value.checkResult) {
showToast("璇烽�夋嫨妫�娴嬬粨鏋�");
return;
@@ -705,7 +717,7 @@
loading.value = true;
form.value.inspectType = 0;
- if (isEdit.value) {
+ if (!isEdit.value) {
tableData.value.forEach(item => {
delete item.id;
});
diff --git a/src/pages/qualityManagement/materialInspection/detail.vue b/src/pages/qualityManagement/materialInspection/detail.vue
index e45b9c0..12ff7a3 100644
--- a/src/pages/qualityManagement/materialInspection/detail.vue
+++ b/src/pages/qualityManagement/materialInspection/detail.vue
@@ -15,7 +15,8 @@
</view>
<text class="header-title">{{ detailData.productName || '-' }}</text>
<view class="status-tags">
- <u-tag :type="getTagType(detailData.checkResult)"
+ <u-tag v-if="detailData.checkResult"
+ :type="getTagType(detailData.checkResult)"
size="small"
class="status-tag">
{{ detailData.checkResult || '-' }}
diff --git a/src/pages/qualityManagement/materialInspection/index.vue b/src/pages/qualityManagement/materialInspection/index.vue
index 83ab257..eb4a140 100644
--- a/src/pages/qualityManagement/materialInspection/index.vue
+++ b/src/pages/qualityManagement/materialInspection/index.vue
@@ -76,7 +76,8 @@
</view>
</view>
<view class="status-tags">
- <u-tag :type="getTagType(item.checkResult)"
+ <u-tag v-if="item.checkResult"
+ :type="getTagType(item.checkResult)"
size="mini"
class="status-tag">
{{ item.checkResult }}
@@ -117,29 +118,29 @@
</view>
<!-- 鎿嶄綔鎸夐挳 -->
<view class="action-buttons">
- <!-- <u-button type="primary"
+ <u-button type="primary"
size="small"
class="action-btn"
:disabled="item.inspectState"
@click.stop="startInspection(item)">
缂栬緫
- </u-button> -->
+ </u-button>
<u-button type="info"
size="small"
class="action-btn"
@click.stop="viewDetail(item)">
璇︽儏
</u-button>
- <!-- <u-button type="success"
+ <u-button type="success"
size="small"
class="action-btn"
:disabled="item.inspectState"
@click.stop="submitInspection(item)">
鎻愪氦
- </u-button> -->
+ </u-button>
</view>
<view class="action-buttons">
- <!-- <u-button type="info"
+ <u-button type="info"
size="small"
class="action-btn"
@click.stop="viewFileList(item)">
@@ -151,7 +152,7 @@
:disabled="item.inspectState || item.checkName !== ''"
@click.stop="assignInspector(item)">
鍒嗛厤妫�楠屽憳
- </u-button> -->
+ </u-button>
</view>
</view>
</view>
@@ -163,12 +164,12 @@
</view>
<!-- 鍒嗛〉缁勪欢 -->
<!-- 娴姩鏂板鎸夐挳 -->
- <!-- <view class="fab-button"
+ <view class="fab-button"
@click="addInspection">
<up-icon name="plus"
size="24"
color="#ffffff"></up-icon>
- </view> -->
+ </view>
<!-- 鏃ユ湡閫夋嫨鍣� -->
<up-popup v-model:show="showDate"
mode="date"
diff --git a/src/pages/qualityManagement/processInspection/add.vue b/src/pages/qualityManagement/processInspection/add.vue
index f146fe1..2f6a6d0 100644
--- a/src/pages/qualityManagement/processInspection/add.vue
+++ b/src/pages/qualityManagement/processInspection/add.vue
@@ -51,6 +51,7 @@
</up-form-item>
<up-form-item label="鎸囨爣閫夋嫨"
prop="testStandardId"
+ required
border-bottom>
<up-input v-model="testStandardDisplay"
placeholder="璇烽�夋嫨鎸囨爣"
@@ -184,11 +185,15 @@
</up-button>
</view>
<!-- 鏃ユ湡閫夋嫨鍣� -->
- <up-popup v-model:show="showDate"
- mode="date"
- :start-year="2020"
- :end-year="2030"
- @confirm="confirmDate" />
+ <up-popup :show="showDate"
+ mode="bottom"
+ @close="showDate = false">
+ <up-datetime-picker :show="true"
+ v-model="pickerValue"
+ @confirm="confirmDate"
+ @cancel="showDate = false"
+ mode="date" />
+ </up-popup>
<!-- 宸ュ簭閫夋嫨 -->
<up-action-sheet :show="showprocessSheet"
:actions="processOptions"
@@ -313,8 +318,8 @@
qualityInspectParamInfo,
qualityInspectDetailByProductId,
getQualityTestStandardParamByTestStandardId,
- list,
} from "@/api/qualityManagement/materialInspection.js";
+ import { getProcessList } from "@/api/productionManagement/processManagement.js";
import { userListNoPage } from "@/api/system/user.js";
// 鏄剧ず鎻愮ず淇℃伅
@@ -442,7 +447,7 @@
{ required: true, message: "璇烽�夋嫨浜у搧鍨嬪彿", trigger: "change" },
],
testStandardId: [
- { required: false, message: "璇烽�夋嫨鎸囨爣", trigger: "change" },
+ { required: true, message: "璇烽�夋嫨鎸囨爣", trigger: "change" },
],
unit: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -602,8 +607,8 @@
// 鑾峰彇宸ュ簭鍒楄〃
const getprocessList = () => {
- list().then(res => {
- processList.value = res.data;
+ getProcessList({ size: -1, current: -1 }).then(res => {
+ processList.value = res.data?.records || res.data || [];
});
};
@@ -691,6 +696,10 @@
showToast("璇烽�夋嫨浜у搧");
return;
}
+ if (!form.value.testStandardId) {
+ showToast("璇烽�夋嫨鎸囨爣");
+ return;
+ }
if (!form.value.checkResult) {
showToast("璇烽�夋嫨妫�娴嬬粨鏋�");
return;
@@ -699,7 +708,7 @@
loading.value = true;
form.value.inspectType = 1;
- if (isEdit.value) {
+ if (!isEdit.value) {
tableData.value.forEach(item => {
delete item.id;
});
diff --git a/src/pages/qualityManagement/processInspection/detail.vue b/src/pages/qualityManagement/processInspection/detail.vue
index 721bb2a..798aeee 100644
--- a/src/pages/qualityManagement/processInspection/detail.vue
+++ b/src/pages/qualityManagement/processInspection/detail.vue
@@ -15,10 +15,11 @@
</view>
<text class="header-title">{{ detailData.productName || '-' }}</text>
<view class="status-tags">
- <u-tag :type="getTagType(detailData.checkResult)"
+ <u-tag v-if="detailData.checkResult"
+ :type="getTagType(detailData.checkResult)"
size="small"
class="status-tag">
- {{ detailData.checkResult || '-' }}
+ {{ detailData.checkResult }}
</u-tag>
<u-tag :type="getStateTagType(detailData.inspectState)"
size="small"
diff --git a/src/pages/qualityManagement/processInspection/index.vue b/src/pages/qualityManagement/processInspection/index.vue
index 14e66ef..40358ef 100644
--- a/src/pages/qualityManagement/processInspection/index.vue
+++ b/src/pages/qualityManagement/processInspection/index.vue
@@ -76,7 +76,8 @@
</view>
</view>
<view class="status-tags">
- <u-tag :type="getTagType(item.checkResult)"
+ <u-tag v-if="item.checkResult"
+ :type="getTagType(item.checkResult)"
size="mini"
class="status-tag">
{{ item.checkResult }}
@@ -117,29 +118,29 @@
</view>
<!-- 鎿嶄綔鎸夐挳 -->
<view class="action-buttons">
- <!-- <u-button type="primary"
+ <u-button type="primary"
size="small"
class="action-btn"
:disabled="item.inspectState"
@click.stop="startInspection(item)">
缂栬緫
- </u-button> -->
+ </u-button>
<u-button type="info"
size="small"
class="action-btn"
@click.stop="viewDetail(item)">
璇︽儏
</u-button>
- <!-- <u-button type="success"
+ <u-button type="success"
size="small"
class="action-btn"
:disabled="item.inspectState"
@click.stop="submitInspection(item)">
鎻愪氦
- </u-button> -->
+ </u-button>
</view>
<view class="action-buttons">
- <!-- <u-button type="info"
+ <u-button type="info"
size="small"
class="action-btn"
@click.stop="viewFileList(item)">
@@ -151,7 +152,7 @@
:disabled="item.inspectState || item.checkName !== ''"
@click.stop="assignInspector(item)">
鍒嗛厤妫�楠屽憳
- </u-button> -->
+ </u-button>
</view>
</view>
</view>
@@ -163,12 +164,12 @@
</view>
<!-- 鍒嗛〉缁勪欢 -->
<!-- 娴姩鏂板鎸夐挳 -->
- <!-- <view class="fab-button"
+ <view class="fab-button"
@click="addInspection">
<up-icon name="plus"
size="24"
color="#ffffff"></up-icon>
- </view> -->
+ </view>
<!-- 鏃ユ湡閫夋嫨鍣� -->
<up-popup v-model:show="showDate"
mode="date"
diff --git a/src/pages/sales/salesAccount/goOut.vue b/src/pages/sales/salesAccount/goOut.vue
index 9980e5f..42321c9 100644
--- a/src/pages/sales/salesAccount/goOut.vue
+++ b/src/pages/sales/salesAccount/goOut.vue
@@ -1,657 +1,451 @@
<template>
- <view class="account-detail">
+ <view class="shipment-page">
<PageHeader title="鍙戣揣"
@back="goBack" />
- <!-- 琛ㄥ崟鍖哄煙 -->
- <u-form ref="formRef"
- @submit="submitForm"
- :rules="rules"
- :model="form"
- label-width="140rpx">
- <u-form-item prop="typeValue"
- label="鍙戣揣绫诲瀷"
- required>
- <u-input v-model="typeValue"
- readonly
- placeholder="璇烽�夋嫨鍙戣揣鏂瑰紡"
- @click="showPicker = true" />
- <template #right>
- <up-icon name="arrow-right"
- @click="showPicker = true"></up-icon>
- </template>
- </u-form-item>
- </u-form>
- <!-- 閫夋嫨鍣ㄥ脊绐� -->
- <up-action-sheet :show="showPicker"
- :actions="productOptions"
- title="鍙戣揣鏂瑰紡"
- @select="onConfirm"
- @close="showPicker = false" />
- <!-- 瀹℃牳娴佺▼鍖哄煙 -->
- <view class="approval-process">
- <view class="approval-header">
- <text class="approval-title">瀹℃牳娴佺▼</text>
- <text class="approval-desc">姣忎釜姝ラ鍙兘閫夋嫨涓�涓鎵逛汉</text>
- </view>
- <view class="approval-steps">
- <view v-for="(step, stepIndex) in approverNodes"
- :key="stepIndex"
- class="approval-step">
- <view class="step-dot"></view>
- <view class="step-title">
- <text>瀹℃壒浜�</text>
+ <view class="form-container">
+ <up-form ref="formRef"
+ :model="form"
+ :rules="rules"
+ label-width="100"
+ input-align="right"
+ error-message-align="right">
+ <!-- 鍩烘湰淇℃伅 -->
+ <u-cell-group title="鍩烘湰淇℃伅"
+ class="form-section">
+ <up-form-item label="鍙戣揣鏂瑰紡"
+ prop="type"
+ required>
+ <up-input v-model="form.type"
+ readonly
+ placeholder="璇烽�夋嫨鍙戣揣鏂瑰紡"
+ @click="showTypePicker = true" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showTypePicker = true"></up-icon>
+ </template>
+ </up-form-item>
+ <block v-if="form.type === '璐ц溅'">
+ <up-form-item label="鍙戣揣杞︾墝"
+ prop="shippingCarNumber"
+ required>
+ <up-input v-model="form.shippingCarNumber"
+ placeholder="璇疯緭鍏ュ彂璐ц溅鐗屽彿"
+ clearable />
+ </up-form-item>
+ </block>
+ <block v-if="form.type === '蹇��'">
+ <up-form-item label="蹇�掑叕鍙�"
+ prop="expressCompany"
+ required>
+ <up-input v-model="form.expressCompany"
+ placeholder="璇疯緭鍏ュ揩閫掑叕鍙�"
+ clearable />
+ </up-form-item>
+ <up-form-item label="蹇�掑崟鍙�"
+ prop="expressNumber"
+ required>
+ <up-input v-model="form.expressNumber"
+ placeholder="璇疯緭鍏ュ揩閫掑崟鍙�"
+ clearable />
+ </up-form-item>
+ </block>
+ </u-cell-group>
+ <!-- 鎵规閫夋嫨 -->
+ <u-cell-group title="鎵规閫夋嫨"
+ class="form-section">
+ <view class="section-header-info">
+ <text class="subtitle">寰呭彂璐ф暟閲�: {{ goOutData.noQuantity || 0 }}</text>
</view>
- <view class="approver-container">
- <view v-if="step.nickName"
- class="approver-item">
- <view class="approver-avatar">
- <text class="avatar-text">{{ step.nickName.charAt(0) }}</text>
- <view class="status-dot"></view>
+ <view v-if="batchList.length === 0"
+ class="empty-text">
+ <text>鏆傛棤鍙敤搴撳瓨鎵规</text>
+ </view>
+ <view v-else
+ class="batch-list">
+ <view v-for="(item, index) in batchList"
+ :key="index"
+ class="batch-card">
+ <view class="batch-header">
+ <text class="batch-no">鎵瑰彿: {{ item.batchNo }}</text>
+ <text class="batch-qty">搴撳瓨: {{ getAvailableQty(item) }}</text>
</view>
- <view class="approver-info">
- <text class="approver-name">{{ step.nickName }}</text>
+ <up-divider></up-divider>
+ <view class="batch-body">
+ <up-form-item label="鍙戣揣鏁伴噺">
+ <up-input v-model="item.deliveryQuantity"
+ type="digit"
+ placeholder="0.00"
+ input-align="right"
+ @blur="onBatchQtyChange(item)" />
+ </up-form-item>
</view>
- <view class="delete-approver-btn"
- @click="removeApprover(stepIndex)">脳</view>
- </view>
- <view v-else
- class="add-approver-btn"
- @click="addApprover(stepIndex)">
- <view class="add-circle">+</view>
- <text class="add-label">閫夋嫨瀹℃壒浜�</text>
</view>
</view>
- <view class="step-line"
- v-if="stepIndex < approverNodes.length - 1"></view>
- <view class="delete-step-btn"
- v-if="approverNodes.length > 1"
- @click="removeApprovalStep(stepIndex)">鍒犻櫎鑺傜偣</view>
- </view>
- </view>
- <view class="add-step-btn">
- <u-button icon="plus"
- plain
- type="primary"
- style="width: 100%"
- @click="addApprovalStep">鏂板鑺傜偣</u-button>
- </view>
+ </u-cell-group>
+ <!-- 鍙戣揣鍥剧墖 -->
+ <u-cell-group title="鍙戣揣鍥剧墖"
+ class="form-section">
+ <view class="upload-container">
+ <up-upload :fileList="fileList"
+ @afterRead="afterRead"
+ @delete="deleteFile"
+ multiple
+ :maxCount="9"
+ width="160rpx"
+ height="160rpx" />
+ </view>
+ </u-cell-group>
+ </up-form>
</view>
<!-- 搴曢儴鎸夐挳 -->
- <view class="footer-btns">
- <u-button class="cancel-btn"
- @click="goBack">鍙栨秷</u-button>
- <u-button class="save-btn"
- @click="submitForm">鍙戣揣</u-button>
- </view>
+ <FooterButtons confirmText="纭鍙戣揣"
+ @cancel="goBack"
+ @confirm="submitForm" />
+ <!-- 鍙戣揣鏂瑰紡閫夋嫨鍣� -->
+ <up-action-sheet :show="showTypePicker"
+ :actions="typeActions"
+ title="鍙戣揣鏂瑰紡"
+ @select="onTypeSelect"
+ @close="showTypePicker = false" />
</view>
</template>
<script setup>
- import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
+ import config from "@/config";
+ import { ref, onMounted, reactive } from "vue";
import PageHeader from "@/components/PageHeader.vue";
+ import FooterButtons from "@/components/FooterButtons.vue";
import { addShippingInfo } from "@/api/salesManagement/salesLedger";
- const showToast = message => {
- uni.showToast({
- title: message,
- icon: "none",
- });
- };
- import { userListNoPageByTenantId } from "@/api/system/user";
+ import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory";
+ import { getToken } from "@/utils/auth";
- const data = reactive({
- form: {
- approveTime: "",
- approveId: "",
- approveUser: "",
- approveUserName: "",
- approveDeptName: "",
- approveDeptId: "",
- approveReason: "",
- checkResult: "",
- tempFileIds: [],
- approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
- startDate: "",
- endDate: "",
- location: "",
- price: "",
- },
- rules: {
- typeValue: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
- },
- });
- const { form, rules } = toRefs(data);
- const showPicker = ref(false);
- const productOptions = ref([
- {
- value: "璐ц溅",
- name: "璐ц溅",
- },
- {
- value: "蹇��",
- name: "蹇��",
- },
- ]);
- const operationType = ref("");
- const currentApproveStatus = ref("");
- const approverNodes = ref([]);
- const userList = ref([]);
- const formRef = ref(null);
- const approveType = ref(0);
const goOutData = ref({});
+ const batchList = ref([]);
+ const fileList = ref([]);
+ const showTypePicker = ref(false);
+ const typeActions = [
+ { name: "璐ц溅", value: "璐ц溅" },
+ { name: "蹇��", value: "蹇��" },
+ ];
+
+ const form = reactive({
+ type: "璐ц溅",
+ shippingCarNumber: "",
+ expressCompany: "",
+ expressNumber: "",
+ });
+
+ const rules = {
+ type: [{ required: true, message: "璇烽�夋嫨鍙戣揣鏂瑰紡", trigger: "change" }],
+ shippingCarNumber: [
+ {
+ required: true,
+ validator: (rule, value, callback) => {
+ if (form.type === "璐ц溅" && !value) {
+ return false;
+ }
+ return true;
+ },
+ message: "璇疯緭鍏ヨ溅鐗屽彿",
+ trigger: "blur",
+ },
+ ],
+ expressCompany: [
+ {
+ required: true,
+ validator: (rule, value, callback) => {
+ if (form.type === "蹇��" && !value) {
+ return false;
+ }
+ return true;
+ },
+ message: "璇疯緭鍏ュ揩閫掑叕鍙�",
+ trigger: "blur",
+ },
+ ],
+ expressNumber: [
+ {
+ required: true,
+ validator: (rule, value, callback) => {
+ if (form.type === "蹇��" && !value) {
+ return false;
+ }
+ return true;
+ },
+ message: "璇疯緭鍏ュ揩閫掑崟鍙�",
+ trigger: "blur",
+ },
+ ],
+ };
+
+ const formRef = ref(null);
+
onMounted(async () => {
- try {
- userListNoPageByTenantId().then(res => {
- userList.value = res.data;
- });
- // 浠庢湰鍦板瓨鍌ㄨ幏鍙栧彂璐ц鎯�
- goOutData.value = JSON.parse(uni.getStorageSync("goOutData"));
- console.log(goOutData.value, "goOutData.value");
-
- // 鍒濆鍖栧鎵规祦绋嬭妭鐐癸紝榛樿涓�涓妭鐐�
- approverNodes.value = [{ id: 1, userId: null }];
-
- // 鐩戝惉鑱旂郴浜洪�夋嫨浜嬩欢
- uni.$on("selectContact", handleSelectContact);
- } catch (error) {
- console.error("鑾峰彇澶辫触:", error);
+ const storedData = uni.getStorageSync("goOutData");
+ goOutData.value = JSON.parse(storedData || "{}");
+ if (goOutData.value.productModelId) {
+ loadBatches(goOutData.value.productModelId);
}
});
- onUnmounted(() => {
- // 绉婚櫎浜嬩欢鐩戝惉
- uni.$off("selectContact", handleSelectContact);
- });
- const typeValue = ref("璐ц溅");
- const onConfirm = item => {
- // 璁剧疆閫変腑鐨勯儴闂�
- typeValue.value = item.name;
- showPicker.value = false;
+ const loadBatches = async modelId => {
+ if (!modelId) return;
+ try {
+ const res = await getStockInventoryByModelId(modelId);
+ const rawList = Array.isArray(res?.data)
+ ? res.data
+ : res?.data?.records || res?.data?.rows || res || [];
+ const seenIds = new Set();
+ batchList.value = rawList
+ .filter(item => {
+ if (!item?.id || !item?.batchNo || seenIds.has(item.id)) {
+ return false;
+ }
+ seenIds.add(item.id);
+ return true;
+ })
+ .map(item => ({
+ ...item,
+ deliveryQuantity: "",
+ }));
+ } catch (e) {
+ console.error("鍔犺浇鎵规澶辫触", e);
+ }
};
- const goBack = () => {
- // 娓呴櫎鏈湴瀛樺偍鐨勬暟鎹�
- uni.removeStorageSync("operationType");
- uni.removeStorageSync("invoiceLedgerEditRow");
- uni.removeStorageSync("approveType");
- uni.navigateBack();
+ const getAvailableQty = item => {
+ const quantity =
+ item?.qualitity ??
+ item?.quantity ??
+ item?.unLockedQuantity ??
+ item?.qualifiedUnLockedQuantity ??
+ item?.qualifiedQuantity ??
+ item?.stockQuantity;
+ return quantity ?? 0;
};
- const submitForm = () => {
- // 妫�鏌ユ瘡涓鎵规楠ゆ槸鍚﹂兘鏈夊鎵逛汉
- const hasEmptyStep = approverNodes.value.some(step => !step.nickName);
- if (hasEmptyStep) {
- showToast("璇蜂负姣忎釜瀹℃壒姝ラ閫夋嫨瀹℃壒浜�");
+ const onBatchQtyChange = item => {
+ const val = parseFloat(item.deliveryQuantity);
+ if (isNaN(val) || val <= 0) {
+ item.deliveryQuantity = "";
return;
}
- formRef.value
- .validate()
- .then(valid => {
- if (valid) {
- // 琛ㄥ崟鏍¢獙閫氳繃锛屽彲浠ユ彁浜ゆ暟鎹�
- // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
- console.log("approverNodes---", approverNodes.value);
- const approveUserIds = approverNodes.value
- .map(node => node.userId)
- .join(",");
- const params = {
- salesLedgerId: goOutData.value.salesLedgerId,
- salesLedgerProductId: goOutData.value.id,
- type: typeValue.value,
- approveUserIds,
- };
- console.log(params, "params");
- addShippingInfo(params).then(res => {
- showToast("鍙戣揣鎴愬姛");
- setTimeout(() => {
- goBack();
- }, 500);
- });
- }
- })
- .catch(error => {
- console.error("琛ㄥ崟鏍¢獙澶辫触:", error);
- // 灏濊瘯鑾峰彇鍏蜂綋鐨勯敊璇瓧娈�
- if (error && error.errors) {
- const firstError = error.errors[0];
- if (firstError) {
- uni.showToast({
- title: firstError.message || "琛ㄥ崟鏍¢獙澶辫触锛岃妫�鏌ュ繀濉」",
- icon: "none",
- });
- return;
+ const available = getAvailableQty(item);
+ if (val > available) {
+ uni.showToast({ title: "涓嶈兘瓒呰繃搴撳瓨鏁伴噺", icon: "none" });
+ item.deliveryQuantity = available.toString();
+ }
+
+ const totalToShip = Number(goOutData.value.noQuantity || 0);
+ const otherBatchesTotal = batchList.value.reduce((sum, b) => {
+ if (b.id === item.id) return sum;
+ return sum + Number(b.deliveryQuantity || 0);
+ }, 0);
+
+ if (val + otherBatchesTotal > totalToShip) {
+ uni.showToast({ title: "鎬绘暟涓嶈兘瓒呰繃寰呭彂璐ф暟閲�", icon: "none" });
+ item.deliveryQuantity = (totalToShip - otherBatchesTotal).toString();
+ }
+ };
+
+ const onTypeSelect = item => {
+ form.type = item.name;
+ showTypePicker.value = false;
+ };
+
+ const afterRead = async event => {
+ const { file } = event;
+ const lists = [].concat(file);
+ const token = getToken();
+
+ for (let i = 0; i < lists.length; i++) {
+ const item = lists[i];
+ const uploadIndex = fileList.value.length;
+ fileList.value.push({
+ ...item,
+ status: "uploading",
+ message: "涓婁紶涓�",
+ });
+
+ uni.uploadFile({
+ url: config.baseUrl + "/common/upload",
+ filePath: item.url,
+ name: "files",
+ header: {
+ Authorization: "Bearer " + token,
+ },
+ success: res => {
+ try {
+ const data = JSON.parse(res.data);
+ if (data.code === 200) {
+ const fileData = Array.isArray(data.data)
+ ? data.data[0]
+ : data.data || data;
+ fileList.value[uploadIndex].status = "success";
+ fileList.value[uploadIndex].message = "";
+ fileList.value[uploadIndex].url = fileData.url;
+ fileList.value[uploadIndex].storageBlobDTO = fileData;
+ } else {
+ fileList.value[uploadIndex].status = "failed";
+ fileList.value[uploadIndex].message = data.msg || "涓婁紶澶辫触";
+ }
+ } catch (e) {
+ fileList.value[uploadIndex].status = "failed";
+ fileList.value[uploadIndex].message = "瑙f瀽澶辫触";
}
- }
- // 鏄剧ず閫氱敤閿欒淇℃伅
- uni.showToast({
- title: "琛ㄥ崟鏍¢獙澶辫触锛岃妫�鏌ュ繀濉」",
- icon: "none",
- });
+ },
+ fail: () => {
+ fileList.value[uploadIndex].status = "failed";
+ fileList.value[uploadIndex].message = "缃戠粶寮傚父";
+ },
});
+ }
};
- // 澶勭悊鑱旂郴浜洪�夋嫨缁撴灉
- const handleSelectContact = data => {
- const { stepIndex, contact } = data;
- // 灏嗛�変腑鐨勮仈绯讳汉璁剧疆涓哄搴斿鎵规楠ょ殑瀹℃壒浜�
- approverNodes.value[stepIndex].userId = contact.userId;
- approverNodes.value[stepIndex].nickName = contact.nickName;
+ const deleteFile = event => {
+ fileList.value.splice(event.index, 1);
};
- const addApprover = stepIndex => {
- // 璺宠浆鍒拌仈绯讳汉閫夋嫨椤甸潰
- uni.setStorageSync("stepIndex", stepIndex);
- uni.navigateTo({
- url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect",
- });
- };
+ const goBack = () => uni.navigateBack();
- const addApprovalStep = () => {
- // 娣诲姞鏂扮殑瀹℃壒姝ラ
- approverNodes.value.push({ userId: null, nickName: null });
- };
+ const submitForm = async () => {
+ const valid = await formRef.value.validate().catch(() => false);
+ if (!valid) return;
- const removeApprover = stepIndex => {
- // 绉婚櫎瀹℃壒浜�
- approverNodes.value[stepIndex].userId = null;
- approverNodes.value[stepIndex].nickName = null;
- };
+ const selectedBatches = batchList.value.filter(
+ b => parseFloat(b.deliveryQuantity) > 0
+ );
+ if (selectedBatches.length === 0) {
+ uni.showToast({ title: "璇疯嚦灏戝~鍐欎竴涓壒娆$殑鍙戣揣鏁伴噺", icon: "none" });
+ return;
+ }
- const removeApprovalStep = stepIndex => {
- // 纭繚鑷冲皯淇濈暀涓�涓鎵规楠�
- if (approverNodes.value.length > 1) {
- approverNodes.value.splice(stepIndex, 1);
- } else {
- uni.showToast({
- title: "鑷冲皯闇�瑕佷竴涓鎵规楠�",
- icon: "none",
- });
+ // Check if any file is still uploading
+ if (fileList.value.some(f => f.status === "uploading")) {
+ uni.showToast({ title: "璇风瓑寰呭浘鐗囦笂浼犲畬鎴�", icon: "none" });
+ return;
+ }
+
+ const payload = {
+ salesLedgerId: goOutData.value.salesLedgerId,
+ salesLedgerProductId: goOutData.value.id,
+ type: form.type,
+ shippingCarNumber: form.type === "璐ц溅" ? form.shippingCarNumber : "",
+ expressCompany: form.type === "蹇��" ? form.expressCompany : "",
+ expressNumber: form.type === "蹇��" ? form.expressNumber : "",
+ storageBlobDTOs: fileList.value
+ .filter(f => f.status === "success")
+ .map(f => f.storageBlobDTO),
+ batchNo: selectedBatches.map(b => b.id),
+ batchNoDetailList: selectedBatches.map(b => ({
+ stockInventoryId: b.id,
+ batchNo: b.batchNo,
+ quantity: parseFloat(b.deliveryQuantity),
+ productModelId: goOutData.value.productModelId,
+ })),
+ };
+
+ try {
+ uni.showLoading({ title: "鎻愪氦涓�..." });
+ const res = await addShippingInfo(payload);
+ uni.hideLoading();
+ uni.showToast({ title: "鍙戣揣鎴愬姛" });
+ setTimeout(() => goBack(), 500);
+ } catch (e) {
+ uni.hideLoading();
+ uni.showToast({ title: "鍙戣揣澶辫触", icon: "none" });
}
};
</script>
<style scoped lang="scss">
@import "@/static/scss/form-common.scss";
-
- .approval-process {
- background: #fff;
- margin: 16px;
- border-radius: 16px;
- padding: 16px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+ .shipment-page {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 100px;
}
- .approval-header {
- margin-bottom: 16px;
+ .form-container {
+ padding: 12px 12px 0;
}
- .approval-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- display: block;
- margin-bottom: 4px;
- }
-
- .approval-desc {
- font-size: 12px;
- color: #999;
- }
-
- /* 鏍峰紡澧炲己涓衡�滅畝娲佸皬鍦嗗湀椋庢牸鈥� */
- .approval-steps {
- padding-left: 22px;
- position: relative;
-
- &::before {
- content: "";
- position: absolute;
- left: 11px;
- top: 40px;
- bottom: 40px;
- width: 2px;
- background: linear-gradient(
- to bottom,
- #e6f7ff 0%,
- #bae7ff 50%,
- #91d5ff 100%
- );
- border-radius: 1px;
- }
- }
-
- .approval-step {
- position: relative;
- margin-bottom: 24px;
-
- &::before {
- content: "";
- position: absolute;
- left: -18px;
- top: 14px; // 浠� 8px 璋冩暣涓� 14px锛屼笌鏂囧瓧涓績瀵归綈
- width: 12px;
- height: 12px;
- background: #fff;
- border: 3px solid #006cfb;
- border-radius: 50%;
- z-index: 2;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
- }
- }
-
- .step-title {
- top: 12px;
+ .form-section {
margin-bottom: 12px;
- position: relative;
- margin-left: 6px;
- }
-
- .step-title text {
- font-size: 14px;
- color: #666;
- background: #f0f0f0;
- padding: 4px 12px;
border-radius: 12px;
- position: relative;
- line-height: 1.4; // 纭繚鏂囧瓧琛岄珮涓�鑷�
+ overflow: hidden;
+ box-shadow: 0 2px 10px rgba(15, 35, 95, 0.05);
}
- .approver-item {
+ .section-header-info {
+ padding: 10px 18px;
+ background: #f8fbff;
display: flex;
- align-items: center;
- background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
- border-radius: 16px;
- padding: 16px;
+ justify-content: flex-end;
+
+ .subtitle {
+ font-size: 13px;
+ color: #7a8599;
+ }
+ }
+
+ .batch-list {
+ padding: 12px;
+ display: flex;
+ flex-direction: column;
gap: 12px;
- position: relative;
- border: 1px solid #e6f7ff;
- box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08);
- transition: all 0.3s ease;
}
- .approver-avatar {
- width: 48px;
- height: 48px;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- position: relative;
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
- }
-
- .avatar-text {
- color: #fff;
- font-size: 18px;
- font-weight: 600;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
- }
-
- .approver-info {
- flex: 1;
- position: relative;
- }
-
- .approver-name {
- display: block;
- font-size: 16px;
- color: #333;
- font-weight: 500;
- position: relative;
- }
-
- .approver-dept {
- font-size: 12px;
- color: #999;
- background: rgba(0, 108, 251, 0.05);
- padding: 2px 8px;
- border-radius: 8px;
- display: inline-block;
- position: relative;
-
- &::before {
- content: "";
- position: absolute;
- left: 4px;
- top: 50%;
- transform: translateY(-50%);
- width: 2px;
- height: 2px;
- background: #006cfb;
- border-radius: 50%;
- }
- }
-
- .delete-approver-btn {
- font-size: 16px;
- color: #ff4d4f;
- background: linear-gradient(
- 135deg,
- rgba(255, 77, 79, 0.1) 0%,
- rgba(255, 77, 79, 0.05) 100%
- );
- width: 28px;
- height: 28px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.3s ease;
- position: relative;
- }
-
- .add-approver-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
- border: 2px dashed #006cfb;
- border-radius: 16px;
- padding: 20px;
- color: #006cfb;
- font-size: 14px;
- position: relative;
- transition: all 0.3s ease;
-
- &::before {
- content: "";
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- width: 32px;
- height: 32px;
- border: 2px solid #006cfb;
- border-radius: 50%;
- opacity: 0;
- transition: all 0.3s ease;
- }
- }
-
- .delete-step-btn {
- color: #ff4d4f;
- font-size: 12px;
- background: linear-gradient(
- 135deg,
- rgba(255, 77, 79, 0.1) 0%,
- rgba(255, 77, 79, 0.05) 100%
- );
- padding: 6px 12px;
- border-radius: 12px;
- display: inline-block;
- position: relative;
- transition: all 0.3s ease;
-
- &::before {
- content: "";
- position: absolute;
- left: 6px;
- top: 50%;
- transform: translateY(-50%);
- width: 4px;
- height: 4px;
- background: #ff4d4f;
- border-radius: 50%;
- }
- }
-
- .step-line {
- display: none; // 闅愯棌鍘熸潵鐨勭嚎鏉★紝浣跨敤浼厓绱犱唬鏇�
- }
-
- .add-step-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .footer-btns {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
+ .batch-card {
background: #fff;
+ border-radius: 12px;
+ padding: 0 12px 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ border: 1px solid #f0f3f7;
+ }
+
+ .batch-header {
+ padding: 12px 0;
display: flex;
- justify-content: space-around;
+ justify-content: space-between;
align-items: center;
- padding: 0.75rem 0;
- box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05);
- z-index: 1000;
- }
- .cancel-btn {
- font-weight: 400;
- font-size: 1rem;
- color: #ffffff;
- width: 6.375rem;
- background: #c7c9cc;
- box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
- border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
- }
-
- .save-btn {
- font-weight: 400;
- font-size: 1rem;
- color: #ffffff;
- width: 14rem;
- background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
- box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
- border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
- }
-
- // 鍔ㄧ敾瀹氫箟
- @keyframes pulse {
- 0% {
- transform: scale(1);
- opacity: 1;
+ .batch-no {
+ font-size: 14px;
+ font-weight: 600;
+ color: #22324d;
}
- 50% {
- transform: scale(1.2);
- opacity: 0.7;
- }
- 100% {
- transform: scale(1);
- opacity: 1;
+
+ .batch-qty {
+ font-size: 13px;
+ color: #2979ff;
+ background: #eef6ff;
+ padding: 2px 8px;
+ border-radius: 4px;
}
}
- @keyframes rotate {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
-
- @keyframes ripple {
- 0% {
- transform: translate(-50%, -50%) scale(0.8);
- opacity: 1;
- }
- 100% {
- transform: translate(-50%, -50%) scale(1.6);
- opacity: 0;
- }
- }
-
- /* 濡傛灉宸叉湁 .step-line锛岃繖閲屾洿绮惧噯瀹氫綅鍒板乏渚т笌灏忓渾鐐瑰榻� */
- .step-line {
- position: absolute;
- left: 4px;
- top: 48px;
- width: 2px;
- height: calc(100% - 48px);
- background: #e5e7eb;
- }
-
- .approver-container {
- display: flex;
- align-items: center;
- background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
- border-radius: 16px;
- gap: 12px;
- padding: 10px 0;
- background: transparent;
- border: none;
- box-shadow: none;
- }
-
- .approver-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 8px 10px;
- background: transparent;
- border: none;
- box-shadow: none;
- border-radius: 0;
- }
-
- .approver-avatar {
- position: relative;
- width: 40px;
- height: 40px;
- border-radius: 50%;
- background: #f3f4f6;
- border: 2px solid #e5e7eb;
- display: flex;
- align-items: center;
- justify-content: center;
- animation: none; /* 绂佺敤鏃嬭浆绛夊姩鐢伙紝鍥炲綊绠�娲� */
- }
-
- .avatar-text {
- font-size: 14px;
- color: #374151;
- font-weight: 600;
- }
-
- .add-approver-btn {
- display: flex;
- align-items: center;
- gap: 8px;
- background: transparent;
- border: none;
- box-shadow: none;
- padding: 0;
- }
-
- .add-approver-btn .add-circle {
- width: 40px;
- height: 40px;
- border: 2px dashed #a0aec0;
- border-radius: 50%;
- color: #6b7280;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22px;
- line-height: 1;
- }
-
- .add-approver-btn .add-label {
- color: #3b82f6;
+ .empty-text {
+ padding: 30px 12px;
+ text-align: center;
+ color: #999;
font-size: 14px;
}
-</style>
\ No newline at end of file
+
+ .upload-container {
+ padding: 12px 18px;
+ }
+
+ :deep(.u-cell-group__title) {
+ padding: 14px 18px 10px !important;
+ font-size: 15px !important;
+ font-weight: 600 !important;
+ color: #22324d !important;
+ background: #f8fbff !important;
+ }
+
+ :deep(.u-form-item) {
+ padding: 0 18px !important;
+ }
+</style>
diff --git a/src/pages/sales/salesQuotation/detail.vue b/src/pages/sales/salesQuotation/detail.vue
index a273974..45c3fd6 100644
--- a/src/pages/sales/salesQuotation/detail.vue
+++ b/src/pages/sales/salesQuotation/detail.vue
@@ -1,7 +1,7 @@
<template>
<view class="customer-detail-page">
- <PageHeader title="鎶ヤ环璇︽儏" @back="goBack" />
-
+ <PageHeader title="鎶ヤ环璇︽儏"
+ @back="goBack" />
<view class="detail-content">
<view class="section">
<view class="section-title">鍩虹淇℃伅</view>
@@ -44,24 +44,13 @@
</view>
</view>
</view>
-
- <view class="section">
- <view class="section-title">瀹℃壒鑺傜偣</view>
- <view v-if="approverNames.length" class="info-list">
- <view v-for="(name, index) in approverNames" :key="index" class="info-item">
- <text class="info-label">瀹℃壒鑺傜偣 {{ index + 1 }}</text>
- <text class="info-value">{{ name }}</text>
- </view>
- </view>
- <view v-else class="empty-box">
- <text>鏆傛棤瀹℃壒鑺傜偣</text>
- </view>
- </view>
-
<view class="section">
<view class="section-title">浜у搧鏄庣粏</view>
- <view v-if="detailData.products && detailData.products.length > 0" class="product-list">
- <view v-for="(item, index) in detailData.products" :key="index" class="product-card">
+ <view v-if="detailData.products && detailData.products.length > 0"
+ class="product-list">
+ <view v-for="(item, index) in detailData.products"
+ :key="index"
+ class="product-card">
<view class="product-head">浜у搧 {{ index + 1 }}</view>
<view class="info-item">
<text class="info-label">浜у搧鍚嶇О</text>
@@ -89,13 +78,16 @@
</view>
</view>
</view>
- <view v-else class="empty-box">
+ <view v-else
+ class="empty-box">
<text>鏆傛棤浜у搧鏄庣粏</text>
</view>
</view>
</view>
-
- <FooterButtons cancelText="杩斿洖" confirmText="缂栬緫" @cancel="goBack" @confirm="goEdit" />
+ <FooterButtons cancelText="杩斿洖"
+ confirmText="缂栬緫"
+ @cancel="goBack"
+ @confirm="goEdit" />
</view>
</template>
@@ -108,29 +100,27 @@
const quotationId = ref("");
const detailData = ref({});
- const approverNames = computed(() => {
- const approverText = detailData.value.approveUserNames || detailData.value.approverNames || detailData.value.approveUserIds || "";
- if (Array.isArray(approverText)) return approverText.filter(Boolean);
- return String(approverText)
- .split(",")
- .map(item => item.trim())
- .filter(Boolean);
- });
-
const goBack = () => {
uni.navigateBack();
};
const goEdit = () => {
if (!quotationId.value) return;
- uni.navigateTo({ url: `/pages/sales/salesQuotation/edit?id=${quotationId.value}` });
+ uni.navigateTo({
+ url: `/pages/sales/salesQuotation/edit?id=${quotationId.value}`,
+ });
};
const formatAmount = amount => `楼${Number(amount || 0).toFixed(2)}`;
const loadDetailFromStorage = () => {
const cachedData = uni.getStorageSync("salesQuotationDetail");
- detailData.value = cachedData || {};
+ if (cachedData && (cachedData.id === quotationId.value || cachedData.id === Number(quotationId.value))) {
+ detailData.value = cachedData;
+ } else {
+ detailData.value = cachedData || {};
+ console.warn("鏈壘鍒板搴旂殑鎶ヤ环鍗曠紦瀛樻暟鎹�");
+ }
};
onLoad(options => {
diff --git a/src/pages/sales/salesQuotation/edit.vue b/src/pages/sales/salesQuotation/edit.vue
index 940a8d9..bfcb930 100644
--- a/src/pages/sales/salesQuotation/edit.vue
+++ b/src/pages/sales/salesQuotation/edit.vue
@@ -1,173 +1,196 @@
<template>
<view class="account-detail">
- <PageHeader :title="pageTitle" @back="goBack" />
-
+ <PageHeader :title="pageTitle"
+ @back="goBack" />
<view class="form-container">
- <up-form
- ref="formRef"
- :model="form"
- :rules="rules"
- label-width="110"
- input-align="right"
- error-message-align="right"
- >
- <u-cell-group title="鍩虹淇℃伅" class="form-section">
- <up-form-item label="瀹㈡埛鍚嶇О" prop="customer" required>
- <up-input v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" readonly @click="showCustomerSheet = true" />
+ <up-form ref="formRef"
+ :model="form"
+ :rules="rules"
+ label-width="110"
+ input-align="right"
+ error-message-align="right">
+ <u-cell-group title="鍩虹淇℃伅"
+ class="form-section">
+ <up-form-item label="瀹㈡埛鍚嶇О"
+ prop="customer"
+ required>
+ <up-input v-model="form.customer"
+ placeholder="璇烽�夋嫨瀹㈡埛"
+ readonly
+ @click="showCustomerSheet = true" />
<template #right>
- <up-icon name="arrow-right" @click="showCustomerSheet = true"></up-icon>
+ <up-icon name="arrow-right"
+ @click="showCustomerSheet = true"></up-icon>
</template>
</up-form-item>
- <up-form-item label="涓氬姟鍛�" prop="salesperson" required>
- <up-input
- v-model="form.salesperson"
- placeholder="璇烽�夋嫨涓氬姟鍛�"
- readonly
- @click="showSalespersonSheet = true"
- />
+ <up-form-item label="涓氬姟鍛�"
+ prop="salesperson"
+ required>
+ <up-input v-model="form.salesperson"
+ placeholder="璇烽�夋嫨涓氬姟鍛�"
+ readonly
+ @click="showSalespersonSheet = true" />
<template #right>
- <up-icon name="arrow-right" @click="showSalespersonSheet = true"></up-icon>
+ <up-icon name="arrow-right"
+ @click="showSalespersonSheet = true"></up-icon>
</template>
</up-form-item>
- <up-form-item label="鎶ヤ环鏃ユ湡" prop="quotationDate" required>
- <up-input
- v-model="form.quotationDate"
- placeholder="璇烽�夋嫨鎶ヤ环鏃ユ湡"
- readonly
- @click="showQuotationDatePicker = true"
- />
+ <up-form-item label="鎶ヤ环鏃ユ湡"
+ prop="quotationDate"
+ required>
+ <up-input v-model="form.quotationDate"
+ placeholder="璇烽�夋嫨鎶ヤ环鏃ユ湡"
+ readonly
+ @click="showQuotationDatePicker = true" />
<template #right>
- <up-icon name="arrow-right" @click="showQuotationDatePicker = true"></up-icon>
+ <up-icon name="arrow-right"
+ @click="showQuotationDatePicker = true"></up-icon>
</template>
</up-form-item>
- <up-form-item label="鏈夋晥鏈熻嚦" prop="validDate" required>
- <up-input
- v-model="form.validDate"
- placeholder="璇烽�夋嫨鏈夋晥鏈�"
- readonly
- @click="showValidDatePicker = true"
- />
+ <up-form-item label="鏈夋晥鏈熻嚦"
+ prop="validDate"
+ required>
+ <up-input v-model="form.validDate"
+ placeholder="璇烽�夋嫨鏈夋晥鏈�"
+ readonly
+ @click="showValidDatePicker = true" />
<template #right>
- <up-icon name="arrow-right" @click="showValidDatePicker = true"></up-icon>
+ <up-icon name="arrow-right"
+ @click="showValidDatePicker = true"></up-icon>
</template>
</up-form-item>
- <up-form-item label="浠樻鏂瑰紡" prop="paymentMethod" required>
- <up-input v-model="form.paymentMethod" placeholder="璇疯緭鍏ヤ粯娆炬柟寮�" clearable />
+ <up-form-item label="浠樻鏂瑰紡"
+ prop="paymentMethod"
+ required>
+ <up-input v-model="form.paymentMethod"
+ placeholder="璇疯緭鍏ヤ粯娆炬柟寮�"
+ clearable />
</up-form-item>
- <up-form-item label="澶囨敞" prop="remark">
- <up-textarea v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" auto-height />
+ <up-form-item label="澶囨敞"
+ prop="remark">
+ <up-textarea v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉�"
+ auto-height />
</up-form-item>
</u-cell-group>
-
- <u-cell-group title="瀹℃壒鑺傜偣" class="form-section">
+ <u-cell-group title="浜у搧淇℃伅"
+ class="form-section">
<view class="section-tools">
- <up-button type="primary" size="small" text="鏂板鑺傜偣" @click="addApproverNode" />
+ <up-button type="primary"
+ size="small"
+ text="鏂板浜у搧"
+ @click="addProduct" />
</view>
- <view v-if="salespersonList.length === 0" class="empty-text">
- <text>鏆傛棤鍙�夊鎵逛汉锛岃妫�鏌ョ敤鎴锋暟鎹�</text>
- </view>
- <view class="node-list">
- <view v-for="(node, index) in approverNodes" :key="node.id" class="node-card">
- <view class="node-top">
- <text class="node-title">瀹℃壒鑺傜偣 {{ index + 1 }}</text>
- <up-icon
- v-if="approverNodes.length > 1"
- name="trash"
- color="#ee0a24"
- size="18"
- @click="removeApproverNode(index)"
- ></up-icon>
- </view>
- <view class="picker-field" @click="openApproverPicker(index)">
- <up-input :model-value="node.nickName || ''" placeholder="璇烽�夋嫨瀹℃壒浜�" readonly disabled />
- <up-icon name="arrow-right" color="#909399" size="16"></up-icon>
- </view>
- </view>
- </view>
- </u-cell-group>
-
- <u-cell-group title="浜у搧淇℃伅" class="form-section">
- <view class="section-tools">
- <up-button type="primary" size="small" text="鏂板浜у搧" @click="addProduct" />
- </view>
- <view v-if="form.products.length === 0" class="empty-text">
+ <view v-if="form.products.length === 0"
+ class="empty-text">
<text>鏆傛棤浜у搧锛岃鍏堟坊鍔犱骇鍝�</text>
</view>
- <view v-else class="product-list">
- <view v-for="(product, index) in form.products" :key="product.uid" class="product-card">
+ <view v-else
+ class="product-list">
+ <view v-for="(product, index) in form.products"
+ :key="product.uid"
+ class="product-card">
<view class="product-header">
<text class="product-title">浜у搧 {{ index + 1 }}</text>
- <up-icon name="trash" color="#ee0a24" size="18" @click="removeProduct(index)"></up-icon>
+ <up-icon name="trash"
+ color="#ee0a24"
+ size="18"
+ @click="removeProduct(index)"></up-icon>
</view>
<up-divider></up-divider>
<view class="product-body">
<up-form-item label="浜у搧鍚嶇О">
- <up-input
- v-model="product.product"
- placeholder="璇烽�夋嫨浜у搧"
- readonly
- @click="openProductPicker(index)"
- />
+ <up-input v-model="product.product"
+ placeholder="璇烽�夋嫨浜у搧"
+ readonly
+ @click="openProductPicker(index)" />
<template #right>
- <up-icon name="arrow-right" @click="openProductPicker(index)"></up-icon>
+ <up-icon name="arrow-right"
+ @click="openProductPicker(index)"></up-icon>
</template>
</up-form-item>
<up-form-item label="瑙勬牸鍨嬪彿">
- <up-input
- v-model="product.specification"
- placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
- readonly
- @click="openModelPicker(index)"
- />
+ <up-input v-model="product.ProductModel"
+ placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
+ readonly
+ @click="openModelPicker(index)" />
<template #right>
- <up-icon name="arrow-right" @click="openModelPicker(index)"></up-icon>
+ <up-icon name="arrow-right"
+ @click="openModelPicker(index)"></up-icon>
</template>
</up-form-item>
<up-form-item label="鍗曚綅">
- <up-input v-model="product.unit" placeholder="璇疯緭鍏ュ崟浣�" clearable />
+ <up-input v-model="product.unit"
+ placeholder="璇疯緭鍏ュ崟浣�"
+ clearable />
</up-form-item>
<up-form-item label="鏁伴噺">
- <up-input
- v-model="product.quantity"
- type="number"
- placeholder="璇疯緭鍏ユ暟閲�"
- clearable
- @blur="calculateAmount(product)"
- />
+ <up-input v-model="product.quantity"
+ type="number"
+ placeholder="璇疯緭鍏ユ暟閲�"
+ clearable
+ @blur="calculateAmount(product)" />
</up-form-item>
<up-form-item label="鍗曚环">
- <up-input
- v-model="product.unitPrice"
- type="number"
- placeholder="璇疯緭鍏ュ崟浠�"
- clearable
- @blur="calculateAmount(product)"
- />
+ <up-input v-model="product.unitPrice"
+ type="number"
+ placeholder="璇疯緭鍏ュ崟浠�"
+ clearable
+ @blur="calculateAmount(product)" />
</up-form-item>
<up-form-item label="閲戦">
- <up-input :model-value="formatAmount(product.amount)" disabled placeholder="鑷姩璁$畻" />
+ <up-input :model-value="formatAmount(product.amount)"
+ disabled
+ placeholder="鑷姩璁$畻" />
</up-form-item>
</view>
</view>
</view>
</u-cell-group>
-
- <u-cell-group title="姹囨�讳俊鎭�" class="form-section">
+ <u-cell-group title="姹囨�讳俊鎭�"
+ class="form-section">
<up-form-item label="鎶ヤ环鎬婚">
- <up-input :model-value="formatAmount(totalAmount)" disabled placeholder="鑷姩姹囨��" />
+ <up-input :model-value="formatAmount(totalAmount)"
+ disabled
+ placeholder="鑷姩姹囨��" />
</up-form-item>
</u-cell-group>
</up-form>
</view>
-
- <FooterButtons :loading="loading" confirmText="淇濆瓨" @cancel="goBack" @confirm="handleSubmit" />
-
- <up-action-sheet :show="showCustomerSheet" title="閫夋嫨瀹㈡埛" :actions="customerActions" @select="onSelectCustomer" @close="showCustomerSheet = false" />
- <up-action-sheet :show="showSalespersonSheet" title="閫夋嫨涓氬姟鍛�" :actions="salespersonActions" @select="onSelectSalesperson" @close="showSalespersonSheet = false" />
- <up-action-sheet :show="showProductSheet" title="閫夋嫨浜у搧" :actions="productActions" @select="onSelectProduct" @close="showProductSheet = false" />
- <up-action-sheet :show="showModelSheet" title="閫夋嫨瑙勬牸鍨嬪彿" :actions="modelActions" @select="onSelectModel" @close="showModelSheet = false" />
- <up-datetime-picker :show="showQuotationDatePicker" v-model="quotationDateValue" mode="date" @confirm="onQuotationDateConfirm" @cancel="showQuotationDatePicker = false" />
- <up-datetime-picker :show="showValidDatePicker" v-model="validDateValue" mode="date" @confirm="onValidDateConfirm" @cancel="showValidDatePicker = false" />
+ <FooterButtons :loading="loading"
+ confirmText="淇濆瓨"
+ @cancel="goBack"
+ @confirm="handleSubmit" />
+ <up-action-sheet :show="showCustomerSheet"
+ title="閫夋嫨瀹㈡埛"
+ :actions="customerActions"
+ @select="onSelectCustomer"
+ @close="showCustomerSheet = false" />
+ <up-action-sheet :show="showSalespersonSheet"
+ title="閫夋嫨涓氬姟鍛�"
+ :actions="salespersonActions"
+ @select="onSelectSalesperson"
+ @close="showSalespersonSheet = false" />
+ <up-action-sheet :show="showProductSheet"
+ title="閫夋嫨浜у搧"
+ :actions="productActions"
+ @select="onSelectProduct"
+ @close="showProductSheet = false" />
+ <up-action-sheet :show="showModelSheet"
+ title="閫夋嫨瑙勬牸鍨嬪彿"
+ :actions="modelActions"
+ @select="onSelectModel"
+ @close="showModelSheet = false" />
+ <up-datetime-picker :show="showQuotationDatePicker"
+ v-model="quotationDateValue"
+ mode="date"
+ @confirm="onQuotationDateConfirm"
+ @cancel="showQuotationDatePicker = false" />
+ <up-datetime-picker :show="showValidDatePicker"
+ v-model="validDateValue"
+ mode="date"
+ @confirm="onValidDateConfirm"
+ @cancel="showValidDatePicker = false" />
</view>
</template>
@@ -179,7 +202,11 @@
import { formatDateToYMD } from "@/utils/ruoyi";
import { modelList, productTreeList } from "@/api/basicData/product";
import { userListNoPageByTenantId } from "@/api/system/user";
- import { addQuotation, getCustomerList, getQuotationDetail, updateQuotation } from "@/api/salesManagement/salesQuotation";
+ import {
+ addQuotation,
+ getCustomerList,
+ updateQuotation,
+ } from "@/api/salesManagement/salesQuotation";
const formRef = ref();
const loading = ref(false);
@@ -199,47 +226,73 @@
const modelActions = ref([]);
let uidSeed = 1;
- let nextApproverId = 2;
const form = ref({
id: undefined,
quotationNo: "",
+ customerId: undefined,
customer: "",
salesperson: "",
quotationDate: "",
validDate: "",
paymentMethod: "",
- status: "寰呭鎵�",
+ status: "鑽夌",
remark: "",
- approveUserIds: "",
products: [],
+ subtotal: 0,
+ freight: 0,
+ otherFee: 0,
+ discountRate: 0,
+ discountAmount: 0,
totalAmount: 0,
});
-
- const approverNodes = ref([{ id: 1, userId: "", nickName: "" }]);
const rules = {
customer: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
salesperson: [{ required: true, message: "璇烽�夋嫨涓氬姟鍛�", trigger: "change" }],
- quotationDate: [{ required: true, message: "璇烽�夋嫨鎶ヤ环鏃ユ湡", trigger: "change" }],
+ quotationDate: [
+ { required: true, message: "璇烽�夋嫨鎶ヤ环鏃ユ湡", trigger: "change" },
+ ],
validDate: [{ required: true, message: "璇烽�夋嫨鏈夋晥鏈�", trigger: "change" }],
- paymentMethod: [{ required: true, message: "璇疯緭鍏ヤ粯娆炬柟寮�", trigger: "blur" }],
+ paymentMethod: [
+ { required: true, message: "璇疯緭鍏ヤ粯娆炬柟寮�", trigger: "blur" },
+ ],
};
const pageTitle = computed(() => (quotationId.value ? "缂栬緫鎶ヤ环" : "鏂板鎶ヤ环"));
const totalAmount = computed(() =>
- Number((form.value.products || []).reduce((sum, item) => sum + Number(item.amount || 0), 0).toFixed(2))
+ Number(
+ (form.value.products || [])
+ .reduce((sum, item) => sum + Number(item.amount || 0), 0)
+ .toFixed(2)
+ )
);
- const customerActions = computed(() => customerList.value.map(item => ({ name: item.customerName, value: item.customerName })));
- const salespersonActions = computed(() => salespersonList.value.map(item => ({ name: item.nickName, value: item.nickName })));
- const productActions = computed(() => productList.value.map(item => ({ name: item.label, value: item.value, label: item.label })));
+ const customerActions = computed(() =>
+ customerList.value.map(item => ({
+ name: item.customerName,
+ value: item.id,
+ }))
+ );
+ const salespersonActions = computed(() =>
+ salespersonList.value.map(item => ({
+ name: item.nickName,
+ value: item.nickName,
+ }))
+ );
+ const productActions = computed(() =>
+ productList.value.map(item => ({
+ name: item.label,
+ value: item.value,
+ label: item.label,
+ }))
+ );
const createEmptyProduct = () => ({
uid: `p_${uidSeed++}`,
productId: "",
product: "",
- specificationId: "",
- specification: "",
+ productModelId: "",
+ ProductModel: "",
unit: "",
quantity: 1,
unitPrice: 0,
@@ -254,7 +307,10 @@
if (item.children && item.children.length) {
walk(item.children);
} else {
- result.push({ label: item.label || item.productName || "", value: item.id || item.value });
+ result.push({
+ label: item.label || item.productName || "",
+ value: item.id || item.value,
+ });
}
});
};
@@ -266,18 +322,12 @@
const goBack = () => uni.navigateBack();
const calculateAmount = product => {
- product.amount = Number((Number(product.quantity || 0) * Number(product.unitPrice || 0)).toFixed(2));
+ product.amount = Number(
+ (Number(product.quantity || 0) * Number(product.unitPrice || 0)).toFixed(2)
+ );
form.value.totalAmount = totalAmount.value;
};
- const addApproverNode = () => approverNodes.value.push({ id: nextApproverId++, userId: "", nickName: "" });
- const removeApproverNode = index => approverNodes.value.splice(index, 1);
- const openApproverPicker = index => {
- uni.setStorageSync("stepIndex", index);
- uni.navigateTo({
- url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect",
- });
- };
const addProduct = () => form.value.products.push(createEmptyProduct());
const removeProduct = index => {
form.value.products.splice(index, 1);
@@ -285,8 +335,14 @@
};
const fetchModelOptions = async (productId, product) => {
- const rows = await modelList({ id: productId }).catch(() => []);
- product.modelOptions = Array.isArray(rows) ? rows : [];
+ try {
+ const res = await modelList({ id: productId });
+ const rows = res?.data?.records || res?.data || res?.records || res || [];
+ product.modelOptions = Array.isArray(rows) ? rows : [];
+ } catch (error) {
+ console.error("鑾峰彇瑙勬牸鍨嬪彿澶辫触:", error);
+ product.modelOptions = [];
+ }
};
const openProductPicker = index => {
@@ -300,7 +356,11 @@
uni.showToast({ title: "璇峰厛閫夋嫨浜у搧", icon: "none" });
return;
}
- modelActions.value = (current.modelOptions || []).map(item => ({ name: item.model, value: item.id, unit: item.unit }));
+ modelActions.value = (current.modelOptions || []).map(item => ({
+ name: item.model || item.specification,
+ value: item.id,
+ unit: item.unit,
+ }));
if (!modelActions.value.length) {
uni.showToast({ title: "鏆傛棤瑙勬牸鍨嬪彿", icon: "none" });
return;
@@ -309,27 +369,21 @@
};
const onSelectCustomer = action => {
- form.value.customer = action.value;
+ form.value.customerId = action.value;
+ form.value.customer = action.name;
showCustomerSheet.value = false;
};
const onSelectSalesperson = action => {
form.value.salesperson = action.value;
showSalespersonSheet.value = false;
};
- const onSelectApprover = data => {
- const { stepIndex, contact } = data || {};
- if (stepIndex === undefined || !contact) return;
- if (!approverNodes.value[stepIndex]) return;
- approverNodes.value[stepIndex].userId = contact.userId;
- approverNodes.value[stepIndex].nickName = contact.nickName;
- };
const onSelectProduct = action => {
const current = form.value.products[currentProductIndex.value];
if (!current) return;
current.productId = action.value;
current.product = action.label;
- current.specificationId = "";
- current.specification = "";
+ current.productModelId = "";
+ current.ProductModel = "";
current.unit = "";
current.modelOptions = [];
showProductSheet.value = false;
@@ -338,8 +392,8 @@
const onSelectModel = action => {
const current = form.value.products[currentProductIndex.value];
if (!current) return;
- current.specificationId = action.value;
- current.specification = action.name;
+ current.productModelId = action.value;
+ current.ProductModel = action.name;
current.unit = action.unit || current.unit;
showModelSheet.value = false;
};
@@ -353,70 +407,114 @@
};
const fetchBaseOptions = async () => {
- const [customers, users, productTree] = await Promise.all([
- getCustomerList({ current: -1, size: -1 }).catch(() => ({})),
- userListNoPageByTenantId().catch(() => ({})),
- productTreeList().catch(() => []),
- ]);
+ const [customers, users, productTree] = await Promise.all([
+ getCustomerList({ current: -1, size: -1 }).catch(() => ({})),
+ userListNoPageByTenantId().catch(() => ({})),
+ productTreeList().catch(() => []),
+ ]);
customerList.value = customers?.data?.records || customers?.records || [];
const userRows = users?.data || [];
salespersonList.value = Array.isArray(userRows) ? userRows : [];
- productList.value = flattenProductTree(Array.isArray(productTree) ? productTree : productTree?.data || []);
+ productList.value = flattenProductTree(
+ Array.isArray(productTree) ? productTree : productTree?.data || []
+ );
+ };
+
+ // 鏍规嵁鍚嶇О鍙嶆煡鑺傜偣 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
+ const findNodeIdByLabel = (nodes, label) => {
+ if (!label) return null;
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (node.label === label) return node.value;
+ if (node.children && node.children.length > 0) {
+ const found = findNodeIdByLabel(node.children, label);
+ if (found !== null && found !== undefined) return found;
+ }
+ }
+ return null;
};
const normalizeProductRows = async rows => {
- const normalized = await Promise.all((Array.isArray(rows) ? rows : []).map(async item => {
- const row = {
- uid: `p_${uidSeed++}`,
- productId: item.productId || "",
- product: item.product || item.productName || "",
- specificationId: item.specificationId || "",
- specification: item.specification || "",
- unit: item.unit || "",
- quantity: Number(item.quantity || 1),
- unitPrice: Number(item.unitPrice || 0),
- amount: Number(item.amount || 0),
- modelOptions: [],
- };
- if (row.productId) await fetchModelOptions(row.productId, row);
- return row;
- }));
+ const normalized = await Promise.all(
+ (Array.isArray(rows) ? rows : []).map(async item => {
+ const productName = item.product || item.productName || "";
+ // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究閫夋嫨鍣ㄥ弽鏄�
+ let resolvedProductId =
+ item.productId ||
+ findNodeIdByLabel(productList.value, productName) ||
+ "";
+
+ const row = {
+ uid: `p_${uidSeed++}`,
+ productId: resolvedProductId,
+ product: productName,
+ productModelId: item.productModelId || "",
+ ProductModel: item.ProductModel || item.specification || "",
+ unit: item.unit || "",
+ quantity: Number(item.quantity || 1),
+ unitPrice: Number(item.unitPrice || 0),
+ amount: Number(item.amount || 0),
+ modelOptions: [],
+ };
+
+ if (row.productId) {
+ await fetchModelOptions(row.productId, row);
+ // 濡傛灉娌℃湁 productModelId 浣嗘湁 ProductModel 鍚嶇О锛屽皾璇曚粠 modelOptions 涓尮閰� ID
+ if (!row.productModelId && row.ProductModel) {
+ const foundModel = row.modelOptions.find(
+ m =>
+ m.model === row.ProductModel ||
+ m.specification === row.ProductModel
+ );
+ if (foundModel) {
+ row.productModelId = foundModel.id;
+ // 缁熶竴浣跨敤 modelOptions 涓殑瀛楁
+ row.ProductModel =
+ foundModel.model || foundModel.specification || row.ProductModel;
+ row.unit = foundModel.unit || row.unit;
+ }
+ }
+ }
+ return row;
+ })
+ );
form.value.products = normalized;
};
const loadDetail = async () => {
if (!quotationId.value) return;
- uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
- try {
- const res = await getQuotationDetail({ id: quotationId.value });
- const data = res?.data || {};
+
+ // 鐩存帴浠庢湰鍦板瓨鍌ㄨ幏鍙栨暟鎹紝涓嶅啀璋冪敤璇︽儏鎺ュ彛
+ const cachedData = uni.getStorageSync("salesQuotationDetail");
+ if (
+ cachedData &&
+ (cachedData.id === quotationId.value ||
+ cachedData.id === Number(quotationId.value))
+ ) {
+ const data = cachedData;
form.value = {
...form.value,
id: data.id,
quotationNo: data.quotationNo || "",
+ customerId: data.customerId,
customer: data.customer || "",
salesperson: data.salesperson || "",
quotationDate: data.quotationDate || "",
validDate: data.validDate || "",
paymentMethod: data.paymentMethod || "",
- status: data.status || "寰呭鎵�",
+ status: data.status || "鑽夌",
remark: data.remark || "",
+ subtotal: data.subtotal || 0,
+ freight: data.freight || 0,
+ otherFee: data.otherFee || 0,
+ discountRate: data.discountRate || 0,
+ discountAmount: data.discountAmount || 0,
+ totalAmount: data.totalAmount || 0,
};
await normalizeProductRows(data.products || []);
- if (data.approveUserIds) {
- const ids = String(data.approveUserIds).split(",").map(item => item.trim()).filter(Boolean);
- approverNodes.value = ids.map((userId, index) => ({
- id: index + 1,
- userId,
- nickName: salespersonList.value.find(item => String(item.userId) === String(userId))?.nickName || "",
- }));
- nextApproverId = approverNodes.value.length + 1;
- }
form.value.totalAmount = totalAmount.value;
- } catch {
- uni.showToast({ title: "鑾峰彇璇︽儏澶辫触", icon: "error" });
- } finally {
- uni.hideLoading();
+ } else {
+ console.warn("鏈壘鍒扮紦瀛樼殑鎶ヤ环鍗曡鎯呮暟鎹�");
}
};
@@ -425,16 +523,16 @@
uni.showToast({ title: "璇疯嚦灏戞坊鍔犱竴涓骇鍝�", icon: "none" });
return false;
}
- const invalid = form.value.products.some(item => !item.productId || !item.specificationId || !item.unit || !Number(item.quantity) || !Number(item.unitPrice));
+ const invalid = form.value.products.some(
+ item =>
+ !item.productId ||
+ !item.productModelId ||
+ !item.unit ||
+ !Number(item.quantity) ||
+ !Number(item.unitPrice)
+ );
if (invalid) {
uni.showToast({ title: "璇峰畬鍠勪骇鍝佷俊鎭�", icon: "none" });
- return false;
- }
- return true;
- };
- const validateApprovers = () => {
- if (approverNodes.value.some(item => !item.userId)) {
- uni.showToast({ title: "璇烽�夋嫨瀹℃壒浜�", icon: "none" });
return false;
}
return true;
@@ -442,17 +540,20 @@
const handleSubmit = async () => {
const valid = await formRef.value.validate().catch(() => false);
- if (!valid || !validateApprovers() || !validateProducts()) return;
+ if (!valid || !validateProducts()) return;
loading.value = true;
+
+ // 鍚屾鏈�鏂扮殑鎬婚
+ form.value.totalAmount = totalAmount.value;
+ form.value.subtotal = totalAmount.value;
+
const payload = {
...form.value,
- approveUserIds: approverNodes.value.map(item => item.userId).join(","),
- totalAmount: totalAmount.value,
products: form.value.products.map(item => ({
productId: item.productId,
product: item.product,
- specificationId: item.specificationId,
- specification: item.specification,
+ productModelId: item.productModelId,
+ ProductModel: item.ProductModel,
quantity: Number(item.quantity || 0),
unit: item.unit,
unitPrice: Number(item.unitPrice || 0),
@@ -486,16 +587,12 @@
onMounted(async () => {
await fetchBaseOptions();
- uni.$on("selectContact", onSelectApprover);
if (quotationId.value) {
await loadDetail();
}
});
- onUnmounted(() => {
- uni.$off("selectContact", onSelectApprover);
- uni.removeStorageSync("stepIndex");
- });
+ onUnmounted(() => {});
</script>
<style scoped lang="scss">
@@ -547,7 +644,6 @@
padding: 12px 12px 0;
}
- .node-list,
.product-list {
padding: 12px;
display: flex;
@@ -555,31 +651,12 @@
gap: 12px;
}
- .node-card {
- background: #f8fbff;
- border-radius: 12px;
- padding: 12px;
- border: 1px solid #e6eef8;
- }
-
- .picker-field {
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .picker-field :deep(.u-input) {
- flex: 1;
- }
-
- .node-top,
.product-header {
display: flex;
align-items: center;
justify-content: space-between;
}
- .node-title,
.product-title {
font-size: 14px;
font-weight: 600;
diff --git a/src/pages/sales/salesQuotation/index.vue b/src/pages/sales/salesQuotation/index.vue
index a6a5103..4ecf910 100644
--- a/src/pages/sales/salesQuotation/index.vue
+++ b/src/pages/sales/salesQuotation/index.vue
@@ -1,47 +1,50 @@
<template>
<view class="sales-account">
- <PageHeader title="閿�鍞姤浠�" @back="goBack" />
-
+ <PageHeader title="閿�鍞姤浠�"
+ @back="goBack" />
<view class="search-section">
<view class="search-bar">
<view class="search-input">
- <up-input
- class="search-text"
- v-model="quotationNo"
- placeholder="璇疯緭鍏ユ姤浠峰崟鍙锋悳绱�"
- clearable
- @change="getList"
- />
+ <up-input class="search-text"
+ v-model="quotationNo"
+ placeholder="璇疯緭鍏ユ姤浠峰崟鍙锋悳绱�"
+ clearable
+ @change="getList" />
</view>
- <view class="filter-button" @click="getList">
- <up-icon name="search" size="24" color="#999"></up-icon>
+ <view class="filter-button"
+ @click="getList">
+ <up-icon name="search"
+ size="24"
+ color="#999"></up-icon>
</view>
</view>
</view>
-
<view class="tabs-section">
- <up-tabs
- v-model="tabValue"
- :list="tabList"
- itemStyle="width: 20%;height: 80rpx;"
- @change="onTabChange"
- />
+ <up-tabs v-model="tabValue"
+ :list="tabList"
+ itemStyle="width: 20%;height: 80rpx;"
+ @change="onTabChange" />
</view>
-
- <view v-if="quotationList.length > 0" class="ledger-list">
- <view v-for="item in quotationList" :key="item.id" class="ledger-item">
+ <view v-if="quotationList.length > 0"
+ class="ledger-list">
+ <view v-for="item in quotationList"
+ :key="item.id"
+ class="ledger-item">
<view class="item-header">
<view class="item-left">
<view class="document-icon">
- <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
+ <up-icon name="file-text"
+ size="16"
+ color="#ffffff"></up-icon>
</view>
<text class="item-id">{{ item.quotationNo || "-" }}</text>
</view>
- <text class="item-index">{{ item.status || "-" }}</text>
+ <up-tag :text="item.status || '寰呭鎵�'"
+ :type="getStatusType(item.status)"
+ size="mini"
+ shape="circle" />
</view>
-
<up-divider></up-divider>
-
<view class="item-details">
<view class="detail-row">
<text class="detail-label">瀹㈡埛鍚嶇О</text>
@@ -60,43 +63,45 @@
<text class="detail-value">{{ item.validDate || "-" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">浠樻鏂瑰紡</text>
- <text class="detail-value">{{ item.paymentMethod || "-" }}</text>
- </view>
- <view class="detail-row">
<text class="detail-label">鎶ヤ环閲戦</text>
<text class="detail-value highlight">{{ formatAmount(item.totalAmount) }}</text>
</view>
- <view class="detail-row">
+ <view class="detail-row"
+ v-if="item.remark">
<text class="detail-label">澶囨敞</text>
- <text class="detail-value">{{ item.remark || "-" }}</text>
+ <text class="detail-value">{{ item.remark }}</text>
</view>
</view>
-
<view class="action-buttons">
- <up-button
- class="action-btn"
- size="small"
- type="primary"
- :disabled="!canEdit(item)"
- @click="goEdit(item)"
- >
- 缂栬緫
- </up-button>
- <up-button class="action-btn" size="small" @click="goDetail(item)">璇︽儏</up-button>
- <up-button class="action-btn" size="small" type="error" plain @click="handleDelete(item)">
+ <up-button class="action-btn"
+ size="small"
+ type="primary"
+ :disabled="!canEdit(item)"
+ @click="goEdit(item)">
+ 缂栬緫
+ </up-button>
+ <up-button class="action-btn"
+ size="small"
+ @click="goDetail(item)">璇︽儏</up-button>
+ <up-button class="action-btn"
+ size="small"
+ type="error"
+ plain
+ @click="handleDelete(item)">
鍒犻櫎
</up-button>
</view>
</view>
</view>
-
- <view v-else class="no-data">
+ <view v-else
+ class="no-data">
<text>鏆傛棤閿�鍞姤浠锋暟鎹�</text>
</view>
-
- <view class="fab-button" @click="goAdd">
- <up-icon name="plus" size="28" color="#ffffff"></up-icon>
+ <view class="fab-button"
+ @click="goAdd">
+ <up-icon name="plus"
+ size="28"
+ color="#ffffff"></up-icon>
</view>
</view>
</template>
@@ -105,7 +110,10 @@
import { reactive, ref } from "vue";
import { onShow } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
- import { deleteQuotation, getQuotationList } from "@/api/salesManagement/salesQuotation";
+ import {
+ deleteQuotation,
+ getQuotationList,
+ } from "@/api/salesManagement/salesQuotation";
const quotationNo = ref("");
const quotationList = ref([]);
@@ -129,11 +137,13 @@
};
const goAdd = () => {
+ uni.removeStorageSync("salesQuotationDetail");
uni.navigateTo({ url: "/pages/sales/salesQuotation/edit" });
};
const goEdit = item => {
if (!canEdit(item)) return;
+ uni.setStorageSync("salesQuotationDetail", item || {});
uni.navigateTo({ url: `/pages/sales/salesQuotation/edit?id=${item.id}` });
};
@@ -159,6 +169,16 @@
return `楼${num.toFixed(2)}`;
};
+ const getStatusType = status => {
+ const statusMap = {
+ 寰呭鎵�: "info",
+ 瀹℃牳涓�: "primary",
+ 閫氳繃: "success",
+ 鎷掔粷: "danger",
+ };
+ return statusMap[status] || "info";
+ };
+
const getList = () => {
uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
getQuotationList({
--
Gitblit v1.9.3