From de4ac959d99138074276563d6d4ca44d76b17705 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 20 五月 2026 16:44:25 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' into dev_天津_宝东
---
src/pages/productionDesign/processManagement/index.vue | 261
src/api/productionManagement/workOrder.js | 23
src/pages/sales/salesAccount/goOut.vue | 976 +-
src/static/images/icon/guihuandengji.svg | 1
src/pages/productionManagement/productionScheduling/index.vue | 241
src/static/images/icon/shengchanhesuan.svg | 1
src/pages/productionManagement/productionOrder/index.vue | 697 +
src/pages/fileManagement/return/index.vue | 262
src/api/inventoryManagement/stockInventory.js | 101
src/pages/productionManagement/productionTraceability/index.vue | 1032 ++
src/api/productionManagement/bom.js | 82
src/pages/fileManagement/return/edit.vue | 313
src/pages/cooperativeOffice/collaborativeApproval/approve.vue | 812 +-
src/pages/productionManagement/productionDispatching/components/DispatchModal.vue | 708 -
src/pages/cooperativeOffice/collaborativeApproval/index.vue | 28
src/pages/productionManagement/mainProductionPlan/index.vue | 300
src/api/basicData/parameterMaintenance.js | 36
src/api/productionManagement/productionReporting.js | 3
src/pages/productionDesign/processManagement/params.vue | 413 +
src/pages/inspectionUpload/index.vue | 1478 ----
src/pages/productionManagement/productionOrder/source.vue | 166
src/api/productionManagement/processManagement.js | 87
src/api/fileManagement/statistics.js | 75
src/pages/qualityManagement/processInspection/detail.vue | 5
src/pages/productionManagement/mainProductionPlan/detail.vue | 252
src/pages/equipmentManagement/repair/index.vue | 16
src/api/fileManagement/return.js | 61
src/pages/productionManagement/productionReporting/ledger.vue | 424 +
src/static/images/icon/bom.svg | 1
src/pages/qualityManagement/materialInspection/index.vue | 19
src/pages/sales/salesQuotation/edit.vue | 636 +
src/pages/productionManagement/productionDispatching/components/formDia.vue | 265
src/pages/inspectionUpload/upload.vue | 982 ++
src/pages/productionDesign/bom/index.vue | 179
src/pages/qualityManagement/materialInspection/add.vue | 30
src/static/images/icon/kucunguanli.svg | 1
src/api/productionManagement/productionCosting.js | 18
src/static/images/icon/shengchanpaichan.svg | 1
src/api/collaborativeApproval/approvalProcess.js | 89
src/api/productionManagement/processRoute.js | 37
src/pages/productionManagement/productionAccounting/index.vue | 506 +
src/pages/equipmentManagement/repair/add.vue | 57
src/pages/productionDesign/basicParameters/edit.vue | 290
src/pages/qualityManagement/materialInspection/detail.vue | 3
src/pages/sales/salesQuotation/detail.vue | 186
src/pages/qualityManagement/processInspection/add.vue | 29
src/api/fileManagement/bookshelf.js | 129
src/pages/index.vue | 448
src/pages/equipmentManagement/upkeep/fileList.vue | 133
src/pages/works.vue | 243
src/static/images/icon/jieyuedengji.svg | 1
src/pages/productionManagement/productionDispatching/index.vue | 421
src/pages/fileManagement/borrow/edit.vue | 333
src/api/basicData/storageAttachment.js | 29
src/pages/inspectionUpload/attachment.vue | 460 +
src/pages/productionDesign/basicParameters/index.vue | 245
src/api/procurementManagement/procurementLedger.js | 10
src/api/productionManagement/productionOrder.js | 78
src/api/productionManagement/productionPlan.js | 29
src/pages/productionManagement/processRoute/items.vue | 554 +
src/pages/qualityManagement/finalInspection/add.vue | 15
src/pages/procurementManagement/procurementLedger/detail.vue | 52
src/pages/productionManagement/processStatistics/index.vue | 370 +
src/pages/qualityManagement/processInspection/index.vue | 19
src/pages/equipmentManagement/upkeep/maintain.vue | 97
src/pages/qualityManagement/finalInspection/detail.vue | 3
src/static/images/icon/jichucanshu.svg | 1
src/pages/fileManagement/borrow/index.vue | 262
src/pages/qualityManagement/finalInspection/index.vue | 19
src/pages/cooperativeOffice/collaborativeApproval/detail.vue | 1113 +-
src/pages/productionDesign/bom/BomStructureItem.vue | 256
src/static/images/icon/shengchandingdan.svg | 1
src/static/images/icon/shengchanzhuisu.svg | 1
src/api/productionManagement/productProcessRoute.js | 11
src/pages/productionDesign/processManagement/edit.vue | 236
src/pages/productionManagement/productionReport/index.vue | 375
src/api/productionManagement/productionProductMain.js | 29
src/pages/productionDesign/bom/structure.vue | 100
src/pages/inventoryManagement/stockManagement/Record.vue | 292
src/pages/equipmentManagement/upkeep/index.vue | 17
src/pages.json | 177
src/static/images/icon/shengchanjihua.svg | 1
src/static/images/icon/shengchanshikuang.svg | 1
src/api/fileManagement/borrow.js | 47
src/static/images/icon/baogongtaizhang.svg | 1
src/components/CommonUpload.vue | 164
src/pages/equipmentManagement/upkeep/add.vue | 767 +-
src/pages/productionManagement/processRoute/index.vue | 287
src/utils/versionUpgrade.js | 6
src/pages/inventoryManagement/stockManagement/index.vue | 133
src/api/fileManagement/document.js | 189
src/pages/sales/salesQuotation/index.vue | 236
src/static/images/icon/gongxuguanli.svg | 1
src/pages/inspectionUpload/components/formDia.vue | 228
/dev/null | 134
src/static/images/icon/gongyiluxian.svg | 1
src/pages/productionManagement/productionOrder/pickingDetail.vue | 350
97 files changed, 15,723 insertions(+), 5,565 deletions(-)
diff --git a/src/api/basicData/parameterMaintenance.js b/src/api/basicData/parameterMaintenance.js
new file mode 100644
index 0000000..387c2e3
--- /dev/null
+++ b/src/api/basicData/parameterMaintenance.js
@@ -0,0 +1,36 @@
+import request from "@/utils/request";
+
+// 鏌ヨ鍩虹鍙傛暟鍒楄〃
+export function getBaseParamList(query) {
+ return request({
+ url: "/technologyParam/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板鍩虹鍙傛暟
+export function addBaseParam(data) {
+ return request({
+ url: "/technologyParam/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 缂栬緫鍩虹鍙傛暟
+export function editBaseParam(data) {
+ return request({
+ url: "/technologyParam/edit",
+ method: "put",
+ data: data,
+ });
+}
+
+// 鍒犻櫎鍩虹鍙傛暟
+export function removeBaseParam(id) {
+ return request({
+ url: "/technologyParam/remove/" + id,
+ method: "delete",
+ });
+}
diff --git a/src/api/basicData/storageAttachment.js b/src/api/basicData/storageAttachment.js
new file mode 100644
index 0000000..3e241f6
--- /dev/null
+++ b/src/api/basicData/storageAttachment.js
@@ -0,0 +1,29 @@
+// 闄勪欢椤甸潰鎺ュ彛
+import request from '@/utils/request'
+
+// 闄勪欢鏌ヨ
+export function attachmentList(query) {
+ return request({
+ url: '/storageAttachment/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 闄勪欢鏂板
+export function createAttachment(data) {
+ return request({
+ url: '/storageAttachment/add',
+ method: 'post',
+ data
+ })
+}
+
+// 闄勪欢鍒犻櫎
+export function deleteAttachment(data) {
+ return request({
+ url: '/storageAttachment/delete',
+ method: 'delete',
+ data
+ })
+}
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/fileManagement/bookshelf.js b/src/api/fileManagement/bookshelf.js
new file mode 100644
index 0000000..eb3a88f
--- /dev/null
+++ b/src/api/fileManagement/bookshelf.js
@@ -0,0 +1,129 @@
+import request from "@/utils/request";
+
+/**
+ * 涔︽灦绠$悊鐩稿叧API鎺ュ彛
+ * 鍖呭惈浠撳簱绠$悊銆佽揣鏋剁鐞嗐�佸浘涔︾鐞嗙瓑鍔熻兘鐨勬帴鍙�
+ */
+
+/**
+ * 鑾峰彇浠撳簱鍒楄〃
+ * @description 鑾峰彇鎵�鏈変粨搴撶殑鍩烘湰淇℃伅鍒楄〃
+ * @returns {Promise} 杩斿洖浠撳簱鍒楄〃鏁版嵁
+ */
+export function getWarehouseList() {
+ return request({
+ url: "/warehouse/tree",
+ method: "get",
+ });
+}
+
+/**
+ * 鏂板浠撳簱
+ * @description 鍒涘缓鏂扮殑浠撳簱璁板綍
+ * @param {Object} data 浠撳簱淇℃伅瀵硅薄锛屽寘鍚粨搴撳悕绉扮瓑瀛楁
+ * @returns {Promise} 杩斿洖鏂板缁撴灉
+ */
+export function addWarehouse(data) {
+ return request({
+ url: "/warehouse/add",
+ method: "post",
+ data,
+ });
+}
+
+/**
+ * 鏇存柊浠撳簱淇℃伅
+ * @description 淇敼鐜版湁浠撳簱鐨勫熀鏈俊鎭�
+ * @param {Object} data 浠撳簱淇℃伅瀵硅薄锛屽繀椤诲寘鍚粨搴揑D
+ * @returns {Promise} 杩斿洖鏇存柊缁撴灉
+ */
+export function updateWarehouse(data) {
+ return request({
+ url: "/warehouse/update",
+ method: "put",
+ data,
+ });
+}
+
+/**
+ * 鍒犻櫎浠撳簱
+ * @description 鏍规嵁浠撳簱ID鍒犻櫎鎸囧畾鐨勪粨搴撹褰�
+ * @param {string|number} id 浠撳簱ID
+ * @returns {Promise} 杩斿洖鍒犻櫎缁撴灉
+ */
+export function deleteWarehouse(data) {
+ return request({
+ url: `/warehouse/delete/`,
+ method: "delete",
+ data,
+ });
+}
+
+/**
+ * 鑾峰彇璐ф灦鍒楄〃
+ * @description 鏍规嵁浠撳簱ID鑾峰彇璇ヤ粨搴撲笅鐨勬墍鏈夎揣鏋朵俊鎭�
+ * @param {string|number} warehouseId 浠撳簱ID
+ * @returns {Promise} 杩斿洖璐ф灦鍒楄〃鏁版嵁
+ */
+export function getShelfList(warehouseId) {
+ return request({
+ url: `/shelf/list/${warehouseId}`,
+ method: "get",
+ });
+}
+
+/**
+ * 鏂板璐ф灦
+ * @description 鍦ㄦ寚瀹氫粨搴撲笅鍒涘缓鏂扮殑璐ф灦璁板綍
+ * @param {Object} data 璐ф灦淇℃伅瀵硅薄锛屽寘鍚揣鏋跺悕绉般�佸眰鏁般�佸垪鏁扮瓑瀛楁
+ * @returns {Promise} 杩斿洖鏂板缁撴灉
+ */
+export function addShelf(data) {
+ return request({
+ url: "/warehouse/goodsShelves/add",
+ method: "post",
+ data,
+ });
+}
+
+/**
+ * 鏇存柊璐ф灦淇℃伅
+ * @description 淇敼鐜版湁璐ф灦鐨勫熀鏈俊鎭�
+ * @param {Object} data 璐ф灦淇℃伅瀵硅薄锛屽繀椤诲寘鍚揣鏋禝D
+ * @returns {Promise} 杩斿洖鏇存柊缁撴灉
+ */
+export function updateShelf(data) {
+ return request({
+ url: "/warehouse/goodsShelves/update",
+ method: "put",
+ data,
+ });
+}
+
+/**
+ * 鍒犻櫎璐ф灦
+ * @description 鏍规嵁璐ф灦ID鍒犻櫎鎸囧畾鐨勮揣鏋惰褰曪紝鍚庣瑕佹眰浼犲叆 ID 鏁扮粍锛堟敮鎸佹壒閲忥級
+ * @param {Array<string|number>} data 璐ф灦ID鏁扮粍
+ * @returns {Promise} 杩斿洖鍒犻櫎缁撴灉
+ */
+export function deleteShelf(data) {
+ return request({
+ url: `/warehouse/goodsShelves/delete/`,
+ method: "delete",
+ data,
+ });
+}
+
+/**
+ * 鑾峰彇浠撳簱缁撴瀯
+ * @description 鑾峰彇鎸囧畾浠撳簱鐨勫畬鏁寸粨鏋勪俊鎭紝鍖呮嫭璐ф灦銆佸眰鏁般�佸垪鏁扮瓑
+ * @param {string|number} warehouseId 浠撳簱ID
+ * @returns {Promise} 杩斿洖浠撳簱鐨勫畬鏁寸粨鏋勬暟鎹�
+ */
+export function getWarehouseStructure(data) {
+ return request({
+ url: `/warehouse/goodsShelvesRowcol/list`,
+ method: "get",
+ params: data,
+ });
+}
diff --git a/src/api/fileManagement/borrow.js b/src/api/fileManagement/borrow.js
new file mode 100644
index 0000000..1f4c72c
--- /dev/null
+++ b/src/api/fileManagement/borrow.js
@@ -0,0 +1,47 @@
+import request from "@/utils/request";
+
+// 鏂囨。鍊熼槄绠$悊鐩稿叧鎺ュ彛
+
+// 鑾峰彇鏂囨。鍒楄〃锛堢敤浜庡�熼槄涔︾睄閫夋嫨锛�
+export function getDocumentList() {
+ return request({
+ url: "/documentation/list",
+ method: "get",
+ });
+}
+
+// 鍊熼槄鍒嗛〉鏌ヨ
+export function getBorrowList(params) {
+ return request({
+ url: "/documentationBorrowManagement/listPage",
+ method: "get",
+ params: params,
+ });
+}
+
+// 鏂板鍊熼槄
+export function addBorrow(data) {
+ return request({
+ url: "/documentationBorrowManagement/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鏇存柊鍊熼槄
+export function updateBorrow(data) {
+ return request({
+ url: "/documentationBorrowManagement/update",
+ method: "put",
+ data: data,
+ });
+}
+
+// 鍒犻櫎鍊熼槄
+export function deleteBorrow(ids) {
+ return request({
+ url: "/documentationBorrowManagement/delete",
+ method: "delete",
+ data: ids,
+ });
+}
diff --git a/src/api/fileManagement/document.js b/src/api/fileManagement/document.js
new file mode 100644
index 0000000..f3d5f4f
--- /dev/null
+++ b/src/api/fileManagement/document.js
@@ -0,0 +1,189 @@
+import request from "@/utils/request";
+
+// 鑾峰彇鍒嗙被鏍�
+export function getCategoryTree() {
+ return request({
+ url: "/warehouse/documentClassification/getList",
+ method: "get",
+ });
+}
+
+// 鏂板鍒嗙被
+export function addCategory(data) {
+ return request({
+ url: "/warehouse/documentClassification/add",
+ method: "post",
+ data: {
+ category: data.category,
+ parentId: data.parentId,
+ },
+ });
+}
+
+// 淇敼鍒嗙被
+export function updateCategory(data) {
+ return request({
+ url: "/warehouse/documentClassification/update",
+ method: "put",
+ data: {
+ id: data.id,
+ category: data.category,
+ },
+ });
+}
+
+// 鍒犻櫎鍒嗙被
+export function deleteCategory(ids) {
+ return request({
+ url: "/warehouse/documentClassification/delete",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 鑾峰彇鏂囨。鍒楄〃锛堝垎椤碉級
+export function getDocumentList(query) {
+ return request({
+ url: "/documentation/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板鏂囨。
+export function addDocument(data) {
+ return request({
+ url: "/documentation/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼鏂囨。
+export function updateDocument(data) {
+ return request({
+ url: "/documentation/update",
+ method: "put",
+ data: data,
+ });
+}
+
+// 鍒犻櫎鏂囨。
+export function deleteDocument(ids) {
+ return request({
+ url: "/documentation/delete",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 鑾峰彇鏂囨。璇︽儏
+export function getDocumentDetail(id) {
+ return request({
+ url: "/document/" + id,
+ method: "get",
+ });
+}
+
+// 鎼滅储鏂囨。
+export function searchDocument(query) {
+ return request({
+ url: "/document/search",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鑾峰彇浠撳簱缁撴瀯
+export function getWarehouseStructure() {
+ return request({
+ url: "/document/warehouse/structure",
+ method: "get",
+ });
+}
+
+// 闄勪欢绠$悊鐩稿叧鎺ュ彛
+// 娣诲姞闄勪欢
+export function addDocumentationFile(data) {
+ return request({
+ url: "/documentation/documentationFile/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鑾峰彇闄勪欢鍒楄〃
+export function getDocumentationFileList(params) {
+ return request({
+ url: "/documentation/documentationFile/listPage",
+ method: "get",
+ params: params,
+ });
+}
+
+// 鍒犻櫎闄勪欢
+export function deleteDocumentationFile(ids) {
+ return request({
+ url: "/documentation/documentationFile/del",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 鏂囨。鍊熼槄绠$悊鐩稿叧鎺ュ彛
+export function getBorrowList(params) {
+ return request({
+ url: "/documentationBorrowManagement/listPage",
+ method: "get",
+ params: params,
+ });
+}
+
+export function addBorrow(data) {
+ return request({
+ url: "/documentationBorrowManagement/add",
+ method: "post",
+ data: data,
+ });
+}
+
+export function updateBorrow(data) {
+ return request({
+ url: "/documentationBorrowManagement/update",
+ method: "put",
+ data: data,
+ });
+}
+
+export function deleteBorrow(ids) {
+ return request({
+ url: "/documentationBorrowManagement/delete",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 缁熻鐩稿叧鎺ュ彛
+// 鑾峰彇鎬讳綋缁熻鏁版嵁
+export function getDocumentationOverview() {
+ return request({
+ url: "/documentation/overview",
+ method: "get",
+ });
+}
+
+// 鑾峰彇鍒嗙被缁熻鏁版嵁
+export function getDocumentationCategoryStats() {
+ return request({
+ url: "/documentation/category",
+ method: "get",
+ });
+}
+
+// 鑾峰彇鐘舵�佺粺璁℃暟鎹�
+export function getDocumentationStatusStats() {
+ return request({
+ url: "/documentation/status",
+ method: "get",
+ });
+}
diff --git a/src/api/fileManagement/return.js b/src/api/fileManagement/return.js
new file mode 100644
index 0000000..9021ac9
--- /dev/null
+++ b/src/api/fileManagement/return.js
@@ -0,0 +1,61 @@
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ褰掕繕璁板綍
+export function getReturnListPage(query) {
+ return request({
+ url: "/documentationBorrowManagement/listPageReturn",
+ method: "get",
+ params: query,
+ });
+}
+
+// 褰掕繕鎿嶄綔
+export function returnDocument(data) {
+ return request({
+ url: "/documentationBorrowManagement/revent",
+ method: "put",
+ data: data,
+ });
+}
+
+// 鍒犻櫎褰掕繕璁板綍
+export function deleteReturn(ids) {
+ return request({
+ url: "/documentationBorrowManagement/reventDelete",
+ method: "delete",
+ data: ids,
+ });
+}
+//鏍规嵁涔︾睄id鏌ヨ鍊熼槄璁板綍
+export function getBorrowListByDocumentationId(id) {
+ return request({
+ url: "/documentationBorrowManagement/getByDocumentationId/"+id,
+ method: "get"
+ });
+}
+
+// 鏇存柊鍊熼槄璁板綍
+export function updateBorrow(data) {
+ return request({
+ url: "/documentationBorrowManagement/update",
+ method: "put",
+ data: data,
+ });
+}
+
+// 褰掕繕鏇存柊
+export function reventUpdate(data) {
+ return request({
+ url: "/documentationBorrowManagement/reventUpdate",
+ method: "put",
+ data: data,
+ });
+}
+
+// 鑾峰彇鏂囨。鍒楄〃
+export function getDocumentList() {
+ return request({
+ url: "/documentationBorrowManagement/list",
+ method: "get",
+ });
+}
diff --git a/src/api/fileManagement/statistics.js b/src/api/fileManagement/statistics.js
new file mode 100644
index 0000000..d77375c
--- /dev/null
+++ b/src/api/fileManagement/statistics.js
@@ -0,0 +1,75 @@
+import request from "@/utils/request";
+
+// 鑾峰彇妗f鎬讳綋缁熻
+export function getDocumentStatistics() {
+ return request({
+ url: "/fileManagement/statistics/overview",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鍒嗙被缁熻
+export function getCategoryStatistics() {
+ return request({
+ url: "/fileManagement/statistics/category",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鐘舵�佺粺璁�
+export function getStatusStatistics() {
+ return request({
+ url: "/fileManagement/statistics/status",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鍊熼槄缁熻
+export function getBorrowStatistics() {
+ return request({
+ url: "/fileManagement/statistics/borrow",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f骞村害缁熻
+export function getYearStatistics() {
+ return request({
+ url: "/fileManagement/statistics/year",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f浣嶇疆缁熻
+export function getLocationStatistics() {
+ return request({
+ url: "/fileManagement/statistics/location",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f瓒嬪娍缁熻
+export function getTrendStatistics(params) {
+ return request({
+ url: "/fileManagement/statistics/trend",
+ method: "get",
+ params: params,
+ });
+}
+
+// 鑾峰彇妗f鍊熼槄鎺掕
+export function getBorrowRanking() {
+ return request({
+ url: "/fileManagement/statistics/borrowRanking",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鍒嗙被璇︽儏缁熻
+export function getCategoryDetailStatistics(categoryId) {
+ return request({
+ url: `/fileManagement/statistics/categoryDetail/${categoryId}`,
+ method: "get",
+ });
+}
+
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/api/productionManagement/bom.js b/src/api/productionManagement/bom.js
new file mode 100644
index 0000000..cb42000
--- /dev/null
+++ b/src/api/productionManagement/bom.js
@@ -0,0 +1,82 @@
+import request from "@/utils/request";
+
+// BOM 鍒楄〃鍒嗛〉鏌ヨ
+export function listPage(query) {
+ return request({
+ url: "/technologyBom/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板 BOM
+export function add(data) {
+ return request({
+ url: "/technologyBom/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼 BOM
+export function update(data) {
+ return request({
+ url: "/technologyBom/update",
+ method: "put",
+ data: data,
+ });
+}
+
+// 鍒犻櫎 BOM
+export function batchDelete(ids) {
+ return request({
+ url: "/technologyBom/batchDelete",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 澶嶅埗 BOM
+export function copy(data) {
+ return request({
+ url: "/technologyBom/copy",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鑾峰彇浜у搧鍒楄〃 (鐢ㄤ簬鏂板BOM鏃堕�夋嫨浜у搧)
+export function getProductList(query) {
+ return request({
+ url: "/product/ledger/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+// --- BOM 缁撴瀯鐩稿叧 ---
+
+// 鏍规嵁 BOM ID 鑾峰彇缁撴瀯鍒楄〃
+export function queryStructureList(bomId) {
+ return request({
+ url: "/technologyBomStructure/listByBomId/" + bomId,
+ method: "get",
+ });
+}
+
+// 淇濆瓨 BOM 缁撴瀯
+export function addStructure(data) {
+ return request({
+ url: "/technologyBomStructure/batchSave",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鍒犻櫎 BOM 缁撴瀯椤�
+export function deleteStructure(id) {
+ return request({
+ url: "/technologyBomStructure/batchDelete/" + id,
+ method: "delete",
+ });
+}
diff --git a/src/api/productionManagement/processManagement.js b/src/api/productionManagement/processManagement.js
new file mode 100644
index 0000000..0f8dc06
--- /dev/null
+++ b/src/api/productionManagement/processManagement.js
@@ -0,0 +1,87 @@
+import request from "@/utils/request";
+
+export function getProcessList(query) {
+ return request({
+ url: "/technologyOperation/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+export function list() {
+ return request({
+ url: "/technologyOperation/list",
+ method: "get",
+ });
+}
+
+export function add(data) {
+ return request({
+ url: "/technologyOperation/add",
+ method: "post",
+ data: data,
+ });
+}
+
+export function update(data) {
+ return request({
+ url: "/technologyOperation/update",
+ method: "put",
+ data: data,
+ });
+}
+
+export function del(ids) {
+ return request({
+ url: "/technologyOperation/batchDelete",
+ method: "delete",
+ data: ids,
+ });
+}
+
+export function getProcessParamList(params) {
+ return request({
+ url: "/technologyOperationParam/list",
+ method: "get",
+ params,
+ });
+}
+
+export function addProcessParam(data) {
+ return request({
+ url: "/technologyOperationParam/",
+ method: "post",
+ data: data,
+ });
+}
+
+export function editProcessParam(data) {
+ return request({
+ url: "/technologyOperationParam/",
+ method: "post",
+ data: data,
+ });
+}
+
+export function deleteProcessParam(id) {
+ return request({
+ url: `/technologyOperationParam/batchDelete/${id}`,
+ method: "delete",
+ });
+}
+
+export function getDeviceLedger(query) {
+ return request({
+ url: "/device/ledger/getDeviceLedger",
+ method: "get",
+ params: query,
+ });
+}
+
+export function getBaseParamList(query) {
+ return request({
+ url: "/technologyParam/list",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/productionManagement/processRoute.js b/src/api/productionManagement/processRoute.js
new file mode 100644
index 0000000..bf49b11
--- /dev/null
+++ b/src/api/productionManagement/processRoute.js
@@ -0,0 +1,37 @@
+// 宸ヨ壓璺嚎鐩稿叧鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ宸ヨ壓璺嚎鍒楄〃
+export function listPage(query) {
+ return request({
+ url: "/technologyRouting/page",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ宸ヨ壓璺嚎椤圭洰鍒楄〃
+export function findProcessRouteItemList(query) {
+ return request({
+ url: "/technologyRoutingOperation/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鑾峰彇宸ュ簭鍙傛暟鍒楄〃
+export function getProcessParamList(query) {
+ return request({
+ url: "/technologyRoutingOperationParam/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨBOM缁撴瀯 (宸ヨ壓璺嚎)
+export function queryBomList(bomId) {
+ return request({
+ url: "/technologyBomStructure/listByBomId/" + bomId,
+ method: "get",
+ });
+}
diff --git a/src/api/productionManagement/productProcessRoute.js b/src/api/productionManagement/productProcessRoute.js
new file mode 100644
index 0000000..6584473
--- /dev/null
+++ b/src/api/productionManagement/productProcessRoute.js
@@ -0,0 +1,11 @@
+// 鐢熶骇鎶ュ伐椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鑾峰彇宸ュ簭鍙傛暟鍒楄〃-鐢熶骇璁㈠崟
+export function findProcessParamListOrder(query) {
+ return request({
+ url: `/productionOrderRoutingOperationParam/list`,
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/productionManagement/productionCosting.js b/src/api/productionManagement/productionCosting.js
index 8cc0251..ed969f6 100644
--- a/src/api/productionManagement/productionCosting.js
+++ b/src/api/productionManagement/productionCosting.js
@@ -8,4 +8,22 @@
method: "get",
params: query,
});
+}
+
+// 宸﹁竟琛ㄦ牸鐨勬帴鍙� (姹囨��)
+export function salesLedgerProductionAccountingList(query) {
+ return request({
+ url: "/productionAccount/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鍙宠竟琛ㄦ牸鐨勬帴鍙� (鏄庣粏)
+export function salesLedgerProductionAccountingListProductionDetails(query) {
+ return request({
+ url: "/productionAccount/listProductionDetails",
+ method: "get",
+ params: query,
+ });
}
\ No newline at end of file
diff --git a/src/api/productionManagement/productionOrder.js b/src/api/productionManagement/productionOrder.js
index ab3dc06..3038550 100644
--- a/src/api/productionManagement/productionOrder.js
+++ b/src/api/productionManagement/productionOrder.js
@@ -1,19 +1,79 @@
// 鐢熶骇璁㈠崟椤甸潰鎺ュ彛
import request from "@/utils/request";
-// 鍒嗛〉鏌ヨ
-export function schedulingListPage(query) {
+// 鍒嗛〉鏌ヨ鐢熶骇璁㈠崟
+export function productOrderListPage(query) {
return request({
- url: "/salesLedger/scheduling/listPage",
+ url: "/productionOrder/page",
method: "get",
params: query,
});
}
-// 鐢熶骇娲惧伐
-export function productionDispatch(query) {
+
+// 鐢熶骇璁㈠崟婧簮璇︽儏
+export function getOrderDetail(npsNo) {
return request({
- url: "/salesLedger/scheduling/productionDispatch",
- method: "post",
- data: query,
+ url: "/productionOrder/ordeDetail",
+ method: "get",
+ params: { npsNo },
});
-}
\ No newline at end of file
+}
+
+// 鑾峰彇鐢熶骇璁㈠崟鏉ユ簮鏁版嵁
+export function getProductOrderSource(id) {
+ return request({
+ url: `/productionOrder/source/${id}`,
+ method: "get",
+ });
+}
+
+// 棰嗘枡璇︽儏鍒楄〃
+export function listMaterialPickingDetail(productionOrderId) {
+ return request({
+ url: "/productionOrderPick/detail/" + productionOrderId,
+ method: "get",
+ });
+}
+
+// 琛ユ枡璁板綍鍒楄〃
+export function listMaterialSupplementRecord(query) {
+ return request({
+ url: "/productionOrderPickRecord/feeding",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鑾峰彇棰嗘枡BOM淇℃伅 (鍙�夛紝澶囩敤)
+export function listMaterialPickingBom(productionOrderId) {
+ return request({
+ url: "/productionOrder/pick/" + productionOrderId,
+ method: "get",
+ });
+}
+
+// 鑾峰彇鐢熶骇璁㈠崟鍏宠仈鐨勫伐鑹鸿矾绾夸富淇℃伅
+export function getOrderProcessRouteMain(orderId) {
+ return request({
+ url: "/productionOrderRouting/listMain",
+ method: "get",
+ params: { orderId },
+ });
+}
+
+// 鏌ヨBOM缁撴瀯 (鐢熶骇璁㈠崟)
+export function queryOrderBomList(bomId) {
+ return request({
+ url: "/productionBomStructure/listByBomId/" + bomId,
+ method: "get",
+ });
+}
+
+// 鑾峰彇宸ュ簭鍙傛暟鍒楄〃 (鐢熶骇璁㈠崟)
+export function findProcessParamListOrder(query) {
+ return request({
+ url: "/productionOrderRoutingOperationParam/list",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/productionManagement/productionPlan.js b/src/api/productionManagement/productionPlan.js
new file mode 100644
index 0000000..ec9a1ae
--- /dev/null
+++ b/src/api/productionManagement/productionPlan.js
@@ -0,0 +1,29 @@
+// 涓荤敓浜ц鍒掓帴鍙�
+import request from "@/utils/request";
+
+// 鍒嗛〉鍒楄〃
+export function productionPlanListPage(query) {
+ return request({
+ url: "/productionPlan/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鎷夊彇鏁版嵁
+export function loadProdData(query) {
+ return request({
+ url: "/productionPlan/loadProdData",
+ method: "get",
+ params: query,
+ });
+}
+
+// 姹囨�荤粺璁�
+export function summaryByProductType(query) {
+ return request({
+ url: "/productionPlan/summaryByProductType",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/productionManagement/productionProductMain.js b/src/api/productionManagement/productionProductMain.js
new file mode 100644
index 0000000..4242e1b
--- /dev/null
+++ b/src/api/productionManagement/productionProductMain.js
@@ -0,0 +1,29 @@
+// 鐢熶骇鎶ュ伐椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ鐢熶骇鎶ュ伐涓昏〃
+export function productionProductMainListPage(query) {
+ return request({
+ url: "/productionProductMain/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鍒犻櫎鎶ュ伐
+export function productionReportDelete(query) {
+ return request({
+ url: "/productionProductMain/delete",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ鎶曞叆鍒楄〃
+export function productionProductInputListPage(query) {
+ return request({
+ url: "/productionProductInput/listPage",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/productionManagement/productionReporting.js b/src/api/productionManagement/productionReporting.js
index 33faadb..be352d5 100644
--- a/src/api/productionManagement/productionReporting.js
+++ b/src/api/productionManagement/productionReporting.js
@@ -20,9 +20,8 @@
// 鏍规嵁ID鑾峰彇宸ュ崟璇︽儏
export function getProductWorkOrderById(query) {
return request({
- url: "/productWorkOrder/getProductWorkOrderById",
+ url: "/productionOperationTask/" + query.id,
method: "get",
- params: query,
});
}
// 鐢熶骇鎶ュ伐
diff --git a/src/api/productionManagement/workOrder.js b/src/api/productionManagement/workOrder.js
index 7e8bd86..d3e0033 100644
--- a/src/api/productionManagement/workOrder.js
+++ b/src/api/productionManagement/workOrder.js
@@ -2,7 +2,7 @@
export function productWorkOrderPage(query) {
return request({
- url: "/productWorkOrder/page",
+ url: "/productionOperationTask/page",
method: "get",
params: query,
});
@@ -10,7 +10,7 @@
export function updateProductWorkOrder(data) {
return request({
- url: "/productWorkOrder/updateProductWorkOrder",
+ url: "/productionOperationTask/updateProductWorkOrder",
method: "post",
data: data,
});
@@ -24,12 +24,29 @@
});
}
+export function assignProductWorkOrder(data) {
+ return request({
+ url: "/productionOperationTask/assign",
+ method: "post",
+ data: data,
+ });
+}
+
// 涓嬭浇宸ュ崟娴佽浆鍗★紙杩斿洖鏂囦欢娴侊級
export function downProductWorkOrder(id) {
return request({
- url: "/productWorkOrder/down",
+ url: "/productionOperationTask/down",
method: "post",
data: { id },
responseType: "blob",
});
}
+
+// 鑾峰彇宸ュ簭缁熻鏁版嵁
+export function getOperationStatistics(query) {
+ return request({
+ url: "/productionOperationTask/getOperation",
+ method: "get",
+ params: query,
+ });
+}
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.json b/src/pages.json
index e52db8a..b3d204b 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -367,6 +367,55 @@
}
},
{
+ "path": "pages/productionDesign/basicParameters/index",
+ "style": {
+ "navigationBarTitleText": "鍩虹鍙傛暟",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionDesign/basicParameters/edit",
+ "style": {
+ "navigationBarTitleText": "鍙傛暟璇︽儏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionDesign/processManagement/index",
+ "style": {
+ "navigationBarTitleText": "宸ュ簭绠$悊",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionDesign/processManagement/edit",
+ "style": {
+ "navigationBarTitleText": "宸ュ簭璇︽儏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionDesign/processManagement/params",
+ "style": {
+ "navigationBarTitleText": "宸ュ簭鍙傛暟閰嶇疆",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionDesign/bom/index",
+ "style": {
+ "navigationBarTitleText": "BOM绠$悊",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionDesign/bom/structure",
+ "style": {
+ "navigationBarTitleText": "BOM缁撴瀯",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/cooperativeOffice/collaborativeApproval/index1",
"style": {
"navigationBarTitleText": "鍏嚭绠$悊",
@@ -712,6 +761,20 @@
}
},
{
+ "path": "pages/inspectionUpload/upload",
+ "style": {
+ "navigationBarTitleText": "涓婁紶宸℃璁板綍",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/inspectionUpload/attachment",
+ "style": {
+ "navigationBarTitleText": "鏌ョ湅闄勪欢",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/equipmentManagement/faultAnalysis/index",
"style": {
"navigationBarTitleText": "鏁呴殰鍒嗘瀽杩芥函",
@@ -722,6 +785,34 @@
"path": "pages/productionManagement/productionOrder/index",
"style": {
"navigationBarTitleText": "鐢熶骇璁㈠崟",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/productionOrder/source",
+ "style": {
+ "navigationBarTitleText": "鏉ユ簮鏁版嵁",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/productionOrder/pickingDetail",
+ "style": {
+ "navigationBarTitleText": "棰嗘枡璇︽儏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/processRoute/index",
+ "style": {
+ "navigationBarTitleText": "宸ヨ壓璺嚎",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/processRoute/items",
+ "style": {
+ "navigationBarTitleText": "璺嚎椤圭洰",
"navigationStyle": "custom"
}
},
@@ -747,19 +838,61 @@
}
},
{
+ "path": "pages/productionManagement/productionReporting/ledger",
+ "style": {
+ "navigationBarTitleText": "鎶ュ伐鍙拌处",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/productionManagement/workOrder/index",
"style": {
"navigationBarTitleText": "鐢熶骇宸ュ崟",
"navigationStyle": "custom"
}
},
- // {
- // "path": "pages/productionManagement/productionCosting/index",
- // "style": {
- // "navigationBarTitleText": "鐢熶骇鏍哥畻",
- // "navigationStyle": "custom"
- // }
- // },
+ {
+ "path": "pages/productionManagement/mainProductionPlan/index",
+ "style": {
+ "navigationBarTitleText": "涓荤敓浜ц鍒�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/mainProductionPlan/detail",
+ "style": {
+ "navigationBarTitleText": "鐢熶骇璁″垝璇︽儏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/productionScheduling/index",
+ "style": {
+ "navigationBarTitleText": "鐢熶骇鎺掍骇",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/productionAccounting/index",
+ "style": {
+ "navigationBarTitleText": "鐢熶骇鏍哥畻",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/productionTraceability/index",
+ "style": {
+ "navigationBarTitleText": "鐢熶骇杩芥函",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/productionManagement/processStatistics/index",
+ "style": {
+ "navigationBarTitleText": "宸ュ簭鐢熶骇瀹炲喌",
+ "navigationStyle": "custom"
+ }
+ },
{
"path": "pages/inventoryManagement/receiptManagement/index",
"style": {
@@ -1143,6 +1276,34 @@
"style": {
"navigationBarTitleText": "娑堟伅涓績"
}
+ },
+ {
+ "path": "pages/fileManagement/borrow/index",
+ "style": {
+ "navigationBarTitleText": "鍊熼槄绠$悊",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/fileManagement/borrow/edit",
+ "style": {
+ "navigationBarTitleText": "鍊熼槄鐧昏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/fileManagement/return/index",
+ "style": {
+ "navigationBarTitleText": "褰掕繕绠$悊",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/fileManagement/return/edit",
+ "style": {
+ "navigationBarTitleText": "褰掕繕鐧昏",
+ "navigationStyle": "custom"
+ }
}
],
"subPackages": [
@@ -1379,4 +1540,4 @@
"navigationBarTitleText": "RuoYi",
"navigationBarBackgroundColor": "#FFFFFF"
}
-}
+}
\ No newline at end of file
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 bc69a5f..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"
@@ -123,13 +130,13 @@
<text>鏆傛棤瀹℃壒鏁版嵁</text>
</view>
<!-- 娴姩鎿嶄綔鎸夐挳 -->
- <!-- <view class="fab-button"
+ <view class="fab-button"
v-if="props.approveType != 5 && props.approveType != 6 && props.approveType != 7"
@click="handleAdd">
<up-icon name="plus"
size="24"
color="#ffffff"></up-icon>
- </view> -->
+ </view>
</view>
</template>
@@ -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 71de940..73c4eba 100644
--- a/src/pages/equipmentManagement/repair/add.vue
+++ b/src/pages/equipmentManagement/repair/add.vue
@@ -69,6 +69,20 @@
placeholder="璇疯緭鍏ユ姤淇汉"
clearable />
</u-form-item>
+ <u-form-item label="缁翠慨浜�"
+ prop="maintenanceName"
+ border-bottom>
+ <u-input v-model="form.maintenanceName"
+ placeholder="璇疯緭鍏ョ淮淇汉"
+ clearable />
+ </u-form-item>
+ <u-form-item label="缁翠慨椤圭洰"
+ prop="machineryCategory"
+ border-bottom>
+ <u-input v-model="form.machineryCategory"
+ placeholder="璇疯緭鍏ョ淮淇」鐩�"
+ clearable />
+ </u-form-item>
<u-form-item label="鏁呴殰鐜拌薄"
prop="remark"
required
@@ -79,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>
<!-- 鎻愪氦鎸夐挳 -->
@@ -108,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,
@@ -132,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([]);
@@ -169,7 +197,10 @@
deviceModel: undefined, // 瑙勬牸鍨嬪彿
repairTime: dayjs().format("YYYY-MM-DD"), // 鎶ヤ慨鏃ユ湡
repairName: undefined, // 鎶ヤ慨浜�
+ maintenanceName: undefined, // 缁翠慨浜�
+ machineryCategory: undefined, // 缁翠慨椤圭洰
remark: undefined, // 鏁呴殰鐜拌薄
+ storageBlobDTOs: [], // 鍥剧墖闄勪欢
});
// 鎶ヤ慨鐘舵�侀�夐」
@@ -221,7 +252,10 @@
form.value.deviceModel = data.deviceModel;
form.value.repairTime = dayjs(data.repairTime).format("YYYY-MM-DD");
form.value.repairName = data.repairName;
+ form.value.maintenanceName = data.maintenanceName;
+ 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 || "";
@@ -328,14 +362,12 @@
};
onShow(() => {
- // 椤甸潰鏄剧ず鏃惰幏鍙栧弬鏁�
- getPageParams();
+ // 椤甸潰鏄剧ず鏃堕�昏緫
});
onMounted(() => {
- // 椤甸潰鍔犺浇鏃惰幏鍙栬澶囧垪琛ㄥ拰鍙傛暟
+ // 椤甸潰鍔犺浇鏃惰幏鍙栬澶囧垪琛�
loadDeviceName();
- getPageParams();
});
// 缁勪欢鍗歌浇鏃舵竻鐞嗗畾鏃跺櫒
@@ -375,7 +407,6 @@
// 鍑嗗鎻愪氦鏁版嵁
const submitData = { ...form.value };
-
const { code } = id
? await editRepair({ id: id, ...submitData })
: await addRepair(submitData);
@@ -396,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();
@@ -419,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 e280595..8c41ab1 100644
--- a/src/pages/equipmentManagement/repair/index.vue
+++ b/src/pages/equipmentManagement/repair/index.vue
@@ -58,12 +58,16 @@
<text class="detail-value">{{ item.repairName || '-' }}</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.maintenanceName || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">缁翠慨椤圭洰</text>
+ <text class="detail-value">{{ item.machineryCategory || '-' }}</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>
@@ -208,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 8dedb1b..5f3bd40 100644
--- a/src/pages/equipmentManagement/upkeep/add.vue
+++ b/src/pages/equipmentManagement/upkeep/add.vue
@@ -1,393 +1,444 @@
<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>
-
- <!-- 鎻愪氦鎸夐挳 -->
- <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>
+ <u-form-item label="闄勪欢鍥剧墖"
+ prop="storageBlobDTOs"
+ border-bottom>
+ <CommonUpload v-model="form.storageBlobDTOs" />
+ </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 CommonUpload from "@/components/CommonUpload.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"), // 璁″垝淇濆吇鏃ユ湡
-});
+ // 浣跨敤 ref 澹版槑琛ㄥ崟鏁版嵁
+ const form = ref({
+ deviceLedgerId: undefined, // 璁惧ID
+ deviceModel: undefined, // 瑙勬牸鍨嬪彿
+ maintenancePlanTime: dayjs().format("YYYY-MM-DD"), // 璁″垝淇濆吇鏃ユ湡
+ maintenancePerson: undefined, // 淇濆吇浜�
+ machineryCategory: undefined, // 淇濆吇椤圭洰
+ storageBlobDTOs: [], // 闄勪欢鍥剧墖
+ });
-// 鍔犺浇璁惧鍒楄〃
-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");
- // 璁剧疆璁惧鍚嶇О鏄剧ず
- 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;
+ form.value.storageBlobDTOs = data.storageBlobVOs || [];
+ // 璁剧疆璁惧鍚嶇О鏄剧ず
+ 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, status: 0 };
+
+ // 纭繚鏃ユ湡鏍煎紡姝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/fileList.vue b/src/pages/equipmentManagement/upkeep/fileList.vue
index b4d4b7f..1680fcb 100644
--- a/src/pages/equipmentManagement/upkeep/fileList.vue
+++ b/src/pages/equipmentManagement/upkeep/fileList.vue
@@ -8,7 +8,7 @@
<view v-if="fileList.length > 0"
class="file-list">
<view v-for="(file, index) in fileList"
- :key="file.id || index"
+ :key="file.storageAttachmentId || file.id || index"
class="file-item">
<!-- 鏂囦欢鍥炬爣 -->
<!-- <view class="file-icon"
@@ -19,7 +19,7 @@
</view> -->
<!-- 鏂囦欢淇℃伅 -->
<view class="file-info">
- <text class="file-name">{{ file.name }}</text>
+ <text class="file-name">{{ file.originalFilename || file.name }}</text>
<!-- <text class="file-meta">{{ formatFileSize(file.fileSize) }} 路 {{ file.uploadTime || file.createTime }}</text> -->
</view>
<!-- 鎿嶄綔鎸夐挳 -->
@@ -65,15 +65,16 @@
<script setup>
import { ref, onMounted } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
import config from "@/config";
import { getToken } from "@/utils/auth";
// import { saveAs } from "file-saver";
import {
- listMaintenanceTaskFiles,
- addMaintenanceTaskFile,
- delMaintenanceTaskFile,
- } from "@/api/equipmentManagement/upkeep";
+ attachmentList,
+ createAttachment,
+ deleteAttachment,
+ } from "@/api/basicData/storageAttachment";
import { blobValidate } from "@/utils/ruoyi";
// 闄勪欢鍒楄〃
@@ -214,21 +215,27 @@
// const fileType = fileName.split(".").pop();
// 3. 鏋勯�犱繚瀛樻枃浠朵俊鎭殑鍙傛暟
const saveData = {
- name: fileName,
- deviceMaintenanceId: upkeepId.value,
- url: res.data.tempPath || "",
+ application: "file",
+ recordType: recordType.value,
+ recordId: upkeepId.value,
+ storageBlobDTOs: [
+ {
+ name: fileName,
+ url:
+ res.data.url ||
+ res.data.previewURL ||
+ res.data.tempPath ||
+ "",
+ ...res.data,
+ },
+ ],
};
console.log(saveData, "淇濆瓨鏂囦欢淇℃伅鍙傛暟");
- // 4. 璋冪敤 addRuleFile 鎺ュ彛淇濆瓨鏂囦欢淇℃伅
- addMaintenanceTaskFile(saveData)
+ // 4. 璋冪敤 createAttachment 鎺ュ彛淇濆瓨鏂囦欢淇℃伅
+ createAttachment(saveData)
.then(addRes => {
if (addRes.code === 200) {
- // 5. 娣诲姞鍒版枃浠跺垪琛�
- const newFile = {
- ...addRes.data,
- uploadTime: new Date().toLocaleString(),
- };
- // fileList.value.push(newFile);
+ // 5. 鍒锋柊鍒楄〃
getFileList();
showToast("涓婁紶鎴愬姛");
} else {
@@ -257,20 +264,32 @@
};
// 涓嬭浇鏂囦欢
const downloadFile = file => {
- var url =
- config.baseUrl +
- "/common/download?fileName=" +
- encodeURIComponent(file.url) +
- "&delete=true";
- console.log(url, "url");
+ let url = file.downloadURL || file.previewURL || file.url;
+ if (!url) {
+ showToast("鏂囦欢鍦板潃鏃犳晥");
+ return;
+ }
+
+ // 濡傛灉涓嶆槸瀹屾暣鐨刄RL锛屽垯鎷兼帴
+ if (!url.startsWith("http")) {
+ url =
+ config.baseUrl +
+ "/common/download?fileName=" +
+ encodeURIComponent(url) +
+ "&delete=true";
+ }
+
+ console.log(url, "涓嬭浇鍦板潃");
+
+ uni.showLoading({ title: "姝e湪涓嬭浇...", mask: true });
uni
.downloadFile({
url: url,
- responseType: "blob",
header: { Authorization: "Bearer " + getToken() },
})
.then(res => {
+ uni.hideLoading();
let osType = uni.getStorageSync("deviceInfo").osName;
let filePath = res.tempFilePath;
if (osType === "ios") {
@@ -280,7 +299,6 @@
success: res => {},
fail: err => {
console.log("uni.openDocument--fail");
- reject(err);
},
});
} else {
@@ -290,10 +308,8 @@
uni.showToast({
icon: "none",
mask: true,
- title:
- "鏂囦欢宸蹭繚瀛橈細Android/data/uni.UNI720216F/apps/__UNI__720216F/" +
- fileRes.savedFilePath, //淇濆瓨璺緞
- duration: 3000,
+ title: "鏂囦欢宸蹭笅杞藉苟灏濊瘯鎵撳紑",
+ duration: 2000,
});
setTimeout(() => {
//鎵撳紑鏂囨。鏌ョ湅
@@ -305,24 +321,12 @@
},
fail: err => {
console.log("uni.save--fail");
- reject(err);
},
});
}
- // const isBlob = blobValidate(res.data);
- // if (isBlob) {
- // const blob = new Blob([res.data], { type: "text/plain" });
- // const url = URL.createObjectURL(blob);
- // const downloadLink = document.getElementById("downloadLink");
- // downloadLink.href = url;
- // downloadLink.download = file.name;
- // downloadLink.click();
- // showToast("涓嬭浇鎴愬姛");
- // } else {
- // showToast("涓嬭浇澶辫触");
- // }
})
.catch(err => {
+ uni.hideLoading();
console.error("涓嬭浇澶辫触:", err);
showToast("涓嬭浇澶辫触");
});
@@ -335,7 +339,7 @@
content: `纭畾瑕佸垹闄ら檮浠� "${file.name}" 鍚楋紵`,
success: res => {
if (res.confirm) {
- deleteFile(file.id, index);
+ deleteFile(file.storageAttachmentId || file.id, index);
}
},
});
@@ -348,7 +352,7 @@
mask: true,
});
- delMaintenanceTaskFile([fileId])
+ deleteAttachment([fileId])
.then(res => {
uni.hideLoading();
if (res.code === 200) {
@@ -372,37 +376,48 @@
icon: "none",
});
};
- const rulesRegulationsManagementId = ref("");
const upkeepId = ref("");
+ const recordType = ref("");
+
+ // 椤甸潰鍔犺浇鏃惰幏鍙栧弬鏁�
+ onLoad(options => {
+ if (options.recordId) {
+ upkeepId.value = options.recordId;
+ } else {
+ upkeepId.value = uni.getStorageSync("upkeepId");
+ }
+
+ if (options.recordType) {
+ recordType.value = options.recordType;
+ } else {
+ recordType.value = "device_maintenance"; // 榛樿鍏煎
+ }
+
+ getFileList();
+ });
+
// 椤甸潰鍔犺浇鏃�
onMounted(() => {
- // 浠� API 鑾峰彇闄勪欢鍒楄〃
-
- // 浠庢湰鍦板瓨鍌ㄨ幏鍙� rulesRegulationsManagementId
- rulesRegulationsManagementId.value = uni.getStorageSync(
- "rulesRegulationsManagement"
- );
- upkeepId.value = uni.getStorageSync("upkeepId");
- getFileList();
+ // getFileList(); // onLoad 涓凡缁忚皟鐢ㄤ簡
});
// 鑾峰彇闄勪欢鍒楄〃
const getFileList = () => {
+ if (!upkeepId.value) return;
+
uni.showLoading({
title: "鍔犺浇涓�...",
mask: true,
});
- listMaintenanceTaskFiles({
- current: 1,
- size: 100,
- deviceMaintenanceId: upkeepId.value,
- rulesRegulationsManagementId: upkeepId.value,
+ attachmentList({
+ recordType: recordType.value,
+ recordId: upkeepId.value,
})
.then(res => {
uni.hideLoading();
if (res.code === 200) {
- fileList.value = res.data.records || [];
+ fileList.value = res.data || [];
} else {
showToast("鑾峰彇闄勪欢鍒楄〃澶辫触");
}
diff --git a/src/pages/equipmentManagement/upkeep/index.vue b/src/pages/equipmentManagement/upkeep/index.vue
index cca1b04..671bc65 100644
--- a/src/pages/equipmentManagement/upkeep/index.vue
+++ b/src/pages/equipmentManagement/upkeep/index.vue
@@ -63,6 +63,14 @@
<text class="detail-value">{{ formatDateTime(item.createTime) || '-' }}</text>
</view>
<view class="detail-row">
+ <text class="detail-label">淇濆吇浜�</text>
+ <text class="detail-value">{{ item.maintenancePerson || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">淇濆吇椤圭洰</text>
+ <text class="detail-value">{{ item.machineryCategory || '-' }}</text>
+ </view>
+ <view class="detail-row">
<text class="detail-label">瀹為檯淇濆吇浜�</text>
<text class="detail-value">{{ item.maintenanceActuallyName || '-' }}</text>
</view>
@@ -72,7 +80,8 @@
</view>
<view class="detail-row">
<text class="detail-label">淇濆吇缁撴灉</text>
- <view class="detail-value">
+ <text class="detail-value">{{ item.maintenanceResult || '-' }}</text>
+ <!-- <view class="detail-value">
<u-tag v-if="item.maintenanceResult === 1"
type="success"
size="mini">
@@ -84,7 +93,7 @@
缁翠慨
</u-tag>
<text v-if="item.maintenanceResult === undefined || item.maintenanceResult === null">-</text>
- </view>
+ </view> -->
</view>
</view>
<!-- 鎸夐挳鍖哄煙 -->
@@ -198,10 +207,8 @@
};
// 鏂板闄勪欢 - 璺宠浆鍒伴檮浠堕〉闈�
const addFile = id => {
- // 浣跨敤鏈湴瀛樺偍浼犻�抜d
- uni.setStorageSync("upkeepId", id);
uni.navigateTo({
- url: "/pages/equipmentManagement/upkeep/fileList",
+ url: `/pages/equipmentManagement/upkeep/fileList?recordId=${id}&recordType=device_maintenance`,
});
};
diff --git a/src/pages/equipmentManagement/upkeep/maintain.vue b/src/pages/equipmentManagement/upkeep/maintain.vue
index f86006c..3abfc58 100644
--- a/src/pages/equipmentManagement/upkeep/maintain.vue
+++ b/src/pages/equipmentManagement/upkeep/maintain.vue
@@ -100,81 +100,9 @@
<!-- 涓婁紶闄勪欢 -->
<u-form-item v-if="form.status == '1'"
label="淇濆吇闄勪欢"
+ prop="storageBlobDTOs"
border-bottom>
- <view class="simple-upload-area">
- <view class="upload-buttons">
- <u-button type="primary"
- @click="chooseMedia('image')"
- :loading="uploading"
- :disabled="uploadFiles.length >= uploadConfig.limit"
- :customStyle="{ marginRight: '10px', flex: 1 }">
- <u-icon name="camera"
- size="18"
- color="#fff"
- style="margin-right: 5px;"></u-icon>
- {{ uploading ? '涓婁紶涓�...' : '鎷嶇収' }}
- </u-button>
- <!-- <u-button type="success"
- @click="chooseMedia('video')"
- :loading="uploading"
- :disabled="uploadFiles.length >= uploadConfig.limit"
- :customStyle="{ flex: 1 }">
- <uni-icons type="videocam"
- name="videocam"
- size="18"
- color="#fff"
- style="margin-right: 5px;"></uni-icons>
- {{ uploading ? '涓婁紶涓�...' : '鎷嶈棰�' }}
- </u-button> -->
- </view>
- <!-- 涓婁紶杩涘害 -->
- <view v-if="uploading"
- class="upload-progress">
- <u-line-progress :percentage="uploadProgress"
- :showText="true"
- activeColor="#409eff"></u-line-progress>
- </view>
- <!-- 涓婁紶鐨勬枃浠跺垪琛� -->
- <view v-if="uploadFiles.length > 0"
- class="file-list">
- <view v-for="(file, index) in uploadFiles"
- :key="index"
- class="file-item">
- <view class="file-preview-container">
- <!-- {{formatFileUrl(file.url)}} -->
- <image v-if="file.type === 'image' || isImageFile(file)"
- :src="formatFileUrl(file.url || file.tempFilePath || file.path || file.downloadUrl)"
- class="file-preview"
- mode="aspectFill" />
- <view v-else-if="file.type === 'video'"
- class="video-preview">
- <uni-icons type="videocam"
- name="videocam"
- size="18"
- color="#fff"
- style="margin-right: 5px;"></uni-icons>
- <text class="video-text">瑙嗛</text>
- </view>
- <!-- 鍒犻櫎鎸夐挳 -->
- <view class="delete-btn"
- @click="removeFile(index)">
- <u-icon name="close"
- size="12"
- color="#fff"></u-icon>
- </view>
- </view>
- <view class="file-info">
- <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? '鍥剧墖' : '瑙嗛')
- }}</text>
- <text class="file-size">{{ formatFileSize(file.size) }}</text>
- </view>
- </view>
- </view>
- <view v-if="uploadFiles.length === 0"
- class="empty-state">
- <text>璇烽�夋嫨瑕佷笂浼犵殑淇濆吇鍥剧墖</text>
- </view>
- </view>
+ <CommonUpload v-model="form.storageBlobDTOs" />
</u-form-item>
<!-- 鎻愪氦鎸夐挳 -->
<view class="footer-btns">
@@ -235,6 +163,7 @@
import { ref, onMounted, reactive } from "vue";
import { onShow } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
+ import CommonUpload from "@/components/CommonUpload.vue";
import { addMaintenance } from "@/api/equipmentManagement/upkeep";
import { getSparePartsList } from "@/api/equipmentManagement/repair";
import useUserStore from "@/store/modules/user";
@@ -275,7 +204,6 @@
const sparePartsQtyRaw = ref("");
// 鏂囦欢涓婁紶鐩稿叧
- const uploadFiles = ref([]);
const uploading = ref(false);
const uploadProgress = ref(0);
const number = ref(0);
@@ -316,6 +244,7 @@
maintenanceResult: undefined, // 淇濆吇缁撴灉
maintenanceActuallyTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 瀹為檯淇濆吇鏃ユ湡锛堝彧鏄剧ず鏃ユ湡锛�
sparePartsIds: undefined, // 璁惧澶囦欢ID
+ storageBlobDTOs: [], // 淇濆吇闄勪欢
});
// 娓呴櫎琛ㄥ崟鏍¢獙鐘舵��
@@ -330,6 +259,7 @@
maintenanceResult: undefined,
maintenanceActuallyTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
sparePartsIds: [],
+ storageBlobDTOs: [],
};
maintenancestatusText.value = "";
selectedSpareParts.value = [];
@@ -374,7 +304,11 @@
} else if (form.value.maintenanceResult === undefined) {
isValid = false;
errorMessage = "璇烽�夋嫨淇濆吇缁撴灉";
- } else if (uploadFiles.value.length === 0 && form.value.status == "1") {
+ } else if (
+ (!form.value.storageBlobDTOs ||
+ form.value.storageBlobDTOs.length === 0) &&
+ form.value.status == "1"
+ ) {
isValid = false;
errorMessage = "璇蜂笂浼犱繚鍏荤収鐗�";
}
@@ -436,7 +370,6 @@
const submitData = {
...form.value,
- imagesFile: form.value.status == "1" ? uploadFiles.value : [],
sparePartsIds: spareIds.length ? spareIds.join(",") : "",
sparePartsQty: spareIds.length
? spareIds.map(pid => sparePartQtyMap?.[pid] ?? 1).join(",")
@@ -605,7 +538,7 @@
// 閲嶇疆閫夋嫨鐨勫浠�
selectedSpareParts.value = [];
// 閲嶇疆涓婁紶鐨勬枃浠�
- uploadFiles.value = [];
+ form.value.storageBlobDTOs = [];
uploading.value = false;
uploadProgress.value = 0;
maintenancestatusText.value = "";
@@ -655,8 +588,10 @@
sparePartsIds.value = itemData.sparePartsIds;
// 濉厖闄勪欢鏁版嵁
- if (itemData.files && itemData.files.length > 0) {
- uploadFiles.value = itemData.files.map(file => ({
+ if (itemData.storageBlobVOs && itemData.storageBlobVOs.length > 0) {
+ form.value.storageBlobDTOs = itemData.storageBlobVOs;
+ } else if (itemData.files && itemData.files.length > 0) {
+ form.value.storageBlobDTOs = itemData.files.map(file => ({
id: file.id,
name: file.name || file.bucketFilename || file.originalFilename,
url: file.url || file.downloadUrl,
@@ -668,7 +603,7 @@
size: file.size || file.byteSize,
}));
} else if (itemData.uploadFiles && itemData.uploadFiles.length > 0) {
- uploadFiles.value = itemData.uploadFiles.map(file => ({
+ form.value.storageBlobDTOs = itemData.uploadFiles.map(file => ({
id: file.id,
name: file.name || file.bucketFilename || file.originalFilename,
url: file.url || file.downloadUrl || file.tempFilePath || file.path,
diff --git a/src/pages/fileManagement/borrow/edit.vue b/src/pages/fileManagement/borrow/edit.vue
new file mode 100644
index 0000000..1338a6f
--- /dev/null
+++ b/src/pages/fileManagement/borrow/edit.vue
@@ -0,0 +1,333 @@
+<template>
+ <view class="borrow-edit">
+ <PageHeader :title="pageTitle" @back="goBack" />
+
+ <up-form
+ ref="formRef"
+ :model="form"
+ :rules="rules"
+ label-width="110"
+ >
+ <up-form-item label="鍊熼槄浜�" prop="borrower" required>
+ <up-input
+ v-model="form.borrower"
+ placeholder="璇疯緭鍏ュ�熼槄浜�"
+ clearable
+ :disabled="isReturned"
+ />
+ </up-form-item>
+
+ <up-form-item label="鍊熼槄涔︾睄" prop="documentationId" required>
+ <up-input
+ v-model="displayDocName"
+ placeholder="璇烽�夋嫨鍊熼槄涔︾睄"
+ readonly
+ :disabled="isEdit"
+ @click="!isEdit && (showDocPicker = true)"
+ />
+ <template #right>
+ <up-icon v-if="!isEdit" name="arrow-right" @click="showDocPicker = true"></up-icon>
+ </template>
+ </up-form-item>
+
+ <up-form-item label="鍊熼槄鏃ユ湡" prop="borrowDate" required>
+ <up-input
+ v-model="form.borrowDate"
+ placeholder="璇烽�夋嫨鍊熼槄鏃ユ湡"
+ readonly
+ @click="!isReturned && (showBorrowDatePicker = true)"
+ :disabled="isReturned"
+ />
+ <template #right>
+ <up-icon name="arrow-right" @click="!isReturned && (showBorrowDatePicker = true)"></up-icon>
+ </template>
+ </up-form-item>
+
+ <up-form-item label="搴斿綊杩樻棩鏈�" prop="dueReturnDate" required>
+ <up-input
+ v-model="form.dueReturnDate"
+ placeholder="璇烽�夋嫨搴斿綊杩樻棩鏈�"
+ readonly
+ @click="!isReturned && (showDueDatePicker = true)"
+ :disabled="isReturned"
+ />
+ <template #right>
+ <up-icon name="arrow-right" @click="!isReturned && (showDueDatePicker = true)"></up-icon>
+ </template>
+ </up-form-item>
+
+ <up-form-item label="鍊熼槄鐩殑" prop="borrowPurpose" required>
+ <up-input
+ v-model="form.borrowPurpose"
+ placeholder="璇疯緭鍏ュ�熼槄鐩殑"
+ clearable
+ :disabled="isReturned"
+ />
+ </up-form-item>
+
+ <up-form-item label="澶囨敞">
+ <up-textarea
+ v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+ height="80"
+ border="none"
+ :disabled="isReturned"
+ />
+ </up-form-item>
+ </up-form>
+
+ <FooterButtons
+ v-if="!isReturned"
+ :loading="loading"
+ :confirmText="isEdit ? '淇濆瓨' : '鏂板'"
+ @cancel="goBack"
+ @confirm="handleSubmit"
+ />
+
+ <!-- 鍊熼槄鏃ユ湡閫夋嫨鍣� -->
+ <up-popup :show="showBorrowDatePicker" mode="bottom" @close="showBorrowDatePicker = false">
+ <up-datetime-picker
+ :show="true"
+ v-model="borrowDateValue"
+ @confirm="onBorrowDateConfirm"
+ @cancel="showBorrowDatePicker = false"
+ mode="date"
+ />
+ </up-popup>
+
+ <!-- 搴斿綊杩樻棩鏈熼�夋嫨鍣� -->
+ <up-popup :show="showDueDatePicker" mode="bottom" @close="showDueDatePicker = false">
+ <up-datetime-picker
+ :show="true"
+ v-model="dueReturnDateValue"
+ @confirm="onDueDateConfirm"
+ @cancel="showDueDatePicker = false"
+ mode="date"
+ />
+ </up-popup>
+
+ <!-- 鏂囨。閫夋嫨鍣� -->
+ <up-action-sheet
+ :show="showDocPicker"
+ :actions="documentOptions"
+ title="閫夋嫨鍊熼槄涔︾睄"
+ @select="onDocSelect"
+ @close="showDocPicker = false"
+ />
+ </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from "vue";
+import { onLoad } from "@dcloudio/uni-app";
+import PageHeader from "@/components/PageHeader.vue";
+import FooterButtons from "@/components/FooterButtons.vue";
+import { addBorrow, updateBorrow, getDocumentList } from "@/api/fileManagement/borrow";
+
+const formRef = ref();
+const loading = ref(false);
+const borrowId = ref("");
+const isEdit = ref(false);
+
+// 寮圭獥鏄剧ず鐘舵��
+const showDocPicker = ref(false);
+const showBorrowDatePicker = ref(false);
+const showDueDatePicker = ref(false);
+
+// 鏁版嵁
+const documentList = ref([]);
+const borrowDateValue = ref(Date.now());
+const dueReturnDateValue = ref(Date.now());
+const displayDocName = ref(""); // 鐢ㄤ簬鏄剧ず鐨勬枃妗e悕绉�
+
+const form = ref({
+ id: "",
+ documentationId: "",
+ borrower: "",
+ borrowPurpose: "",
+ borrowDate: "",
+ dueReturnDate: "",
+ remark: "",
+ borrowStatus: "",
+});
+
+const rules = {
+ borrower: [{ required: true, message: "璇疯緭鍏ュ�熼槄浜�", trigger: "blur" }],
+ documentationId: [{ required: true, message: "璇烽�夋嫨鍊熼槄涔︾睄", trigger: "change" }],
+ borrowPurpose: [{ required: true, message: "璇疯緭鍏ュ�熼槄鐩殑", trigger: "blur" }],
+ borrowDate: [{ required: true, message: "璇烽�夋嫨鍊熼槄鏃ユ湡", trigger: "change" }],
+ dueReturnDate: [{ required: true, message: "璇烽�夋嫨搴斿綊杩樻棩鏈�", trigger: "change" }],
+};
+
+// 椤甸潰鏍囬
+const pageTitle = computed(() => {
+ if (isEdit.value) {
+ return form.value.borrowStatus === "褰掕繕" ? "鍊熼槄璇︽儏" : "缂栬緫鍊熼槄";
+ }
+ return "鏂板鍊熼槄";
+});
+
+// 鏄惁宸插綊杩�
+const isReturned = computed(() => {
+ return form.value.borrowStatus === "褰掕繕";
+});
+
+// 鏂囨。閫夐」锛堜粎鏂板妯″紡浣跨敤锛�
+const documentOptions = computed(() => {
+ return documentList.value.map((item) => ({
+ name: item.docName || item.name,
+ id: item.id,
+ }));
+});
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.removeStorageSync("borrowEditData");
+ uni.navigateBack();
+};
+
+// 鍔犺浇鏂囨。鍒楄〃锛堜粎鏂板妯″紡闇�瑕侊級
+const loadDocumentList = async () => {
+ try {
+ const res = await getDocumentList();
+ if (res.code === 200) {
+ documentList.value = res.data || [];
+ }
+ } catch (error) {
+ console.error("鑾峰彇鏂囨。鍒楄〃澶辫触", error);
+ }
+};
+
+// 鏂囨。閫夋嫨纭
+const onDocSelect = (e) => {
+ form.value.documentationId = e.id;
+ displayDocName.value = e.name;
+ showDocPicker.value = false;
+};
+
+// 鍊熼槄鏃ユ湡纭
+const onBorrowDateConfirm = (e) => {
+ const date = new Date(e.value);
+ form.value.borrowDate = formatDate(date);
+ showBorrowDatePicker.value = false;
+};
+
+// 搴斿綊杩樻棩鏈熺‘璁�
+const onDueDateConfirm = (e) => {
+ const date = new Date(e.value);
+ form.value.dueReturnDate = formatDate(date);
+ showDueDatePicker.value = false;
+};
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (date) => {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0");
+ const day = String(date.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+};
+
+// 鎻愪氦琛ㄥ崟
+const handleSubmit = () => {
+ // 濡傛灉宸插綊杩橈紝绂佹鎻愪氦
+ if (isReturned.value) {
+ uni.showToast({ title: "宸插綊杩樼殑鍊熼槄璁板綍涓嶈兘缂栬緫", icon: "none" });
+ return;
+ }
+
+ formRef.value
+ .validate()
+ .then(async () => {
+ try {
+ loading.value = true;
+
+ if (isEdit.value) {
+ // 缂栬緫妯″紡
+ const res = await updateBorrow({
+ id: form.value.id,
+ borrower: form.value.borrower,
+ borrowPurpose: form.value.borrowPurpose,
+ borrowDate: form.value.borrowDate,
+ dueReturnDate: form.value.dueReturnDate,
+ remark: form.value.remark,
+ });
+
+ if (res.code === 200) {
+ uni.showToast({ title: "缂栬緫鎴愬姛", icon: "success" });
+ setTimeout(() => {
+ goBack();
+ }, 1500);
+ } else {
+ uni.showToast({ title: res.msg || "缂栬緫澶辫触", icon: "none" });
+ }
+ } else {
+ // 鏂板妯″紡
+ const res = await addBorrow({
+ documentationId: form.value.documentationId,
+ borrower: form.value.borrower,
+ borrowPurpose: form.value.borrowPurpose,
+ borrowDate: form.value.borrowDate,
+ dueReturnDate: form.value.dueReturnDate,
+ borrowStatus: "鍊熼槄",
+ remark: form.value.remark,
+ });
+
+ if (res.code === 200) {
+ uni.showToast({ title: "鏂板鎴愬姛", icon: "success" });
+ setTimeout(() => {
+ goBack();
+ }, 1500);
+ } else {
+ uni.showToast({ title: res.msg || "鏂板澶辫触", icon: "none" });
+ }
+ }
+ } catch (error) {
+ uni.showToast({ title: "鎿嶄綔澶辫触", icon: "none" });
+ } finally {
+ loading.value = false;
+ }
+ })
+ .catch(() => {
+ // 楠岃瘉澶辫触
+ });
+};
+
+// 椤甸潰鍔犺浇
+onLoad((options) => {
+ if (options.id) {
+ // 缂栬緫妯″紡
+ isEdit.value = true;
+ borrowId.value = options.id;
+
+ // 浠� storage 鑾峰彇缂栬緫鏁版嵁
+ const editDataStr = uni.getStorageSync("borrowEditData");
+ if (editDataStr) {
+ try {
+ const data = JSON.parse(editDataStr);
+ Object.assign(form.value, data);
+ borrowDateValue.value = new Date(data.borrowDate).getTime();
+ dueReturnDateValue.value = new Date(data.dueReturnDate).getTime();
+ // 鐩存帴浣跨敤浼犻�掔殑鏂囨。鍚嶇О鏄剧ず锛屽皾璇曞涓彲鑳界殑瀛楁鍚�
+ displayDocName.value = data.docName || data.documentationName || data.fileName || data.name || "";
+ } catch (e) {
+ console.error("瑙f瀽缂栬緫鏁版嵁澶辫触", e);
+ }
+ }
+ } else {
+ // 鏂板妯″紡锛屽姞杞芥枃妗e垪琛ㄥ苟璁剧疆榛樿鏃ユ湡
+ loadDocumentList();
+ const today = new Date();
+ form.value.borrowDate = formatDate(today);
+ borrowDateValue.value = today.getTime();
+ }
+});
+</script>
+
+<style lang="scss">
+@import "@/static/scss/form-common.scss";
+
+.borrow-edit {
+ min-height: 100vh;
+ background: #f5f5f5;
+}
+</style>
diff --git a/src/pages/fileManagement/borrow/index.vue b/src/pages/fileManagement/borrow/index.vue
new file mode 100644
index 0000000..b051137
--- /dev/null
+++ b/src/pages/fileManagement/borrow/index.vue
@@ -0,0 +1,262 @@
+<template>
+ <view class="sales-account">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="鍊熼槄绠$悊" @back="goBack" />
+
+ <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input
+ class="search-text"
+ placeholder="璇疯緭鍏ュ�熼槄浜烘悳绱�"
+ v-model="searchForm.borrower"
+ @change="getList"
+ clearable
+ />
+ </view>
+ <view class="filter-button" @click="getList">
+ <up-icon name="search" size="24" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍊熼槄鍒楄〃 -->
+ <view class="ledger-list" v-if="borrowList.length > 0">
+ <view v-for="(item, index) in borrowList" :key="index">
+ <view 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.docName || '-' }}</text>
+ </view>
+ <view class="item-tag" :class="getStatusClass(item.borrowStatus)">
+ <text class="tag-text">{{ item.borrowStatus }}</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.borrower || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍊熼槄鐩殑</text>
+ <text class="detail-value">{{ item.borrowPurpose || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍊熼槄鏃ユ湡</text>
+ <text class="detail-value">{{ item.borrowDate || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">搴斿綊杩樻棩鏈�</text>
+ <text class="detail-value">{{ item.dueReturnDate || '-' }}</text>
+ </view>
+ <view class="detail-row" v-if="item.remark">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark }}</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="detail-buttons">
+ <u-button
+ v-if="item.borrowStatus !== '褰掕繕'"
+ class="detail-button"
+ size="small"
+ type="primary"
+ @click.stop="goEdit(item)"
+ >
+ 缂栬緫
+ </u-button>
+ <u-button
+ v-if="item.borrowStatus !== '褰掕繕'"
+ class="detail-button"
+ size="small"
+ type="error"
+ plain
+ @click.stop="handleDelete(item)"
+ >
+ 鍒犻櫎
+ </u-button>
+ <u-button
+ v-if="item.borrowStatus === '褰掕繕'"
+ class="detail-button"
+ size="small"
+ type="primary"
+ plain
+ @click.stop="goView(item)"
+ >
+ 鏌ョ湅
+ </u-button>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <view v-else class="no-data">
+ <text>鏆傛棤鍊熼槄璁板綍</text>
+ </view>
+
+ <!-- 娴姩鎿嶄綔鎸夐挳 -->
+ <view class="fab-button" @click="goAdd">
+ <up-icon name="plus" size="24" color="#ffffff"></up-icon>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, reactive } from "vue";
+import { onShow } from "@dcloudio/uni-app";
+import PageHeader from "@/components/PageHeader.vue";
+import { getBorrowList, deleteBorrow } from "@/api/fileManagement/borrow";
+
+// 鏌ヨ琛ㄥ崟
+const searchForm = reactive({
+ borrower: "",
+});
+
+// 鍊熼槄鍒楄〃鏁版嵁
+const borrowList = ref([]);
+
+// 鍒嗛〉鐩稿叧
+const pagination = reactive({
+ currentPage: 1,
+ pageSize: 10,
+ total: 0,
+});
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.navigateBack();
+};
+
+// 鑾峰彇鐘舵�佹牱寮忕被
+const getStatusClass = (status) => {
+ if (status === "褰掕繕") return "tag-success";
+ if (status === "鍊熼槄") return "tag-warning";
+ return "tag-default";
+};
+
+// 鍔犺浇鍊熼槄鍒楄〃
+const getList = async () => {
+ uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
+
+ const query = {
+ page: -1,
+ size: -1,
+ borrower: searchForm.borrower || undefined,
+ };
+
+ try {
+ const res = await getBorrowList(query);
+ if (res.code === 200) {
+ borrowList.value = res.data.records || [];
+ pagination.total = res.data.total || 0;
+ } else {
+ uni.showToast({ title: res.msg || "鑾峰彇鍊熼槄鍒楄〃澶辫触", icon: "none" });
+ borrowList.value = [];
+ }
+ } catch (error) {
+ uni.showToast({ title: "鑾峰彇鍊熼槄鍒楄〃澶辫触", icon: "none" });
+ borrowList.value = [];
+ } finally {
+ uni.hideLoading();
+ }
+};
+
+// 璺宠浆鍒版柊澧為〉闈�
+const goAdd = () => {
+ uni.navigateTo({
+ url: "/pages/fileManagement/borrow/edit",
+ });
+};
+
+// 璺宠浆鍒扮紪杈戦〉闈�
+const goEdit = (item) => {
+ uni.setStorageSync("borrowEditData", JSON.stringify(item));
+ uni.navigateTo({
+ url: `/pages/fileManagement/borrow/edit?id=${item.id}`,
+ });
+};
+
+// 璺宠浆鍒版煡鐪嬮〉闈紙宸插綊杩樿褰曪級
+const goView = (item) => {
+ uni.setStorageSync("borrowEditData", JSON.stringify(item));
+ uni.navigateTo({
+ url: `/pages/fileManagement/borrow/edit?id=${item.id}`,
+ });
+};
+
+// 鍒犻櫎
+const handleDelete = (row) => {
+ uni.showModal({
+ title: "鍒犻櫎纭",
+ content: "閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�",
+ confirmText: "纭",
+ cancelText: "鍙栨秷",
+ success: async (res) => {
+ if (res.confirm) {
+ try {
+ uni.showLoading({ title: "鍒犻櫎涓�...", mask: true });
+ const result = await deleteBorrow([row.id]);
+ if (result.code === 200) {
+ uni.showToast({ title: "鍒犻櫎鎴愬姛", icon: "success" });
+ getList();
+ } else {
+ uni.showToast({ title: result.msg || "鍒犻櫎澶辫触", icon: "none" });
+ }
+ } catch (error) {
+ uni.showToast({ title: "鍒犻櫎澶辫触", icon: "none" });
+ } finally {
+ uni.hideLoading();
+ }
+ }
+ },
+ });
+};
+
+onShow(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+@import "@/styles/sales-common.scss";
+
+// 鏍囩鏍峰紡
+.item-tag {
+ border-radius: 4px;
+ padding: 2px 8px;
+
+ &.tag-success {
+ background: #4caf50;
+ }
+
+ &.tag-warning {
+ background: #ff9800;
+ }
+
+ &.tag-default {
+ background: #9e9e9e;
+ }
+}
+
+.tag-text {
+ font-size: 11px;
+ color: #ffffff;
+ font-weight: 500;
+}
+
+// 鎸夐挳鏍峰紡
+.detail-buttons {
+ padding: 12px 0;
+ display: flex;
+ gap: 12px;
+}
+
+.detail-button {
+ flex: 1;
+}
+</style>
diff --git a/src/pages/fileManagement/return/edit.vue b/src/pages/fileManagement/return/edit.vue
new file mode 100644
index 0000000..e0f9e86
--- /dev/null
+++ b/src/pages/fileManagement/return/edit.vue
@@ -0,0 +1,313 @@
+<template>
+ <view class="return-edit">
+ <PageHeader :title="pageTitle" @back="goBack" />
+
+ <up-form
+ ref="formRef"
+ :model="form"
+ :rules="rules"
+ label-width="110"
+ >
+ <up-form-item label="鏂囨。" prop="borrowId" required>
+ <up-input
+ v-model="displayDocName"
+ placeholder="璇烽�夋嫨鏂囨。"
+ readonly
+ :disabled="isEdit"
+ @click="!isEdit && (showDocPicker = true)"
+ />
+ <template #right>
+ <up-icon v-if="!isEdit" name="arrow-right" @click="showDocPicker = true"></up-icon>
+ </template>
+ </up-form-item>
+
+ <up-form-item label="鍊熼槄浜�" prop="borrower">
+ <up-input
+ v-model="form.borrower"
+ placeholder="閫夋嫨鏂囨。鍚庤嚜鍔ㄥ甫鍑�"
+ disabled
+ />
+ </up-form-item>
+
+ <up-form-item label="褰掕繕浜�" prop="returner" required>
+ <up-input
+ v-model="form.returner"
+ placeholder="璇疯緭鍏ュ綊杩樹汉"
+ clearable
+ :disabled="isReturned"
+ />
+ </up-form-item>
+
+ <up-form-item label="褰掕繕鏃ユ湡" prop="returnDate" required>
+ <up-input
+ v-model="form.returnDate"
+ placeholder="璇烽�夋嫨褰掕繕鏃ユ湡"
+ readonly
+ @click="!isReturned && (showReturnDatePicker = true)"
+ :disabled="isReturned"
+ />
+ <template #right>
+ <up-icon name="arrow-right" @click="!isReturned && (showReturnDatePicker = true)"></up-icon>
+ </template>
+ </up-form-item>
+
+ <up-form-item label="搴斿綊杩樻棩鏈�" prop="dueReturnDate">
+ <up-input
+ v-model="form.dueReturnDate"
+ placeholder="閫夋嫨鏂囨。鍚庤嚜鍔ㄥ甫鍑�"
+ disabled
+ />
+ </up-form-item>
+
+ <up-form-item label="澶囨敞璇存槑" prop="remark">
+ <up-textarea
+ v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉ㄨ鏄�"
+ height="80"
+ border="none"
+ :disabled="isReturned"
+ />
+ </up-form-item>
+ </up-form>
+
+ <FooterButtons
+ v-if="!isReturned"
+ :loading="loading"
+ :confirmText="isEdit ? '淇濆瓨' : '鏂板'"
+ @cancel="goBack"
+ @confirm="handleSubmit"
+ />
+
+ <!-- 鏂囨。閫夋嫨鍣� -->
+ <up-action-sheet
+ :show="showDocPicker"
+ :actions="documentOptions"
+ title="閫夋嫨鏂囨。"
+ @select="onDocSelect"
+ @close="showDocPicker = false"
+ />
+
+ <!-- 褰掕繕鏃ユ湡閫夋嫨鍣� -->
+ <up-popup :show="showReturnDatePicker" mode="bottom" @close="showReturnDatePicker = false">
+ <up-datetime-picker
+ :show="true"
+ v-model="returnDateValue"
+ @confirm="onReturnDateConfirm"
+ @cancel="showReturnDatePicker = false"
+ mode="date"
+ />
+ </up-popup>
+ </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from "vue";
+import { onLoad } from "@dcloudio/uni-app";
+import PageHeader from "@/components/PageHeader.vue";
+import FooterButtons from "@/components/FooterButtons.vue";
+import { returnDocument, reventUpdate, getDocumentList } from "@/api/fileManagement/return";
+
+const formRef = ref();
+const loading = ref(false);
+const returnId = ref("");
+const isEdit = ref(false);
+
+// 寮圭獥鏄剧ず鐘舵��
+const showDocPicker = ref(false);
+const showReturnDatePicker = ref(false);
+
+// 鏁版嵁
+const documentList = ref([]);
+const returnDateValue = ref(Date.now());
+const displayDocName = ref(""); // 鐢ㄤ簬鏄剧ず鐨勬枃妗e悕绉�
+
+const form = ref({
+ id: "",
+ borrowId: "",
+ documentationId: "",
+ borrower: "",
+ returner: "",
+ borrowStatus: "",
+ returnDate: "",
+ dueReturnDate: "",
+ remark: "",
+});
+
+const rules = {
+ borrowId: [{ required: true, message: "璇烽�夋嫨鏂囨。", trigger: "change" }],
+ returner: [{ required: true, message: "璇疯緭鍏ュ綊杩樹汉", trigger: "blur" }],
+ returnDate: [{ required: true, message: "璇烽�夋嫨褰掕繕鏃ユ湡", trigger: "change" }],
+};
+
+// 椤甸潰鏍囬
+const pageTitle = computed(() => {
+ if (isEdit.value) {
+ return form.value.borrowStatus === "褰掕繕" ? "褰掕繕璇︽儏" : "缂栬緫褰掕繕";
+ }
+ return "鏂板褰掕繕";
+});
+
+// 鏄惁宸插綊杩�
+const isReturned = computed(() => {
+ return form.value.borrowStatus === "褰掕繕";
+});
+
+// 鏂囨。閫夐」锛堜粎鏂板妯″紡浣跨敤锛�
+const documentOptions = computed(() => {
+ return documentList.value.map((item) => ({
+ name: item.docName || item.name,
+ id: item.id,
+ borrower: item.borrower,
+ dueReturnDate: item.dueReturnDate,
+ }));
+});
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.removeStorageSync("returnEditData");
+ uni.navigateBack();
+};
+
+// 鍔犺浇鏂囨。鍒楄〃锛堜粎鏂板妯″紡闇�瑕侊級
+const loadDocumentList = async () => {
+ try {
+ const res = await getDocumentList();
+ if (res.code === 200) {
+ documentList.value = res.data || [];
+ }
+ } catch (error) {
+ console.error("鑾峰彇鏂囨。鍒楄〃澶辫触", error);
+ }
+};
+
+// 鏂囨。閫夋嫨纭
+const onDocSelect = (e) => {
+ form.value.borrowId = e.id;
+ form.value.documentationId = e.id;
+ displayDocName.value = e.name;
+ // 鑷姩甯﹀嚭鍊熼槄浜哄拰搴斿綊杩樻棩鏈�
+ form.value.borrower = e.borrower || "";
+ form.value.dueReturnDate = e.dueReturnDate || "";
+ showDocPicker.value = false;
+};
+
+// 褰掕繕鏃ユ湡纭
+const onReturnDateConfirm = (e) => {
+ const date = new Date(e.value);
+ form.value.returnDate = formatDate(date);
+ showReturnDatePicker.value = false;
+};
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (date) => {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0");
+ const day = String(date.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+};
+
+// 鎻愪氦琛ㄥ崟
+const handleSubmit = () => {
+ // 濡傛灉宸插綊杩橈紝绂佹鎻愪氦
+ if (isReturned.value) {
+ uni.showToast({ title: "宸插綊杩樼殑璁板綍涓嶈兘缂栬緫", icon: "none" });
+ return;
+ }
+
+ formRef.value
+ .validate()
+ .then(async () => {
+ try {
+ loading.value = true;
+
+ if (isEdit.value) {
+ // 缂栬緫妯″紡
+ const res = await reventUpdate({
+ id: form.value.id,
+ documentationId: form.value.documentationId,
+ borrower: form.value.borrower,
+ returner: form.value.returner,
+ borrowStatus: form.value.borrowStatus,
+ returnDate: form.value.returnDate,
+ dueReturnDate: form.value.dueReturnDate,
+ remark: form.value.remark,
+ });
+
+ if (res.code === 200) {
+ uni.showToast({ title: "缂栬緫鎴愬姛", icon: "success" });
+ setTimeout(() => {
+ goBack();
+ }, 1500);
+ } else {
+ uni.showToast({ title: res.msg || "缂栬緫澶辫触", icon: "none" });
+ }
+ } else {
+ // 鏂板妯″紡
+ const res = await returnDocument({
+ borrowId: form.value.borrowId,
+ borrower: form.value.borrower,
+ returner: form.value.returner,
+ borrowStatus: "褰掕繕",
+ returnDate: form.value.returnDate,
+ dueReturnDate: form.value.dueReturnDate,
+ remark: form.value.remark,
+ });
+
+ if (res.code === 200) {
+ uni.showToast({ title: "鏂板鎴愬姛", icon: "success" });
+ setTimeout(() => {
+ goBack();
+ }, 1500);
+ } else {
+ uni.showToast({ title: res.msg || "鏂板澶辫触", icon: "none" });
+ }
+ }
+ } catch (error) {
+ uni.showToast({ title: "鎿嶄綔澶辫触", icon: "none" });
+ } finally {
+ loading.value = false;
+ }
+ })
+ .catch(() => {
+ // 楠岃瘉澶辫触
+ });
+};
+
+// 椤甸潰鍔犺浇
+onLoad((options) => {
+ if (options.id) {
+ // 缂栬緫妯″紡
+ isEdit.value = true;
+ returnId.value = options.id;
+
+ // 浠� storage 鑾峰彇缂栬緫鏁版嵁
+ const editDataStr = uni.getStorageSync("returnEditData");
+ if (editDataStr) {
+ try {
+ const data = JSON.parse(editDataStr);
+ Object.assign(form.value, data);
+ returnDateValue.value = new Date(data.returnDate).getTime();
+ // 鐩存帴浣跨敤浼犻�掔殑 docName 鏄剧ず
+ displayDocName.value = data.docName || "";
+ } catch (e) {
+ console.error("瑙f瀽缂栬緫鏁版嵁澶辫触", e);
+ }
+ }
+ } else {
+ // 鏂板妯″紡锛屽姞杞芥枃妗e垪琛ㄥ苟璁剧疆榛樿鏃ユ湡
+ loadDocumentList();
+ const today = new Date();
+ form.value.returnDate = formatDate(today);
+ returnDateValue.value = today.getTime();
+ }
+});
+</script>
+
+<style lang="scss">
+@import "@/static/scss/form-common.scss";
+
+.return-edit {
+ min-height: 100vh;
+ background: #f5f5f5;
+}
+</style>
diff --git a/src/pages/fileManagement/return/index.vue b/src/pages/fileManagement/return/index.vue
new file mode 100644
index 0000000..3c8aeb4
--- /dev/null
+++ b/src/pages/fileManagement/return/index.vue
@@ -0,0 +1,262 @@
+<template>
+ <view class="sales-account">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="褰掕繕绠$悊" @back="goBack" />
+
+ <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input
+ class="search-text"
+ placeholder="璇疯緭鍏ュ�熼槄浜烘悳绱�"
+ v-model="searchForm.borrower"
+ @change="getList"
+ clearable
+ />
+ </view>
+ <view class="filter-button" @click="getList">
+ <up-icon name="search" size="24" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+
+ <!-- 褰掕繕鍒楄〃 -->
+ <view class="ledger-list" v-if="returnList.length > 0">
+ <view v-for="(item, index) in returnList" :key="index">
+ <view 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.docName || '-' }}</text>
+ </view>
+ <view class="item-tag" :class="getStatusClass(item.borrowStatus)">
+ <text class="tag-text">{{ item.borrowStatus }}</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.borrower || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">褰掕繕浜�</text>
+ <text class="detail-value">{{ item.returner || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">褰掕繕鏃ユ湡</text>
+ <text class="detail-value">{{ item.returnDate || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">搴斿綊杩樻棩鏈�</text>
+ <text class="detail-value">{{ item.dueReturnDate || '-' }}</text>
+ </view>
+ <view class="detail-row" v-if="item.remark">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark }}</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="detail-buttons">
+ <u-button
+ v-if="item.borrowStatus !== '褰掕繕'"
+ class="detail-button"
+ size="small"
+ type="primary"
+ @click.stop="goEdit(item)"
+ >
+ 缂栬緫
+ </u-button>
+ <u-button
+ v-if="item.borrowStatus !== '褰掕繕'"
+ class="detail-button"
+ size="small"
+ type="error"
+ plain
+ @click.stop="handleDelete(item)"
+ >
+ 鍒犻櫎
+ </u-button>
+ <u-button
+ v-if="item.borrowStatus === '褰掕繕'"
+ class="detail-button"
+ size="small"
+ type="primary"
+ plain
+ @click.stop="goView(item)"
+ >
+ 鏌ョ湅
+ </u-button>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <view v-else class="no-data">
+ <text>鏆傛棤褰掕繕璁板綍</text>
+ </view>
+
+ <!-- 娴姩鎿嶄綔鎸夐挳 -->
+ <view class="fab-button" @click="goAdd">
+ <up-icon name="plus" size="24" color="#ffffff"></up-icon>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, reactive } from "vue";
+import { onShow } from "@dcloudio/uni-app";
+import PageHeader from "@/components/PageHeader.vue";
+import { getReturnListPage, deleteReturn } from "@/api/fileManagement/return";
+
+// 鏌ヨ琛ㄥ崟
+const searchForm = reactive({
+ borrower: "",
+});
+
+// 褰掕繕鍒楄〃鏁版嵁
+const returnList = ref([]);
+
+// 鍒嗛〉鐩稿叧
+const pagination = reactive({
+ currentPage: 1,
+ pageSize: 10,
+ total: 0,
+});
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.navigateBack();
+};
+
+// 鑾峰彇鐘舵�佹牱寮忕被
+const getStatusClass = (status) => {
+ if (status === "褰掕繕") return "tag-success";
+ if (status === "鍊熼槄") return "tag-warning";
+ return "tag-default";
+};
+
+// 鍔犺浇褰掕繕鍒楄〃
+const getList = async () => {
+ uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
+
+ const query = {
+ page: -1,
+ size: -1,
+ borrower: searchForm.borrower || undefined,
+ };
+
+ try {
+ const res = await getReturnListPage(query);
+ if (res.code === 200) {
+ returnList.value = res.data.records || [];
+ pagination.total = res.data.total || 0;
+ } else {
+ uni.showToast({ title: res.msg || "鑾峰彇褰掕繕鍒楄〃澶辫触", icon: "none" });
+ returnList.value = [];
+ }
+ } catch (error) {
+ uni.showToast({ title: "鑾峰彇褰掕繕鍒楄〃澶辫触", icon: "none" });
+ returnList.value = [];
+ } finally {
+ uni.hideLoading();
+ }
+};
+
+// 璺宠浆鍒版柊澧為〉闈�
+const goAdd = () => {
+ uni.navigateTo({
+ url: "/pages/fileManagement/return/edit",
+ });
+};
+
+// 璺宠浆鍒扮紪杈戦〉闈�
+const goEdit = (item) => {
+ uni.setStorageSync("returnEditData", JSON.stringify(item));
+ uni.navigateTo({
+ url: `/pages/fileManagement/return/edit?id=${item.id}`,
+ });
+};
+
+// 璺宠浆鍒版煡鐪嬮〉闈紙宸插綊杩樿褰曪級
+const goView = (item) => {
+ uni.setStorageSync("returnEditData", JSON.stringify(item));
+ uni.navigateTo({
+ url: `/pages/fileManagement/return/edit?id=${item.id}`,
+ });
+};
+
+// 鍒犻櫎
+const handleDelete = (row) => {
+ uni.showModal({
+ title: "鍒犻櫎纭",
+ content: "閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�",
+ confirmText: "纭",
+ cancelText: "鍙栨秷",
+ success: async (res) => {
+ if (res.confirm) {
+ try {
+ uni.showLoading({ title: "鍒犻櫎涓�...", mask: true });
+ const result = await deleteReturn([row.id]);
+ if (result.code === 200) {
+ uni.showToast({ title: "鍒犻櫎鎴愬姛", icon: "success" });
+ getList();
+ } else {
+ uni.showToast({ title: result.msg || "鍒犻櫎澶辫触", icon: "none" });
+ }
+ } catch (error) {
+ uni.showToast({ title: "鍒犻櫎澶辫触", icon: "none" });
+ } finally {
+ uni.hideLoading();
+ }
+ }
+ },
+ });
+};
+
+onShow(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+@import "@/styles/sales-common.scss";
+
+// 鏍囩鏍峰紡
+.item-tag {
+ border-radius: 4px;
+ padding: 2px 8px;
+
+ &.tag-success {
+ background: #4caf50;
+ }
+
+ &.tag-warning {
+ background: #ff9800;
+ }
+
+ &.tag-default {
+ background: #9e9e9e;
+ }
+}
+
+.tag-text {
+ font-size: 11px;
+ color: #ffffff;
+ font-weight: 500;
+}
+
+// 鎸夐挳鏍峰紡
+.detail-buttons {
+ padding: 12px 0;
+ display: flex;
+ gap: 12px;
+}
+
+.detail-button {
+ flex: 1;
+}
+</style>
diff --git a/src/pages/index.vue b/src/pages/index.vue
index b7d3fa9..2d253b2 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -3,23 +3,42 @@
<scroll-view class="scroll" scroll-y>
<!-- 椤堕儴 Banner锛氭斁鍏ユ粴鍔ㄥ尯鍩燂紝闅忛〉闈竴璧锋粴鍔紝涓嶅浐瀹氬湪椤堕儴 -->
<view class="hero-section">
- <view class="bg-img">
- <view class="hero-content">
- <view class="hero-ornaments">
- <view class="hero-glow glow-left" />
- <view class="hero-glow glow-right" />
- <view class="hero-mist mist-top" />
- <view class="hero-mist mist-bottom" />
- <view class="hero-curve curve-main" />
- <view class="hero-curve curve-sub" />
+ <view class="hero-banner">
+ <view class="hero-top">
+ <view class="hero-copy">
+ <view class="hero-badge">缁忚惀鐪嬫澘</view>
+ <text class="hero-title">{{ heroTitle }}</text>
+ <text class="hero-subtitle">{{ heroSubtitle }}</text>
+ <view class="hero-meta">
+ <text
+ v-for="item in heroMetaItems"
+ :key="item"
+ class="hero-meta-item"
+ >
+ {{ item }}
+ </text>
+ </view>
+ </view>
+ <view class="hero-avatar">
+ <text class="hero-avatar-text">{{ heroInitial }}</text>
</view>
</view>
- <view class="hero-wave"></view>
+ <view class="hero-panels">
+ <view
+ v-for="item in heroMetrics"
+ :key="item.label"
+ class="hero-panel"
+ >
+ <text class="hero-panel-label">{{ item.label }}</text>
+ <text class="hero-panel-value">{{ item.value }}</text>
+ <text class="hero-panel-hint">{{ item.hint }}</text>
+ </view>
+ </view>
</view>
</view>
<!-- 蹇嵎鍏ュ彛 -->
- <view class="quick-section">
+ <view v-if="quickTools.length" class="quick-section">
<up-grid :border="false" col="4">
<up-grid-item
v-for="item in quickTools"
@@ -35,7 +54,7 @@
</view>
<!-- 鏁版嵁鎬昏 -->
- <view class="section">
+ <view v-if="hasOverviewSection" class="section">
<view class="section-header">
<view class="section-title">
<view class="title-bar" />
@@ -48,7 +67,7 @@
</view>
<view v-show="overviewExpanded" class="overview">
- <view class="overview-card sales">
+ <view v-if="canShowSalesOverview" class="overview-card sales">
<view class="card-left">
<text class="card-title">閿�鍞暟鎹�</text>
<view class="card-metrics">
@@ -64,7 +83,7 @@
</view>
</view>
- <view class="overview-card purchase">
+ <view v-if="canShowPurchaseOverview" class="overview-card purchase">
<view class="card-left">
<text class="card-title">閲囪喘鏁版嵁</text>
<view class="card-metrics">
@@ -80,7 +99,7 @@
</view>
</view>
- <view class="overview-card stock">
+ <view v-if="canShowStockOverview" class="overview-card stock">
<view class="card-left">
<text class="card-title">搴撳瓨鏁版嵁</text>
<view class="card-metrics">
@@ -99,7 +118,7 @@
</view>
<!-- 瀹㈡埛鍚堝悓閲戦鍒嗘瀽 -->
- <view class="section">
+ <view v-if="canShowContractAnalysis" class="section">
<view class="section-header">
<view class="section-title">
<view class="title-bar" />
@@ -197,12 +216,15 @@
import { analysisCustomerContractAmounts, getBusiness } from "@/api/viewIndex";
import { createVersionUpgradeChecker } from "@/utils/versionUpgrade";
import DownloadProgressMask from "@/components/DownloadProgressMask.vue";
+import useUserStore from "@/store/modules/user";
const imgNum1 = "/static/images/index/num1.png";
const imgNum2 = "/static/images/index/num2.png";
const imgNum3 = "/static/images/index/num3.png";
-const quickTools = [
+const userStore = useUserStore();
+
+const quickToolSource = [
{
label: "鐢熶骇鎶ュ伐",
icon: "/static/images/icon/shengchanbaogong.svg",
@@ -224,6 +246,8 @@
route: "/pages/equipmentManagement/repair/index",
},
];
+const quickTools = ref([...quickToolSource]);
+const allowedMenuTitles = ref(new Set());
const isCanvas2d = ref(false);
@@ -272,6 +296,125 @@
uni.showToast({ title: "鏇村鍔熻兘寰呮帴鍏�", icon: "none" });
}
+
+function filterQuickToolsByRoutes() {
+ const routers = userStore.routers || [];
+
+ if (!routers || routers.length === 0) {
+ allowedMenuTitles.value = new Set();
+ quickTools.value = [...quickToolSource];
+ return;
+ }
+
+ const titles = new Set();
+ const collectMenuTitles = (routes) => {
+ if (!Array.isArray(routes)) return;
+ routes.forEach((route) => {
+ if (route.meta && route.meta.title) {
+ titles.add(route.meta.title);
+ }
+ if (route.children && route.children.length > 0) {
+ collectMenuTitles(route.children);
+ }
+ });
+ };
+ collectMenuTitles(routers);
+ allowedMenuTitles.value = titles;
+
+ quickTools.value = quickToolSource.filter((item) =>
+ titles.has(item.label)
+ );
+}
+
+function hasAnyPermission(titles) {
+ const titleSet = allowedMenuTitles.value;
+ if (!titleSet || titleSet.size === 0) return true;
+ return titles.some((title) => titleSet.has(title));
+}
+
+const canShowSalesOverview = computed(() => hasAnyPermission(["閿�鍞彴璐�"]));
+const canShowPurchaseOverview = computed(() => hasAnyPermission(["閲囪喘鍙拌处"]));
+const canShowStockOverview = computed(() => hasAnyPermission(["搴撳瓨绠$悊"]));
+const hasOverviewSection = computed(
+ () =>
+ canShowSalesOverview.value ||
+ canShowPurchaseOverview.value ||
+ canShowStockOverview.value
+);
+const canShowContractAnalysis = computed(() =>
+ hasAnyPermission(["閿�鍞彴璐�", "瀹㈡埛妗f", "瀹㈡埛寰�鏉�"])
+);
+
+const userDisplayName = computed(
+ () => userStore.nickName
+);
+const heroInitial = computed(() => userDisplayName.value.slice(0, 1).toUpperCase());
+const heroTitle = computed(() => `浣犲ソ锛�${userDisplayName.value}`);
+const heroSubtitle = computed(
+ () => userStore.currentFactoryName || "褰撳墠璐﹀彿宸茶繘鍏ヤ笟鍔¢椤�"
+);
+const heroMetaItems = computed(() => {
+ const items = [];
+ if (userStore.roleName) items.push(userStore.roleName);
+ if (userStore.currentLoginTime) items.push(`鐧诲綍浜� ${userStore.currentLoginTime}`);
+ if (!items.length) items.push("褰撳墠涓氬姟姒傝");
+ return items;
+});
+const visibleSectionCount = computed(() => {
+ let count = 0;
+ if (quickTools.value.length > 0) count += 1;
+ if (hasOverviewSection.value) count += 1;
+ if (canShowContractAnalysis.value) count += 1;
+ return count;
+});
+const heroMetrics = computed(() => {
+ const items = [];
+
+ if (canShowSalesOverview.value) {
+ items.push({
+ label: "閿�鍞�",
+ value: overviewCards.value.sales.today,
+ hint: "鏈湀钀ヤ笟棰�",
+ });
+ }
+ if (canShowPurchaseOverview.value) {
+ items.push({
+ label: "閲囪喘",
+ value: overviewCards.value.purchase.today,
+ hint: "鏈湀閲囪喘棰�",
+ });
+ }
+ if (canShowStockOverview.value) {
+ items.push({
+ label: "搴撳瓨",
+ value: overviewCards.value.stock.today,
+ hint: "褰撳墠搴撳瓨閲�",
+ });
+ }
+ if (canShowContractAnalysis.value && items.length < 3) {
+ items.push({
+ label: "鍚堝悓",
+ value: contractSummaryView.value.sumText,
+ hint: "瀹㈡埛鍚堝悓棰�",
+ });
+ }
+ if (items.length < 3) {
+ items.push({
+ label: "蹇嵎",
+ value: String(quickTools.value.length),
+ hint: "鍙敤鍏ュ彛",
+ });
+ }
+ if (items.length < 3) {
+ items.push({
+ label: "妯″潡",
+ value: String(visibleSectionCount.value),
+ hint: "鍙鏉垮潡",
+ });
+ }
+
+ return items.slice(0, 3);
+});
function getByPath(obj, path) {
if (!obj || !path) return undefined;
@@ -423,7 +566,13 @@
async function loadHome() {
chartReady.value = false;
try {
- const [bRes, cRes] = await Promise.all([getBusiness(), analysisCustomerContractAmounts()]);
+ const businessPromise = hasOverviewSection.value
+ ? getBusiness()
+ : Promise.resolve({ data: {} });
+ const contractPromise = canShowContractAnalysis.value
+ ? analysisCustomerContractAmounts()
+ : Promise.resolve({ data: { item: [], sum: "0", chain: "0", yny: "0" } });
+ const [bRes, cRes] = await Promise.all([businessPromise, contractPromise]);
businessRaw.value = bRes?.data || {};
const cData = cRes?.data || {};
contractSummary.value = {
@@ -448,11 +597,21 @@
isCanvas2d.value = false;
}
triggerVersionCheck("onMounted");
- loadHome();
+ userStore
+ .getRouters()
+ .then(() => {
+ filterQuickToolsByRoutes();
+ loadHome();
+ })
+ .catch(() => {
+ filterQuickToolsByRoutes();
+ loadHome();
+ });
});
onShow(() => {
triggerVersionCheck("onShow");
+ filterQuickToolsByRoutes();
});
</script>
@@ -508,146 +667,169 @@
}
}
.hero-section {
- margin: 0 12px;
- margin-bottom: 12px;
- animation: fadeInUp 0.6s ease-out 0.1s both;
+ margin: 0 14px 12px;
+ animation: fadeInUp 0.6s ease-out 0.1s both;
}
-.bg-img {
- width: 100%;
- height: 10.25rem;
- background:
- linear-gradient(135deg, rgba(234, 245, 255, 0.98) 0%, rgba(220, 239, 255, 0.94) 42%, rgba(244, 250, 255, 0.96) 100%),
- url("/static/images/banner/backview.png") center/cover no-repeat;
- border-radius: 18px;
- position: relative;
- overflow: hidden;
- box-shadow: 0 12px 30px rgba(118, 154, 186, 0.16);
-
- &::before {
- content: "";
- position: absolute;
- inset: 0;
- background:
- radial-gradient(circle at 14% 22%, rgba(255, 255, 255, 0.95) 0, rgba(255, 255, 255, 0) 28%),
- radial-gradient(circle at 84% 18%, rgba(191, 226, 255, 0.7) 0, rgba(191, 226, 255, 0) 26%),
- linear-gradient(180deg, rgba(255, 255, 255, 0.46) 0%, rgba(255, 255, 255, 0.16) 42%, rgba(206, 229, 247, 0.22) 100%);
- pointer-events: none;
- }
-
- &::after {
- content: "";
- position: absolute;
- left: 18%;
- bottom: -44px;
- width: 64%;
- height: 88px;
- background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.72) 0%, rgba(255, 255, 255, 0) 72%);
- border-radius: 50%;
- filter: blur(10px);
- pointer-events: none;
- }
+.hero-banner {
+ position: relative;
+ overflow: hidden;
+ border-radius: 18px;
+ padding: 18px 16px 16px;
+ min-height: 182px;
+ background:
+ linear-gradient(135deg, rgba(22, 74, 170, 0.92) 0%, rgba(33, 115, 185, 0.88) 48%, rgba(18, 156, 144, 0.82) 100%),
+ url("/static/images/banner/backview.png") center right / cover no-repeat;
+ box-shadow: 0 14px 34px rgba(29, 78, 137, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.18);
+
+ &::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background:
+ linear-gradient(120deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.02) 32%, rgba(255, 255, 255, 0) 60%),
+ radial-gradient(circle at top right, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0) 34%);
+ pointer-events: none;
+ }
+
+ &::after {
+ content: "";
+ position: absolute;
+ right: -28px;
+ bottom: -34px;
+ width: 156px;
+ height: 156px;
+ border-radius: 50%;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 72%);
+ pointer-events: none;
+ }
}
-.hero-content {
- position: relative;
- z-index: 1;
- padding: 16px 16px 14px;
- height: 100%;
- backdrop-filter: blur(2px);
+.hero-top {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 14px;
}
-.hero-ornaments {
- position: relative;
- width: 100%;
- height: 100%;
+.hero-copy {
+ min-width: 0;
+ flex: 1;
}
-.hero-glow {
- position: absolute;
- border-radius: 50%;
- filter: blur(4px);
- background: radial-gradient(circle, rgba(255, 255, 255, 0.96) 0%, rgba(255, 255, 255, 0) 72%);
- opacity: 0.9;
+.hero-badge {
+ display: inline-flex;
+ align-items: center;
+ height: 26px;
+ padding: 0 10px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.16);
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ color: rgba(255, 255, 255, 0.92);
+ font-size: 11px;
+ font-weight: 600;
}
-.hero-glow.glow-left {
- left: -10px;
- top: 8px;
- width: 120px;
- height: 120px;
+.hero-title {
+ display: block;
+ margin-top: 12px;
+ color: #ffffff;
+ font-size: 24px;
+ font-weight: 700;
+ line-height: 1.2;
}
-.hero-glow.glow-right {
- right: -20px;
- top: 4px;
- width: 144px;
- height: 144px;
- background: radial-gradient(circle, rgba(207, 234, 255, 0.92) 0%, rgba(207, 234, 255, 0) 74%);
+.hero-subtitle {
+ display: block;
+ margin-top: 8px;
+ color: rgba(255, 255, 255, 0.84);
+ font-size: 13px;
+ line-height: 1.45;
}
-.hero-mist {
- position: absolute;
- border-radius: 999px;
- background: linear-gradient(90deg, rgba(255, 255, 255, 0.52), rgba(255, 255, 255, 0.08));
- border: 1px solid rgba(255, 255, 255, 0.34);
- backdrop-filter: blur(10px);
- box-shadow: 0 10px 24px rgba(154, 190, 219, 0.14);
+.hero-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 12px;
}
-.hero-mist.mist-top {
- left: 18px;
- top: 20px;
- width: 112px;
- height: 18px;
+.hero-meta-item {
+ padding: 3px 10px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.12);
+ color: rgba(255, 255, 255, 0.86);
+ font-size: 11px;
+ line-height: 18px;
}
-.hero-mist.mist-bottom {
- left: 18px;
- top: 48px;
- width: 72px;
- height: 10px;
- opacity: 0.82;
+.hero-avatar {
+ position: relative;
+ z-index: 1;
+ width: 52px;
+ height: 52px;
+ flex: 0 0 52px;
+ border-radius: 16px;
+ background: rgba(255, 255, 255, 0.18);
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
-.hero-curve {
- position: absolute;
- border-radius: 999px;
- border: 2px solid rgba(255, 255, 255, 0.72);
- background: linear-gradient(180deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.12));
- box-shadow:
- 0 10px 26px rgba(154, 190, 219, 0.16),
- inset 0 1px 0 rgba(255, 255, 255, 0.8);
- backdrop-filter: blur(10px);
+.hero-avatar-text {
+ color: #ffffff;
+ font-size: 20px;
+ font-weight: 700;
}
-.hero-curve.curve-main {
- right: 18px;
- bottom: 22px;
- width: 176px;
- height: 84px;
- transform: rotate(-9deg);
- border-top-left-radius: 90px;
- border-bottom-right-radius: 90px;
- opacity: 1;
+.hero-panels {
+ position: relative;
+ z-index: 1;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 10px;
+ margin-top: 18px;
}
-.hero-curve.curve-sub {
- right: 96px;
- bottom: 60px;
- width: 104px;
- height: 40px;
- transform: rotate(-9deg);
- border-top-left-radius: 60px;
- border-bottom-right-radius: 60px;
- opacity: 0.9;
+.hero-panel {
+ min-width: 0;
+ padding: 12px 10px;
+ border-radius: 14px;
+ background: rgba(11, 25, 48, 0.18);
+ border: 1px solid rgba(255, 255, 255, 0.14);
+ backdrop-filter: blur(10px);
}
-.hero-wave {
- height: 1.1rem;
- background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(244, 249, 253, 0.96) 100%);
- margin-top: -1px;
- position: relative;
+.hero-panel-label {
+ display: block;
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 11px;
+ line-height: 1.2;
+}
+
+.hero-panel-value {
+ display: block;
+ margin-top: 8px;
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 1.2;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.hero-panel-hint {
+ display: block;
+ margin-top: 6px;
+ color: rgba(255, 255, 255, 0.72);
+ font-size: 11px;
+ line-height: 1.2;
}
.safe-top {
diff --git a/src/pages/inspectionUpload/attachment.vue b/src/pages/inspectionUpload/attachment.vue
new file mode 100644
index 0000000..c94efa5
--- /dev/null
+++ b/src/pages/inspectionUpload/attachment.vue
@@ -0,0 +1,460 @@
+<template>
+ <view class="attachment-page">
+ <!-- 椤甸潰澶撮儴 -->
+ <PageHeader :title="`鏌ョ湅闄勪欢 - ${taskInfo?.taskName || ''}`" @back="goBack" />
+
+ <!-- 椤甸潰鍐呭 -->
+ <view class="attachment-content">
+ <!-- 鍒嗙被鏍囩椤� -->
+ <view class="attachment-tabs">
+ <view
+ class="tab-item"
+ :class="{ active: currentViewType === 'before' }"
+ @click="switchViewType('before')"
+ >
+ 鐢熶骇鍓� ({{ getAttachmentsByType(0).length }})
+ </view>
+ <view
+ class="tab-item"
+ :class="{ active: currentViewType === 'after' }"
+ @click="switchViewType('after')"
+ >
+ 鐢熶骇涓� ({{ getAttachmentsByType(1).length }})
+ </view>
+ <view
+ class="tab-item"
+ :class="{ active: currentViewType === 'issue' }"
+ @click="switchViewType('issue')"
+ >
+ 鐢熶骇鍚� ({{ getAttachmentsByType(2).length }})
+ </view>
+ </view>
+
+ <!-- 褰撳墠鍒嗙被鐨勯檮浠跺垪琛� -->
+ <view class="attachment-list-container">
+ <view v-if="getCurrentViewAttachments().length > 0" class="attachment-list">
+ <view
+ v-for="(file, index) in getCurrentViewAttachments()"
+ :key="index"
+ class="attachment-item"
+ @click="previewAttachment(file)"
+ >
+ <view class="attachment-preview-container">
+ <image
+ v-if="isImageFile(file)"
+ :src="file.url || file.downloadUrl"
+ class="attachment-preview"
+ mode="aspectFill"
+ />
+ <view v-else class="attachment-video-preview">
+ <u-icon name="video" size="40" color="#409eff"></u-icon>
+ <text class="video-text">瑙嗛</text>
+ </view>
+ </view>
+ <view class="attachment-info">
+ <text class="attachment-name">{{ file.originalFilename || file.bucketFilename || file.name || '闄勪欢' }}</text>
+ <text class="attachment-size">{{ formatFileSize(file.byteSize || file.size) }}</text>
+ </view>
+ </view>
+ </view>
+ <view v-else class="attachment-empty">
+ <u-icon name="folder-open" size="60" color="#ccc"></u-icon>
+ <text class="empty-text">璇ュ垎绫绘殏鏃犻檮浠�</text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 瑙嗛棰勮寮圭獥 -->
+ <view v-if="showVideoDialog" class="video-modal-overlay" @click="closeVideoPreview">
+ <view class="video-modal-container" @click.stop>
+ <view class="video-modal-header">
+ <text class="video-modal-title">{{ currentVideoFile?.originalFilename || '瑙嗛棰勮' }}</text>
+ <view class="close-btn-video" @click="closeVideoPreview">
+ <u-icon name="close" size="20" color="#fff"></u-icon>
+ </view>
+ </view>
+ <view class="video-modal-body">
+ <video
+ v-if="currentVideoFile"
+ :src="currentVideoFile.url || currentVideoFile.downloadUrl"
+ class="video-player"
+ controls
+ autoplay
+ @error="handleVideoError"
+ ></video>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { onLoad } from '@dcloudio/uni-app';
+import PageHeader from '@/components/PageHeader.vue';
+import config from '@/config';
+
+// 浠诲姟淇℃伅
+const taskInfo = ref(null);
+
+// 闄勪欢鍒楄〃
+const attachmentList = ref([]);
+
+// 褰撳墠鏌ョ湅绫诲瀷
+const currentViewType = ref('before'); // 'before', 'after', 'issue'
+
+// 瑙嗛棰勮鐩稿叧鐘舵��
+const showVideoDialog = ref(false);
+const currentVideoFile = ref(null);
+
+// 鏂囦欢璁块棶鍩虹鍩�
+const filePreviewBase = config.fileUrl;
+
+// 椤甸潰鍔犺浇
+onLoad((options) => {
+ if (options.taskInfo) {
+ try {
+ taskInfo.value = JSON.parse(decodeURIComponent(options.taskInfo));
+ loadAttachments();
+ } catch (e) {
+ console.error('瑙f瀽浠诲姟淇℃伅澶辫触:', e);
+ uni.showToast({
+ title: '鍔犺浇澶辫触',
+ icon: 'error'
+ });
+ }
+ }
+});
+
+// 鍔犺浇闄勪欢鏁版嵁
+const loadAttachments = () => {
+ const task = taskInfo.value;
+ if (!task) return;
+
+ attachmentList.value = [];
+
+ // 鍚庣鍙嶆樉瀛楁
+ const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : [];
+ const beforeList = Array.isArray(task?.commonFileListBefore)
+ ? task.commonFileListBefore
+ : allList.filter((f) => f?.type === 10);
+ const afterList = Array.isArray(task?.commonFileListAfter)
+ ? task.commonFileListAfter
+ : allList.filter((f) => f?.type === 11);
+ const issueList = Array.isArray(task?.commonFileListIssue)
+ ? task.commonFileListIssue
+ : allList.filter((f) => f?.type === 12);
+
+ const mapToViewFile = (file, viewType) => {
+ const u = normalizeFileUrl(file?.url || file?.downloadUrl || '');
+ return {
+ ...file,
+ type: viewType,
+ name: file?.name || file?.originalFilename || file?.bucketFilename,
+ bucketFilename: file?.bucketFilename || file?.name,
+ originalFilename: file?.originalFilename || file?.name,
+ url: u,
+ downloadUrl: u,
+ size: file?.size || file?.byteSize,
+ };
+ };
+
+ attachmentList.value.push(...beforeList.map((f) => mapToViewFile(f, 0)));
+ attachmentList.value.push(...afterList.map((f) => mapToViewFile(f, 1)));
+ attachmentList.value.push(...issueList.map((f) => mapToViewFile(f, 2)));
+};
+
+// 灏嗗悗绔繑鍥炵殑鏂囦欢鍦板潃瑙勮寖鎴愬彲璁块棶URL
+const normalizeFileUrl = (rawUrl) => {
+ try {
+ if (!rawUrl || typeof rawUrl !== 'string') return '';
+ const url = rawUrl.trim();
+ if (!url) return '';
+ if (/^https?:\/\//i.test(url)) return url;
+ if (url.startsWith('/')) return `${filePreviewBase}${url}`;
+
+ // Windows path -> web path
+ if (/^[a-zA-Z]:\\/.test(url)) {
+ const normalized = url.replace(/\\/g, '/');
+ const idx = normalized.indexOf('/prod/');
+ if (idx >= 0) {
+ const relative = normalized.slice(idx + '/prod/'.length);
+ return `${filePreviewBase}/${relative}`;
+ }
+ return normalized;
+ }
+
+ return `${filePreviewBase}/${url.replace(/^\//, '')}`;
+ } catch (e) {
+ return rawUrl || '';
+ }
+};
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.navigateBack();
+};
+
+// 鍒囨崲鏌ョ湅绫诲瀷
+const switchViewType = (type) => {
+ currentViewType.value = type;
+};
+
+// 鏍规嵁type鑾峰彇瀵瑰簲鍒嗙被鐨勯檮浠�
+const getAttachmentsByType = (typeValue) => {
+ return attachmentList.value.filter((file) => file.type === typeValue) || [];
+};
+
+// 鑾峰彇褰撳墠鏌ョ湅绫诲瀷鐨勯檮浠�
+const getCurrentViewAttachments = () => {
+ switch (currentViewType.value) {
+ case 'before':
+ return getAttachmentsByType(0);
+ case 'after':
+ return getAttachmentsByType(1);
+ case 'issue':
+ return getAttachmentsByType(2);
+ default:
+ return [];
+ }
+};
+
+// 鍒ゆ柇鏄惁涓哄浘鐗囨枃浠�
+const isImageFile = (file) => {
+ if (file.contentType && file.contentType.startsWith('image/')) {
+ return true;
+ }
+ if (file.type === 'image') return true;
+
+ const name = file.bucketFilename || file.originalFilename || file.name || '';
+ const ext = name.split('.').pop()?.toLowerCase();
+ return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext);
+};
+
+// 棰勮闄勪欢
+const previewAttachment = (file) => {
+ if (isImageFile(file)) {
+ const imageUrls = getCurrentViewAttachments()
+ .filter((f) => isImageFile(f))
+ .map((f) => f.url || f.downloadUrl);
+
+ uni.previewImage({
+ urls: imageUrls,
+ current: file.url || file.downloadUrl,
+ });
+ } else {
+ showVideoPreview(file);
+ }
+};
+
+// 鏄剧ず瑙嗛棰勮
+const showVideoPreview = (file) => {
+ currentVideoFile.value = file;
+ showVideoDialog.value = true;
+};
+
+// 鍏抽棴瑙嗛棰勮
+const closeVideoPreview = () => {
+ showVideoDialog.value = false;
+ currentVideoFile.value = null;
+};
+
+// 瑙嗛鎾斁閿欒澶勭悊
+const handleVideoError = () => {
+ uni.showToast({
+ title: '瑙嗛鎾斁澶辫触',
+ icon: 'error',
+ });
+};
+
+// 鏍煎紡鍖栨枃浠跺ぇ灏�
+const formatFileSize = (size) => {
+ if (!size) return '';
+ if (size < 1024) return size + 'B';
+ if (size < 1024 * 1024) return (size / 1024).toFixed(1) + 'KB';
+ return (size / (1024 * 1024)).toFixed(1) + 'MB';
+};
+</script>
+
+<style scoped>
+.attachment-page {
+ min-height: 100vh;
+ background-color: #f5f5f5;
+}
+
+.attachment-content {
+ padding: 15px;
+}
+
+/* 鏍囩椤垫牱寮� */
+.attachment-tabs {
+ display: flex;
+ background: #fff;
+ border-radius: 12px;
+ margin-bottom: 15px;
+ padding: 4px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.tab-item {
+ flex: 1;
+ text-align: center;
+ padding: 12px 8px;
+ font-size: 14px;
+ color: #666;
+ border-radius: 8px;
+ transition: all 0.3s ease;
+}
+
+.tab-item.active {
+ background: #409eff;
+ color: #fff;
+ font-weight: 500;
+}
+
+/* 闄勪欢鍒楄〃鏍峰紡 */
+.attachment-list-container {
+ background: #fff;
+ border-radius: 12px;
+ padding: 15px;
+ min-height: 400px;
+}
+
+.attachment-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.attachment-item {
+ width: calc(33.33% - 10px);
+ background: #f8f9fa;
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ transition: all 0.3s ease;
+}
+
+.attachment-item:active {
+ transform: scale(0.98);
+}
+
+.attachment-preview-container {
+ width: 100%;
+ height: 120px;
+ background: #e9ecef;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.attachment-preview {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.attachment-video-preview {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+}
+
+.video-text {
+ font-size: 12px;
+ color: #666;
+}
+
+.attachment-info {
+ padding: 10px;
+}
+
+.attachment-name {
+ font-size: 12px;
+ color: #333;
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-bottom: 4px;
+}
+
+.attachment-size {
+ font-size: 10px;
+ color: #999;
+}
+
+/* 绌虹姸鎬佹牱寮� */
+.attachment-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 80px 20px;
+ color: #999;
+}
+
+.empty-text {
+ margin-top: 15px;
+ font-size: 14px;
+}
+
+/* 瑙嗛寮圭獥鏍峰紡 */
+.video-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.9);
+ z-index: 10000;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+}
+
+.video-modal-container {
+ width: 100%;
+ max-width: 800px;
+ background: #000;
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+.video-modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 20px;
+ background: #1a1a1a;
+}
+
+.video-modal-title {
+ font-size: 16px;
+ color: #fff;
+ font-weight: 500;
+}
+
+.close-btn-video {
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 50%;
+}
+
+.video-modal-body {
+ padding: 20px;
+}
+
+.video-player {
+ width: 100%;
+ height: 400px;
+ border-radius: 8px;
+}
+</style>
diff --git a/src/pages/inspectionUpload/components/formDia.vue b/src/pages/inspectionUpload/components/formDia.vue
index 6b900cd..62bf40f 100644
--- a/src/pages/inspectionUpload/components/formDia.vue
+++ b/src/pages/inspectionUpload/components/formDia.vue
@@ -8,53 +8,99 @@
>
<view class="popup-content">
<view class="popup-header">
- <text class="popup-title">涓婁紶</text>
+ <text class="popup-title">宸℃璁板綍涓婁紶</text>
</view>
<view class="upload-container">
+ <!-- 寮傚父鐘舵�侀�夋嫨 -->
<view class="form-container">
- <view class="title">鐢熶骇鍓�</view>
- <u-upload
- :fileList="beforeModelValue"
- @afterRead="afterRead"
- @delete="deleteFile"
- name="before"
- multiple
- :maxCount="10"
- :maxSize="5 * 1024 * 1024"
- accept="image/*"
- :previewFullImage="true"
- ></u-upload>
+ <view class="title">宸℃鐘舵��</view>
+ <view class="exception-section">
+ <view class="exception-options">
+ <view
+ class="exception-option"
+ :class="{ active: hasException === false }"
+ @click="setExceptionStatus(false)"
+ >
+ <u-icon name="checkmark-circle" size="20" color="#52c41a"></u-icon>
+ <text class="option-text">姝e父</text>
+ </view>
+ <view
+ class="exception-option"
+ :class="{ active: hasException === true }"
+ @click="setExceptionStatus(true)"
+ >
+ <u-icon name="close-circle" size="20" color="#ff4d4f"></u-icon>
+ <text class="option-text">瀛樺湪寮傚父</text>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 寮傚父鎻忚堪锛堜粎鍦ㄥ紓甯告椂鏄剧ず锛� -->
+ <view class="form-container" v-if="hasException === true">
+ <view class="title">寮傚父鎻忚堪</view>
+ <u-input
+ v-model="exceptionDescription"
+ type="textarea"
+ :maxlength="500"
+ placeholder="璇锋弿杩板紓甯告儏鍐�..."
+ :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }"
+ />
</view>
- <view class="form-container">
- <view class="title">鐢熶骇鍚�</view>
- <u-upload
- :fileList="afterModelValue"
- @afterRead="afterRead"
- @delete="deleteFile"
- name="after"
- multiple
- :maxCount="10"
- :maxSize="5 * 1024 * 1024"
- accept="image/*"
- :previewFullImage="true"
- ></u-upload>
- </view>
-
- <view class="form-container">
- <view class="title">鐢熶骇闂</view>
- <u-upload
- :fileList="issueModelValue"
- @afterRead="afterRead"
- @delete="deleteFile"
- name="issue"
- multiple
- :maxCount="10"
- :maxSize="5 * 1024 * 1024"
- accept="image/*"
- :previewFullImage="true"
- ></u-upload>
+ <!-- 涓婁紶鍖哄煙锛堜粎鍦ㄥ紓甯告椂鏄剧ず锛� -->
+ <template v-if="hasException === true">
+ <view class="form-container">
+ <view class="title">鐢熶骇鍓�</view>
+ <u-upload
+ :fileList="beforeModelValue"
+ @afterRead="afterRead"
+ @delete="deleteFile"
+ name="before"
+ multiple
+ :maxCount="10"
+ :maxSize="5 * 1024 * 1024"
+ accept="image/*"
+ :previewFullImage="true"
+ ></u-upload>
+ </view>
+
+ <view class="form-container">
+ <view class="title">鐢熶骇鍚�</view>
+ <u-upload
+ :fileList="afterModelValue"
+ @afterRead="afterRead"
+ @delete="deleteFile"
+ name="after"
+ multiple
+ :maxCount="10"
+ :maxSize="5 * 1024 * 1024"
+ accept="image/*"
+ :previewFullImage="true"
+ ></u-upload>
+ </view>
+
+ <view class="form-container">
+ <view class="title">鐢熶骇闂</view>
+ <u-upload
+ :fileList="issueModelValue"
+ @afterRead="afterRead"
+ @delete="deleteFile"
+ name="issue"
+ multiple
+ :maxCount="10"
+ :maxSize="5 * 1024 * 1024"
+ accept="image/*"
+ :previewFullImage="true"
+ ></u-upload>
+ </view>
+ </template>
+
+ <!-- 姝e父鐘舵�佹彁绀� -->
+ <view class="form-container normal-tip" v-if="hasException === false">
+ <u-icon name="info-circle" size="40" color="#52c41a"></u-icon>
+ <text class="tip-text">璁惧杩愯姝e父锛屾棤闇�涓婁紶鐓х墖</text>
</view>
</view>
@@ -79,6 +125,11 @@
const afterModelValue = ref([])
const issueModelValue = ref([])
const infoData = ref(null)
+
+// 寮傚父鐘舵�侊細null=鏈�夋嫨, false=姝e父, true=寮傚父
+const hasException = ref(null)
+// 寮傚父鎻忚堪
+const exceptionDescription = ref('')
// 璁$畻涓婁紶URL
const uploadFileUrl = computed(() => {
@@ -196,9 +247,43 @@
}
}
+// 璁剧疆寮傚父鐘舵��
+const setExceptionStatus = (status) => {
+ hasException.value = status
+}
+
// 鎻愪氦琛ㄥ崟
const submitForm = async () => {
try {
+ // 妫�鏌ユ槸鍚﹂�夋嫨浜嗗贰妫�鐘舵��
+ if (hasException.value === null) {
+ uni.showToast({
+ title: '璇烽�夋嫨宸℃鐘舵��',
+ icon: 'none'
+ })
+ return
+ }
+
+ // 濡傛灉鏄紓甯哥姸鎬侊紝妫�鏌ユ槸鍚︽湁涓婁紶鏂囦欢
+ if (hasException.value === true) {
+ const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length
+ if (totalFiles === 0) {
+ uni.showToast({
+ title: '璇蜂笂浼犲紓甯哥収鐗�',
+ icon: 'none'
+ })
+ return
+ }
+ // 妫�鏌ユ槸鍚﹀~鍐欎簡寮傚父鎻忚堪
+ if (!exceptionDescription.value.trim()) {
+ uni.showToast({
+ title: '璇峰~鍐欏紓甯告弿杩�',
+ icon: 'none'
+ })
+ return
+ }
+ }
+
let arr = []
if (beforeModelValue.value.length > 0) {
arr.push(...beforeModelValue.value.map(item => ({ ...item, statusType: 0 })))
@@ -212,6 +297,8 @@
// 鎻愪氦鏁版嵁
infoData.value.storageBlobDTO = arr
+ infoData.value.hasException = hasException.value
+ infoData.value.exceptionDescription = exceptionDescription.value
await submitInspectionRecord({ ...infoData.value })
uni.showToast({
@@ -238,6 +325,8 @@
beforeModelValue.value = []
afterModelValue.value = []
issueModelValue.value = []
+ hasException.value = null
+ exceptionDescription.value = ''
}
// 鍏抽棴寮规
@@ -311,4 +400,61 @@
border-top: 1px solid #f0f0f0;
background-color: #fafafa;
}
+
+// 寮傚父鐘舵�侀�夋嫨鏍峰紡
+.exception-section {
+ padding: 10px 0;
+}
+
+.exception-options {
+ display: flex;
+ gap: 15px;
+}
+
+.exception-option {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 15px 20px;
+ border: 2px solid #e0e0e0;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+ background-color: #fff;
+
+ &.active {
+ border-color: #1890ff;
+ background-color: #e6f7ff;
+ }
+
+ &:active {
+ opacity: 0.8;
+ }
+}
+
+.option-text {
+ font-size: 14px;
+ color: #333;
+ font-weight: 500;
+}
+
+// 姝e父鐘舵�佹彁绀烘牱寮�
+.normal-tip {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 20px;
+ background-color: #f6ffed;
+ border: 1px dashed #b7eb8f;
+ border-radius: 8px;
+
+ .tip-text {
+ margin-top: 15px;
+ font-size: 14px;
+ color: #52c41a;
+ }
+}
</style>
diff --git a/src/pages/inspectionUpload/index.vue b/src/pages/inspectionUpload/index.vue
index 135244b..bedc38f 100644
--- a/src/pages/inspectionUpload/index.vue
+++ b/src/pages/inspectionUpload/index.vue
@@ -56,6 +56,10 @@
<text class="detail-value">{{ item.taskId || item.id }}</text>
</view>
<view class="detail-item">
+ <text class="detail-label">宸℃椤圭洰</text>
+ <text class="detail-value">{{ item.inspectionProject || '鏃�' }}</text>
+ </view>
+ <view class="detail-item">
<text class="detail-label">澶囨敞</text>
<text class="detail-value">{{ item.remarks || '鏃�' }}</text>
</view>
@@ -80,7 +84,7 @@
size="small"
type="primary"
inverted></uni-tag>
- <uni-tag v-else=""
+ <uni-tag v-else
text="鏈贰妫�"
size="small"
type="warning"
@@ -95,227 +99,6 @@
<view v-if="taskTableData?.length === 0"
class="no-data">
<text>鏆傛棤鏁版嵁</text>
- </view>
- </view>
- <!-- 鍥剧墖涓婁紶寮圭獥 - 鍘熺敓瀹炵幇 -->
- <view v-if="showUploadDialog"
- class="custom-modal-overlay"
- @click="closeUploadDialog">
- <view class="custom-modal-container"
- @click.stop>
- <view class="upload-popup-content">
- <view class="upload-popup-header">
- <text class="upload-popup-title">涓婁紶宸℃璁板綍</text>
- </view>
- <view class="upload-popup-body">
- <!-- 鍒嗙被鏍囩椤� -->
- <view class="upload-tabs">
- <view class="tab-item"
- :class="{ active: currentUploadType === 'before' }"
- @click="switchUploadType('before')">
- 鐢熶骇鍓�
- </view>
- <view class="tab-item"
- :class="{ active: currentUploadType === 'after' }"
- @click="switchUploadType('after')">
- 鐢熶骇涓�
- </view>
- <view class="tab-item"
- :class="{ active: currentUploadType === 'issue' }"
- @click="switchUploadType('issue')">
- 鐢熶骇鍚�
- </view>
- </view>
- <!-- 寮傚父鐘舵�侀�夋嫨 -->
- <view class="exception-section">
- <text class="section-title">鏄惁瀛樺湪寮傚父锛�</text>
- <view class="exception-options">
- <view class="exception-option"
- :class="{ active: hasException === false }"
- @click="setExceptionStatus(false)">
- <u-icon name="checkmark-circle"
- size="20"
- color="#52c41a"></u-icon>
- <text>姝e父</text>
- </view>
- <view class="exception-option"
- :class="{ active: hasException === true }"
- @click="setExceptionStatus(true)">
- <u-icon name="close-circle"
- size="20"
- color="#ff4d4f"></u-icon>
- <text>瀛樺湪寮傚父</text>
- </view>
- </view>
- </view>
- <!-- 褰撳墠鍒嗙被鐨勪笂浼犲尯鍩� -->
- <view class="simple-upload-area">
- <view class="upload-buttons">
- <u-button type="primary"
- @click="chooseMedia('image')"
- :loading="uploading"
- :disabled="getCurrentFiles().length >= uploadConfig.limit"
- :customStyle="{ marginRight: '10px', flex: 1 }">
- <u-icon name="camera"
- size="18"
- color="#fff"
- style="margin-right: 5px;"></u-icon>
- {{ uploading ? '涓婁紶涓�...' : '鎷嶇収' }}
- </u-button>
- <u-button type="success"
- @click="chooseMedia('video')"
- :loading="uploading"
- :disabled="getCurrentFiles().length >= uploadConfig.limit"
- :customStyle="{ flex: 1 }">
- <uni-icons type="videocam"
- name="videocam"
- size="18"
- color="#fff"
- style="margin-right: 5px;"></uni-icons>
- {{ uploading ? '涓婁紶涓�...' : '鎷嶈棰�' }}
- </u-button>
- </view>
- <!-- 涓婁紶杩涘害 -->
- <view v-if="uploading"
- class="upload-progress">
- <u-line-progress :percentage="uploadProgress"
- :showText="true"
- activeColor="#409eff"></u-line-progress>
- </view>
- <!-- 褰撳墠鍒嗙被鐨勬枃浠跺垪琛� -->
- <view v-if="getCurrentFiles().length > 0"
- class="file-list">
- <view v-for="(file, index) in getCurrentFiles()"
- :key="index"
- class="file-item">
- <view class="file-preview-container">
- <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)"
- :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
- class="file-preview"
- mode="aspectFill" />
- <view v-else-if="file.type === 'video'"
- class="video-preview">
- <uni-icons type="videocam"
- name="videocam"
- size="18"
- color="#fff"
- style="margin-right: 5px;"></uni-icons>
- <text class="video-text">瑙嗛</text>
- </view>
- <!-- 鍒犻櫎鎸夐挳 -->
- <view class="delete-btn"
- @click="removeFile(index)">
- <u-icon name="close"
- size="12"
- color="#fff"></u-icon>
- </view>
- </view>
- <view class="file-info">
- <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? '鍥剧墖' : '瑙嗛')
- }}</text>
- <text class="file-size">{{ formatFileSize(file.size) }}</text>
- </view>
- </view>
- </view>
- <view v-if="getCurrentFiles().length === 0"
- class="empty-state">
- <text>璇烽�夋嫨瑕佷笂浼犵殑{{ getUploadTypeText() }}鍥剧墖鎴栬棰�</text>
- </view>
- <!-- 缁熻淇℃伅 -->
- <view class="upload-summary">
- <text class="summary-text">
- 鐢熶骇鍓�: {{ beforeModelValue.length }}涓枃浠� |
- 鐢熶骇涓�: {{ afterModelValue.length }}涓枃浠� |
- 鐢熶骇鍚�: {{ issueModelValue.length }}涓枃浠�
- </text>
- </view>
- </view>
- </view>
- <view class="upload-popup-footer">
- <u-button @click="closeUploadDialog"
- :customStyle="{ marginRight: '10px' }">鍙栨秷</u-button>
- <u-button v-if="hasException === true"
- type="warning"
- @click="goToRepair"
- :customStyle="{ marginRight: '10px' }">
- 鏂板鎶ヤ慨
- </u-button>
- <u-button type="primary"
- @click="submitUpload">鎻愪氦</u-button>
- </view>
- </view>
- </view>
- </view>
- <!-- 鏌ョ湅闄勪欢寮圭獥 -->
- <view v-if="showAttachmentDialog"
- class="custom-modal-overlay"
- @click="closeAttachmentDialog">
- <view class="custom-modal-container"
- @click.stop>
- <view class="attachment-popup-content">
- <view class="attachment-popup-header">
- <text class="attachment-popup-title">鏌ョ湅闄勪欢 - {{ currentViewTask?.taskName }}</text>
- <view class="close-btn-attachment"
- @click="closeAttachmentDialog">
- <u-icon name="close"
- size="16"
- color="#666"></u-icon>
- </view>
- </view>
- <view class="attachment-popup-body">
- <!-- 鍒嗙被鏍囩椤� -->
- <view class="attachment-tabs">
- <view class="tab-item"
- :class="{ active: currentViewType === 'before' }"
- @click="switchViewType('before')">
- 鐢熶骇鍓� ({{ getAttachmentsByType(0).length }})
- </view>
- <view class="tab-item"
- :class="{ active: currentViewType === 'after' }"
- @click="switchViewType('after')">
- 鐢熶骇涓� ({{ getAttachmentsByType(1).length }})
- </view>
- <view class="tab-item"
- :class="{ active: currentViewType === 'issue' }"
- @click="switchViewType('issue')">
- 鐢熶骇鍚� ({{ getAttachmentsByType(2).length }})
- </view>
- </view>
- <!-- 褰撳墠鍒嗙被鐨勯檮浠跺垪琛� -->
- <view class="attachment-content">
- <view v-if="getCurrentViewAttachments().length > 0"
- class="attachment-list">
- <view v-for="(file, index) in getCurrentViewAttachments()"
- :key="index"
- class="attachment-item"
- @click="previewAttachment(file)">
- <view class="attachment-preview-container">
- <image v-if="file.type === 'image' || isImageFile(file)"
- :src="file.url || file.downloadUrl"
- class="attachment-preview"
- mode="aspectFill" />
- <view v-else
- class="attachment-video-preview">
- <u-icon name="video"
- size="24"
- color="#409eff"></u-icon>
- <text class="video-text">瑙嗛</text>
- </view>
- </view>
- <view class="attachment-info">
- <text class="attachment-name">{{ file.originalFilename || file.bucketFilename || file.name || '闄勪欢'
- }}</text>
- <text class="attachment-size">{{ formatFileSize(file.byteSize || file.size) }}</text>
- </view>
- </view>
- </view>
- <view v-else
- class="attachment-empty">
- <text>璇ュ垎绫绘殏鏃犻檮浠�</text>
- </view>
- </view>
- </view>
- </view>
</view>
</view>
<!-- 瑙嗛棰勮寮圭獥 -->
@@ -378,57 +161,9 @@
const currentScanningTask = ref(null);
const infoData = ref(null);
- // 涓婁紶鐩稿叧鐘舵��
- const showUploadDialog = ref(false);
- const uploadFiles = ref([]); // 淇濈暀鐢ㄤ簬鍏煎鎬�
- const uploadStatusType = ref(0);
- const uploading = ref(false);
- const uploadProgress = ref(0);
- const number = ref(0);
- const uploadList = ref([]);
-
- // 涓変釜鍒嗙被鐨勪笂浼犵姸鎬�
- const beforeModelValue = ref([]); // 鐢熶骇鍓�
- const afterModelValue = ref([]); // 鐢熶骇涓�
- const issueModelValue = ref([]); // 鐢熶骇鍚�
-
- // 褰撳墠婵�娲荤殑涓婁紶绫诲瀷
- const currentUploadType = ref("before"); // 'before', 'after', 'issue'
-
- // 鏌ョ湅闄勪欢鐩稿叧鐘舵��
- const showAttachmentDialog = ref(false);
- const currentViewTask = ref(null);
- const currentViewType = ref("before"); // 'before', 'after', 'issue'
- const attachmentList = ref([]); // 褰撳墠鏌ョ湅浠诲姟鐨勯檮浠跺垪琛�
-
// 瑙嗛棰勮鐩稿叧鐘舵��
const showVideoDialog = ref(false);
const currentVideoFile = ref(null);
-
- // 寮傚父鐘舵��
- const hasException = ref(null); // null: 鏈�夋嫨, true: 瀛樺湪寮傚父, false: 姝e父
-
- // 涓婁紶閰嶇疆
- const uploadConfig = {
- action: "/file/upload",
- limit: 10,
- fileSize: 50, // MB
- fileType: ["jpg", "jpeg", "png", "mp4", "mov"],
- maxVideoDuration: 60, // 绉�
- };
-
- // 璁$畻涓婁紶URL
- const uploadFileUrl = computed(() => {
- const baseUrl = config.baseUrl;
-
- return baseUrl + uploadConfig.action;
- });
-
- // 璁$畻璇锋眰澶�
- const headers = computed(() => {
- const token = getToken();
- return token ? { Authorization: "Bearer " + token } : {};
- });
// 璇锋眰鍙栨秷鏍囧織锛岀敤浜庡彇娑堟鍦ㄨ繘琛岀殑璇锋眰
let isRequestCancelled = false;
@@ -489,11 +224,6 @@
onUnmounted(() => {
// 璁剧疆鍙栨秷鏍囧織锛岄樆姝㈠悗缁殑寮傛鎿嶄綔
isRequestCancelled = true;
-
- // 鍏抽棴涓婁紶寮圭獥
- if (showUploadDialog.value) {
- showUploadDialog.value = false;
- }
});
// 杩斿洖涓婁竴椤�
@@ -653,322 +383,27 @@
}
};
- // 鎵撳紑涓婁紶寮圭獥
+ // 鎵撳紑涓婁紶椤甸潰
const openUploadDialog = task => {
- // 璁剧疆浠诲姟淇℃伅鍒癷nfoData
- if (task) {
- infoData.value = {
- ...task,
- taskId: task.taskId || task.id,
- storageBlobDTO: [], // 鍒濆鍖栨枃浠跺垪琛�
- };
- }
-
- // 璁剧疆涓婁紶鐘舵�佺被鍨嬶紙鍙互鏍规嵁浠诲姟绫诲瀷璁剧疆涓嶅悓鐨勭姸鎬侊級
- uploadStatusType.value = 0; // 榛樿鐘舵��
-
- // 娓呯┖涔嬪墠鐨勬枃浠�
- uploadFiles.value = [];
-
- // 鏄剧ず涓婁紶寮圭獥
- showUploadDialog.value = true;
- };
-
- // 鍏抽棴涓婁紶寮圭獥
- const closeUploadDialog = () => {
- showUploadDialog.value = false;
- uploadFiles.value = [];
- // 娓呯悊涓変釜鍒嗙被鐨勬暟鎹�
- beforeModelValue.value = [];
- afterModelValue.value = [];
- issueModelValue.value = [];
- currentUploadType.value = "before";
- hasException.value = null; // 閲嶇疆寮傚父鐘舵��
- infoData.value = null; // 娓呯悊浠诲姟鏁版嵁
- };
-
- // 鍒囨崲涓婁紶绫诲瀷
- const switchUploadType = type => {
- currentUploadType.value = type;
- };
-
- // 鑾峰彇褰撳墠鍒嗙被鐨勬枃浠跺垪琛�
- const getCurrentFiles = () => {
- switch (currentUploadType.value) {
- case "before":
- return beforeModelValue.value || [];
- case "after":
- return afterModelValue.value || [];
- case "issue":
- return issueModelValue.value || [];
- default:
- return [];
- }
- };
-
- // 鑾峰彇涓婁紶绫诲瀷鏂囨湰
- const getUploadTypeText = () => {
- switch (currentUploadType.value) {
- case "before":
- return "鐢熶骇鍓�";
- case "after":
- return "鐢熶骇涓�";
- case "issue":
- return "鐢熶骇鍚�";
- default:
- return "";
- }
- };
-
- // 澶勭悊涓婁紶鏂囦欢鏇存柊
- const handleUploadUpdate = files => {
- uploadFiles.value = files;
- };
-
- // 璁剧疆寮傚父鐘舵��
- const setExceptionStatus = status => {
- hasException.value = status;
- };
-
- // 璺宠浆鍒版柊澧炴姤淇〉闈�
- const goToRepair = () => {
- try {
- // 瀛樺偍褰撳墠浠诲姟淇℃伅鍒版湰鍦板瓨鍌紝渚涙姤淇〉闈娇鐢�
- const taskInfo = {
- taskId: infoData.value?.taskId || infoData.value?.id,
- taskName: infoData.value?.taskName,
- inspectionLocation: infoData.value?.inspectionLocation,
- inspector: infoData.value?.inspector,
- // 浼犻�掑綋鍓嶄笂浼犵殑鏂囦欢淇℃伅
- uploadedFiles: {
- before: beforeModelValue.value,
- after: afterModelValue.value,
- issue: issueModelValue.value,
- },
- };
-
- uni.setStorageSync("repairTaskInfo", JSON.stringify(taskInfo));
-
- // 璺宠浆鍒版柊澧炴姤淇〉闈�
- uni.navigateTo({
- url: "/pages/equipmentManagement/repair/add",
- });
-
- // 鍏抽棴涓婁紶寮圭獥
- closeUploadDialog();
- } catch (error) {
- console.error("璺宠浆鎶ヤ慨椤甸潰澶辫触:", error);
- uni.showToast({
- title: "璺宠浆澶辫触锛岃閲嶈瘯",
- icon: "error",
- });
- }
- };
-
- // 鎻愪氦涓婁紶
- const submitUpload = async () => {
- try {
- // 妫�鏌ユ槸鍚﹂�夋嫨浜嗗紓甯哥姸鎬�
- if (hasException.value === null) {
- uni.showToast({
- title: "璇烽�夋嫨鏄惁瀛樺湪寮傚父",
- icon: "none",
- });
- return;
- }
-
- // 妫�鏌ユ槸鍚︽湁浠讳綍鏂囦欢涓婁紶
- const totalFiles =
- beforeModelValue.value.length +
- afterModelValue.value.length +
- issueModelValue.value.length;
- if (totalFiles === 0) {
- uni.showToast({
- title: "璇峰厛涓婁紶鏂囦欢",
- icon: "none",
- });
- return;
- }
-
- // 鏄剧ず鎻愪氦涓殑鍔犺浇鎻愮ず
- showLoadingToast("鎻愪氦涓�...");
-
- // 鎸夌収鎮ㄧ殑閫昏緫鍚堝苟鎵�鏈夊垎绫荤殑鏂囦欢
- let arr = [];
- if (beforeModelValue.value.length > 0) {
- arr.push(...beforeModelValue.value);
- }
- if (afterModelValue.value.length > 0) {
- arr.push(...afterModelValue.value);
- }
- if (issueModelValue.value.length > 0) {
- arr.push(...issueModelValue.value);
- }
-
- // 浼犵粰鍚庣鐨勪复鏃舵枃浠禝D鍒楄〃锛坱empFileIds锛�
- // 鍏煎锛氭湁浜涙帴鍙e彲鑳借繑鍥� tempId / tempFileId / id
- let tempFileIds = [];
- if (arr !== null && arr.length > 0) {
- tempFileIds = arr
- .map(item => item?.tempId ?? item?.tempFileId ?? item?.id)
- .filter(v => v !== undefined && v !== null && v !== "");
- }
-
- // 鎻愪氦鏁版嵁
- infoData.value.storageBlobDTO = arr;
- // 娣诲姞寮傚父鐘舵�佷俊鎭�
- infoData.value.hasException = hasException.value;
- infoData.value.tempFileIds = tempFileIds;
- const result = await uploadInspectionTask({ ...infoData.value });
-
- // 妫�鏌ユ彁浜ょ粨鏋�
- if (result && (result.code === 200 || result.success)) {
- // 鎻愪氦鎴愬姛
- closeToast(); // 鍏抽棴鍔犺浇鎻愮ず
-
- uni.showToast({
- title: "鎻愪氦鎴愬姛",
- icon: "success",
- });
-
- // 鍏抽棴寮圭獥
- closeUploadDialog();
-
- // 鍒锋柊鍒楄〃
- setTimeout(() => {
- reloadPage();
- }, 500);
- } else {
- // 鎻愪氦澶辫触
- closeToast();
- uni.showToast({
- title: result?.msg || result?.message || "鎻愪氦澶辫触",
- icon: "error",
- });
- }
- } catch (error) {
- console.error("鎻愪氦涓婁紶澶辫触:", error);
- closeToast(); // 鍏抽棴鍔犺浇鎻愮ず
-
- let errorMessage = "鎻愪氦澶辫触";
- if (error.message) {
- errorMessage = error.message;
- } else if (error.msg) {
- errorMessage = error.msg;
- } else if (typeof error === "string") {
- errorMessage = error;
- }
-
- uni.showToast({
- title: errorMessage,
- icon: "error",
- });
- }
+ // 灏嗕换鍔′俊鎭紶閫掑埌涓婁紶椤甸潰
+ const taskData = encodeURIComponent(JSON.stringify(task));
+ uni.navigateTo({
+ url: `/pages/inspectionUpload/upload?taskInfo=${taskData}`,
+ });
};
// 鍥剧墖涓婁紶(鍙�夋嫨鍥剧墖涓婁紶鎴栬�呮槸鐩告満鎷嶇収)
const startUploadForTask = async (task, type) => {
- // 鐩存帴鎵撳紑涓婁紶寮圭獥
+ // 鎵撳紑涓婁紶椤甸潰
openUploadDialog(task);
};
- // 鏌ョ湅闄勪欢
+ // 鏌ョ湅闄勪欢 - 璺宠浆鍒伴檮浠堕〉闈�
const viewAttachments = async task => {
- try {
- currentViewTask.value = task;
- currentViewType.value = "before";
-
- // 瑙f瀽鏂扮殑鏁版嵁缁撴瀯
- attachmentList.value = [];
-
- // 鍚庣鍙嶆樉瀛楁锛堜綘鎻愪緵鐨勬暟鎹粨鏋勶級锛�
- // - commonFileListBefore锛氱敓浜у墠锛堥�氬父 type=10锛�
- // - commonFileListAfter锛氱敓浜т腑锛堥�氬父 type=11锛�
- // - commonFileList锛氬彲鑳芥槸鍏ㄩ儴/鍏滃簳锛堣嫢鍖呭惈鐢熶骇鍚庯紝涓�鑸� type=12锛�
- const allList = Array.isArray(task?.commonFileList)
- ? task.commonFileList
- : [];
- const beforeList = Array.isArray(task?.commonFileListBefore)
- ? task.commonFileListBefore
- : allList.filter(f => f?.type === 10);
- const afterList = Array.isArray(task?.commonFileListAfter)
- ? task.commonFileListAfter
- : allList.filter(f => f?.type === 11);
- // 濡傛灉鍚庣鍚庣画琛ヤ簡 commonFileListIssue锛屽垯浼樺厛鐢紱鍚﹀垯浠� commonFileList 閲屾寜 type=12 鍏滃簳
- const issueList = Array.isArray(task?.commonFileListIssue)
- ? task.commonFileListIssue
- : allList.filter(f => f?.type === 12);
-
- const mapToViewFile = (file, viewType) => {
- const u = normalizeFileUrl(file?.url || file?.downloadUrl || "");
- return {
- ...file,
- // 鐢ㄤ簬涓夋爣绛鹃〉鍒嗙粍锛�0=鐢熶骇鍓� 1=鐢熶骇涓� 2=鐢熶骇鍚�
- type: viewType,
- name: file?.name || file?.originalFilename || file?.bucketFilename,
- bucketFilename: file?.bucketFilename || file?.name,
- originalFilename: file?.originalFilename || file?.name,
- url: u,
- downloadUrl: u,
- size: file?.size || file?.byteSize,
- };
- };
-
- attachmentList.value.push(...beforeList.map(f => mapToViewFile(f, 0)));
- attachmentList.value.push(...afterList.map(f => mapToViewFile(f, 1)));
- attachmentList.value.push(...issueList.map(f => mapToViewFile(f, 2)));
-
- showAttachmentDialog.value = true;
- } catch (error) {
- uni.showToast({
- title: "鑾峰彇闄勪欢澶辫触",
- icon: "error",
- });
- }
- };
-
- // 鍏抽棴闄勪欢鏌ョ湅寮圭獥
- const closeAttachmentDialog = () => {
- showAttachmentDialog.value = false;
- currentViewTask.value = null;
- attachmentList.value = [];
- currentViewType.value = "before";
- };
-
- // 鍒囨崲鏌ョ湅绫诲瀷
- const switchViewType = type => {
- currentViewType.value = type;
- };
-
- // 鏍规嵁type鑾峰彇瀵瑰簲鍒嗙被鐨勯檮浠�
- const getAttachmentsByType = typeValue => {
- return attachmentList.value.filter(file => file.type === typeValue) || [];
- };
- // 鑾峰彇type鍊�
- const getTabType = () => {
- switch (currentUploadType.value) {
- case "before":
- return 10;
- case "after":
- return 11;
- case "issue":
- return 12;
- default:
- return 10;
- }
- };
- // 鑾峰彇褰撳墠鏌ョ湅绫诲瀷鐨勯檮浠�
- const getCurrentViewAttachments = () => {
- switch (currentViewType.value) {
- case "before":
- return getAttachmentsByType(0);
- case "after":
- return getAttachmentsByType(1);
- case "issue":
- return getAttachmentsByType(2);
- default:
- return [];
- }
+ const taskData = encodeURIComponent(JSON.stringify(task));
+ uni.navigateTo({
+ url: `/pages/inspectionUpload/attachment?taskInfo=${taskData}`,
+ });
};
// 鍒ゆ柇鏄惁涓哄浘鐗囨枃浠�
@@ -1058,475 +493,6 @@
title: "瑙嗛鎾斁澶辫触",
icon: "error",
});
- };
-
- // 鎷嶇収/鎷嶈棰戯紙鐪熸満浼樺厛鐢� chooseMedia锛涗笉鏀寔鍒欓檷绾э級
- const chooseMedia = type => {
- if (getCurrentFiles().length >= uploadConfig.limit) {
- uni.showToast({
- title: `鏈�澶氬彧鑳介�夋嫨${uploadConfig.limit}涓枃浠禶,
- icon: "none",
- });
- return;
- }
-
- const remaining = uploadConfig.limit - getCurrentFiles().length;
-
- // 浼樺厛锛歝hooseMedia锛堟敮鎸� image/video锛�
- if (typeof uni.chooseMedia === "function") {
- uni.chooseMedia({
- count: Math.min(remaining, 1),
- mediaType: [type || "image"],
- sizeType: ["compressed", "original"],
- sourceType: ["camera"],
- success: res => {
- try {
- const files = res?.tempFiles || [];
- if (!files.length) throw new Error("鏈幏鍙栧埌鏂囦欢");
-
- files.forEach((tf, idx) => {
- const filePath = tf.tempFilePath || tf.path || "";
- const fileType = tf.fileType || type || "image";
- const ext = fileType === "video" ? "mp4" : "jpg";
- const file = {
- tempFilePath: filePath,
- path: filePath,
- type: fileType,
- name: `${fileType}_${Date.now()}_${idx}.${ext}`,
- size: tf.size || 0,
- duration: tf.duration || 0,
- createTime: Date.now(),
- uid: Date.now() + Math.random() + idx,
- };
- handleBeforeUpload(file);
- });
- } catch (e) {
- console.error("澶勭悊鎷嶆憚缁撴灉澶辫触:", e);
- uni.showToast({ title: "澶勭悊鏂囦欢澶辫触", icon: "error" });
- }
- },
- fail: err => {
- console.error("鎷嶆憚澶辫触:", err);
- uni.showToast({ title: "鎷嶆憚澶辫触", icon: "error" });
- },
- });
- return;
- }
-
- // 闄嶇骇锛歝hooseImage / chooseVideo
- if (type === "video") {
- chooseVideo();
- } else {
- uni.chooseImage({
- count: 1,
- sizeType: ["compressed", "original"],
- sourceType: ["camera"],
- success: res => {
- const tempFilePath = res?.tempFilePaths?.[0];
- const tempFile = res?.tempFiles?.[0] || {};
- if (!tempFilePath) return;
- handleBeforeUpload({
- tempFilePath,
- path: tempFilePath,
- type: "image",
- name: `photo_${Date.now()}.jpg`,
- size: tempFile.size || 0,
- createTime: Date.now(),
- uid: Date.now() + Math.random(),
- });
- },
- });
- }
- };
-
- // 鎷嶇収
- const chooseImage = () => {
- if (uploadFiles.value.length >= uploadConfig.limit) {
- uni.showToast({
- title: `鏈�澶氬彧鑳芥媿鎽�${uploadConfig.limit}涓枃浠禶,
- icon: "none",
- });
- return;
- }
-
- uni.chooseMedia({
- count: 1,
- mediaType: ["image", "video"],
- sizeType: ["compressed", "original"],
- sourceType: ["camera"],
- success: res => {
- try {
- if (!res.tempFiles || res.tempFiles.length === 0) {
- throw new Error("鏈幏鍙栧埌鍥剧墖鏂囦欢");
- }
-
- const tempFilePath = res.tempFiles[0];
- const tempFile =
- res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {};
-
- const file = {
- tempFilePath: tempFilePath,
- path: tempFilePath, // 淇濇寔鍏煎鎬�
- type: "image",
- name: `photo_${Date.now()}.jpg`,
- size: tempFile.size || 0,
- createTime: new Date().getTime(),
- uid: Date.now() + Math.random(),
- };
-
- handleBeforeUpload(file);
- } catch (error) {
- console.error("澶勭悊鎷嶇収缁撴灉澶辫触:", error);
- uni.showToast({
- title: "澶勭悊鍥剧墖澶辫触",
- icon: "error",
- });
- }
- },
- fail: err => {
- console.error("鎷嶇収澶辫触:", err);
- uni.showToast({
- title: "鎷嶇収澶辫触: " + (err.errMsg || "鏈煡閿欒"),
- icon: "error",
- });
- },
- });
- };
-
- // 鎷嶈棰�
- const chooseVideo = () => {
- if (uploadFiles.value.length >= uploadConfig.limit) {
- uni.showToast({
- title: `鏈�澶氬彧鑳芥媿鎽�${uploadConfig.limit}涓枃浠禶,
- icon: "none",
- });
- return;
- }
-
- uni.chooseVideo({
- sourceType: ["camera"],
- maxDuration: uploadConfig.maxVideoDuration,
- camera: "back",
- success: res => {
- try {
- if (!res.tempFilePath) {
- throw new Error("鏈幏鍙栧埌瑙嗛鏂囦欢");
- }
-
- const file = {
- tempFilePath: res.tempFilePath,
- path: res.tempFilePath, // 淇濇寔鍏煎鎬�
- type: "video",
- name: `video_${Date.now()}.mp4`,
- size: res.size || 0,
- duration: res.duration || 0,
- createTime: new Date().getTime(),
- uid: Date.now() + Math.random(),
- };
-
- handleBeforeUpload(file);
- } catch (error) {
- console.error("澶勭悊鎷嶈棰戠粨鏋滃け璐�:", error);
- uni.showToast({
- title: "澶勭悊瑙嗛澶辫触",
- icon: "error",
- });
- }
- },
- fail: err => {
- console.error("鎷嶈棰戝け璐�:", err);
- uni.showToast({
- title: "鎷嶈棰戝け璐�: " + (err.errMsg || "鏈煡閿欒"),
- icon: "error",
- });
- },
- });
- };
-
- // 鍒犻櫎鏂囦欢
- const removeFile = index => {
- uni.showModal({
- title: "纭鍒犻櫎",
- content: "纭畾瑕佸垹闄よ繖涓枃浠跺悧锛�",
- success: res => {
- if (res.confirm) {
- // 鏍规嵁褰撳墠涓婁紶绫诲瀷鍒犻櫎瀵瑰簲鍒嗙被鐨勬枃浠�
- switch (currentUploadType.value) {
- case "before":
- beforeModelValue.value.splice(index, 1);
- break;
- case "after":
- afterModelValue.value.splice(index, 1);
- break;
- case "issue":
- issueModelValue.value.splice(index, 1);
- break;
- }
- uni.showToast({
- title: "鍒犻櫎鎴愬姛",
- icon: "success",
- });
- }
- },
- });
- };
-
- // 妫�鏌ョ綉缁滆繛鎺�
- const checkNetworkConnection = () => {
- return new Promise(resolve => {
- uni.getNetworkType({
- success: res => {
- if (res.networkType === "none") {
- resolve(false);
- } else {
- resolve(true);
- }
- },
- fail: () => {
- resolve(false);
- },
- });
- });
- };
-
- // 涓婁紶鍓嶆牎楠�
- const handleBeforeUpload = async file => {
- // 鏍¢獙鏂囦欢绫诲瀷
- if (
- uploadConfig.fileType &&
- Array.isArray(uploadConfig.fileType) &&
- uploadConfig.fileType.length > 0
- ) {
- const fileName = file.name || "";
- const fileExtension = fileName
- ? fileName.split(".").pop().toLowerCase()
- : "";
-
- // 鏍规嵁鏂囦欢绫诲瀷纭畾鏈熸湜鐨勬墿灞曞悕
- let expectedTypes = [];
- if (file.type === "image") {
- expectedTypes = ["jpg", "jpeg", "png", "gif", "webp"];
- } else if (file.type === "video") {
- expectedTypes = ["mp4", "mov", "avi", "wmv"];
- }
-
- // 妫�鏌ユ枃浠舵墿灞曞悕鏄惁鍦ㄥ厑璁哥殑绫诲瀷涓�
- if (fileExtension && expectedTypes.length > 0) {
- const isAllowed = expectedTypes.some(
- type => uploadConfig.fileType.includes(type) && type === fileExtension
- );
-
- if (!isAllowed) {
- uni.showToast({
- title: `鏂囦欢鏍煎紡涓嶆敮鎸侊紝璇锋媿鎽� ${expectedTypes.join("/")} 鏍煎紡鐨勬枃浠禶,
- icon: "none",
- });
- return false;
- }
- }
- }
-
- // 鏍¢獙閫氳繃锛屽紑濮嬩笂浼�
- uploadFile(file);
- return true;
- };
-
- // 鏂囦欢涓婁紶澶勭悊锛堢湡鏈鸿蛋 uni.uploadFile锛�
- const uploadFile = async file => {
- uploading.value = true;
- uploadProgress.value = 0;
- number.value++; // 澧炲姞涓婁紶璁℃暟
-
- // 纭繚token瀛樺湪
- const token = getToken();
- if (!token) {
- handleUploadError("鐢ㄦ埛鏈櫥褰�");
- return;
- }
-
- const typeValue = getTabType(); // 鐢熶骇鍓�:10, 鐢熶骇涓�:11, 鐢熶骇鍚�:12
-
- uploadWithUniUploadFile(
- file,
- file.tempFilePath || file.path || "",
- typeValue,
- token
- );
- };
-
- // 浣跨敤uni.uploadFile涓婁紶锛堥潪H5鐜鎴朒5鍥為��鏂规锛�
- const uploadWithUniUploadFile = (file, filePath, typeValue, token) => {
- if (!filePath) {
- handleUploadError("鏂囦欢璺緞涓嶅瓨鍦�");
- return;
- }
-
- const uploadTask = uni.uploadFile({
- url: uploadFileUrl.value,
- filePath: filePath,
- name: "file",
- formData: {
- type: typeValue,
- },
- header: {
- Authorization: `Bearer ${token}`,
- },
- success: res => {
- try {
- if (res.statusCode === 200) {
- const response = JSON.parse(res.data);
- if (response.code === 200) {
- handleUploadSuccess(response, file);
- uni.showToast({
- title: "涓婁紶鎴愬姛",
- icon: "success",
- });
- } else {
- handleUploadError(response.msg || "鏈嶅姟鍣ㄨ繑鍥為敊璇�");
- }
- } else {
- handleUploadError(`鏈嶅姟鍣ㄩ敊璇紝鐘舵�佺爜: ${res.statusCode}`);
- }
- } catch (e) {
- console.error("瑙f瀽鍝嶅簲澶辫触:", e);
- console.error("鍘熷鍝嶅簲鏁版嵁:", res.data);
- handleUploadError("鍝嶅簲鏁版嵁瑙f瀽澶辫触: " + e.message);
- }
- },
- fail: err => {
- console.error("涓婁紶澶辫触:", err.errMsg || err);
- number.value--; // 涓婁紶澶辫触鏃跺噺灏戣鏁�
-
- let errorMessage = "涓婁紶澶辫触";
- if (err.errMsg) {
- if (err.errMsg.includes("statusCode: null")) {
- errorMessage = "缃戠粶杩炴帴澶辫触锛岃妫�鏌ョ綉缁滆缃�";
- } else if (err.errMsg.includes("timeout")) {
- errorMessage = "涓婁紶瓒呮椂锛岃閲嶈瘯";
- } else if (err.errMsg.includes("fail")) {
- errorMessage = "涓婁紶澶辫触锛岃妫�鏌ョ綉缁滆繛鎺�";
- } else {
- errorMessage = err.errMsg;
- }
- }
-
- handleUploadError(errorMessage);
- },
- complete: () => {
- uploading.value = false;
- uploadProgress.value = 0;
- },
- });
-
- // 鐩戝惉涓婁紶杩涘害
- if (uploadTask && uploadTask.onProgressUpdate) {
- uploadTask.onProgressUpdate(res => {
- uploadProgress.value = res.progress;
- });
- }
- };
-
- // 涓婁紶澶辫触澶勭悊
- const handleUploadError = (message = "涓婁紶鏂囦欢澶辫触", showRetry = false) => {
- uploading.value = false;
- uploadProgress.value = 0;
-
- if (showRetry) {
- uni.showModal({
- title: "涓婁紶澶辫触",
- content: message + "锛屾槸鍚﹂噸璇曪紵",
- success: res => {
- if (res.confirm) {
- // 鐢ㄦ埛閫夋嫨閲嶈瘯锛岃繖閲屽彲浠ラ噸鏂拌Е鍙戜笂浼�
- }
- },
- });
- } else {
- uni.showToast({
- title: message,
- icon: "error",
- });
- }
- };
-
- // 涓婁紶鎴愬姛鍥炶皟
- const handleUploadSuccess = (res, file) => {
- console.log("涓婁紶鎴愬姛鍝嶅簲:", res);
-
- // 澶勭悊涓嶅悓鐨勬暟鎹粨鏋勶細鍙兘鏄暟缁勶紝涔熷彲鑳芥槸鍗曚釜瀵硅薄
- let uploadedFile = null;
- uploadedFile = res.data;
-
- if (!uploadedFile) {
- console.error("鏃犳硶瑙f瀽涓婁紶鍝嶅簲鏁版嵁:", res);
- number.value--; // 涓婁紶澶辫触鏃跺噺灏戣鏁�
- handleUploadError("涓婁紶鍝嶅簲鏁版嵁鏍煎紡閿欒", false);
- return;
- }
-
- // 鏍规嵁褰撳墠涓婁紶绫诲瀷璁剧疆type瀛楁
- let typeValue = 0; // 榛樿涓虹敓浜у墠
- switch (currentUploadType.value) {
- case "before":
- typeValue = 0;
- break;
- case "after":
- typeValue = 1;
- break;
- case "issue":
- typeValue = 2;
- break;
- }
-
- // 纭繚涓婁紶鐨勬枃浠舵暟鎹畬鏁达紝鍖呭惈id鍜宼ype
- const fileData = {
- ...file,
- id: uploadedFile.id, // 娣诲姞鏈嶅姟鍣ㄨ繑鍥炵殑id
- tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id,
- url:
- uploadedFile.url ||
- uploadedFile.downloadUrl ||
- file.tempFilePath ||
- file.path,
- bucketFilename:
- uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name,
- downloadUrl: uploadedFile.downloadUrl || uploadedFile.url,
- size: uploadedFile.size || uploadedFile.byteSize || file.size,
- createTime: uploadedFile.createTime || new Date().getTime(),
- type: typeValue, // 娣诲姞绫诲瀷瀛楁锛�0=鐢熶骇鍓�, 1=鐢熶骇涓�, 2=鐢熶骇鍚�
- };
-
- uploadList.value.push(fileData);
-
- // 绔嬪嵆娣诲姞鍒板搴旂殑鍒嗙被锛屼笉绛夊緟鎵�鏈夋枃浠朵笂浼犲畬鎴�
- switch (currentUploadType.value) {
- case "before":
- beforeModelValue.value.push(fileData);
- break;
- case "after":
- afterModelValue.value.push(fileData);
- break;
- case "issue":
- issueModelValue.value.push(fileData);
- break;
- }
-
- // 閲嶇疆涓婁紶鍒楄〃锛堝洜涓哄凡缁忔坊鍔犲埌瀵瑰簲鍒嗙被浜嗭級
- uploadList.value = [];
- number.value = 0;
- };
-
- // 涓婁紶缁撴潫澶勭悊锛堝凡搴熷純锛岀幇鍦ㄥ湪handleUploadSuccess涓洿鎺ュ鐞嗭級
- const uploadedSuccessfully = () => {
- // 姝ゅ嚱鏁板凡涓嶅啀浣跨敤锛屾枃浠朵笂浼犳垚鍔熷悗绔嬪嵆娣诲姞鍒板搴斿垎绫�
- };
-
- // 鏍煎紡鍖栨枃浠跺ぇ灏�
- const formatFileSize = size => {
- if (!size) return "";
- if (size < 1024) return size + "B";
- if (size < 1024 * 1024) return (size / 1024).toFixed(1) + "KB";
- return (size / (1024 * 1024)).toFixed(1) + "MB";
};
</script>
@@ -1725,416 +691,6 @@
display: flex;
align-items: center;
justify-content: center;
- }
-
- /* 涓婁紶寮圭獥鏍峰紡 */
- .upload-popup-content {
- background: #fff;
- border-radius: 12px;
- width: 100%;
- min-height: 300px;
- max-height: 70vh;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
- }
-
- .upload-popup-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15px 20px;
- border-bottom: 1px solid #eee;
- }
-
- .upload-popup-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- }
-
- .upload-popup-body {
- flex: 1;
- padding: 20px;
- overflow-y: auto;
- }
-
- .upload-popup-footer {
- display: flex;
- justify-content: flex-end;
- padding: 15px 20px;
- border-top: 1px solid #eee;
- gap: 10px;
- }
-
- /* 绠�鍖栦笂浼犵粍浠舵牱寮� */
- .simple-upload-area {
- padding: 15px;
- }
-
- .upload-buttons {
- display: flex;
- gap: 10px;
- margin-bottom: 15px;
- }
-
- .file-list {
- margin-top: 15px;
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
- }
-
- .file-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- background: #fff;
- border-radius: 12px;
- padding: 8px;
- border: 1px solid #e9ecef;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
- transition: all 0.3s ease;
- width: calc(50% - 6px);
- min-width: 120px;
- }
-
- .file-item:hover {
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
- transform: translateY(-2px);
- }
-
- .file-preview-container {
- position: relative;
- margin-bottom: 8px;
- }
-
- .file-preview {
- width: 80px;
- height: 80px;
- border-radius: 8px;
- object-fit: cover;
- border: 2px solid #f0f0f0;
- }
-
- .video-preview {
- width: 80px;
- height: 80px;
- background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
- border-radius: 8px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- border: 2px solid #f0f0f0;
- }
-
- .video-text {
- font-size: 12px;
- color: #666;
- margin-top: 4px;
- }
-
- .delete-btn {
- position: absolute;
- top: -6px;
- right: -6px;
- width: 20px;
- height: 20px;
- background: #ff4757;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- box-shadow: 0 2px 4px rgba(255, 71, 87, 0.3);
- transition: all 0.3s ease;
- }
-
- .delete-btn:hover {
- background: #ff3742;
- transform: scale(1.1);
- }
-
- .file-info {
- text-align: center;
- width: 100%;
- }
-
- .file-name {
- font-size: 12px;
- color: #333;
- font-weight: 500;
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 100px;
- }
-
- .file-size {
- font-size: 10px;
- color: #999;
- margin-top: 2px;
- display: block;
- }
-
- .empty-state {
- text-align: center;
- padding: 40px 20px;
- color: #999;
- font-size: 14px;
- background: #f8f9fa;
- border-radius: 8px;
- border: 2px dashed #ddd;
- }
-
- .upload-progress {
- margin: 15px 0;
- padding: 0 10px;
- }
-
- /* 涓婁紶鏍囩椤垫牱寮� */
- .upload-tabs {
- display: flex;
- background: #f8f9fa;
- border-radius: 8px;
- margin-bottom: 15px;
- padding: 4px;
- }
-
- .tab-item {
- flex: 1;
- text-align: center;
- padding: 8px 12px;
- font-size: 14px;
- color: #666;
- border-radius: 6px;
- transition: all 0.3s ease;
- cursor: pointer;
- }
-
- .tab-item.active {
- background: #409eff;
- color: #fff;
- font-weight: 500;
- }
-
- .tab-item:hover:not(.active) {
- background: #e9ecef;
- color: #333;
- }
-
- /* 寮傚父鐘舵�侀�夋嫨鏍峰紡 */
- .exception-section {
- margin-bottom: 20px;
- padding: 15px;
- background: #f8f9fa;
- border-radius: 8px;
- border: 1px solid #e9ecef;
- }
-
- .section-title {
- display: block;
- font-size: 14px;
- font-weight: 600;
- color: #333;
- margin-bottom: 12px;
- }
-
- .exception-options {
- display: flex;
- gap: 12px;
- }
-
- .exception-option {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- padding: 12px 16px;
- background: #fff;
- border: 2px solid #e9ecef;
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.3s ease;
- font-size: 14px;
- color: #666;
- }
-
- .exception-option.active {
- border-color: #409eff;
- background: #f0f8ff;
- color: #409eff;
- font-weight: 500;
- }
-
- .exception-option:hover:not(.active) {
- border-color: #d9d9d9;
- background: #fafafa;
- }
-
- /* 缁熻淇℃伅鏍峰紡 */
- .upload-summary {
- margin-top: 15px;
- padding: 10px;
- background: #f8f9fa;
- border-radius: 6px;
- border-left: 3px solid #409eff;
- }
-
- .summary-text {
- font-size: 12px;
- color: #666;
- line-height: 1.4;
- }
-
- /* 鏌ョ湅闄勪欢寮圭獥鏍峰紡 */
- .attachment-popup-content {
- background: #fff;
- border-radius: 12px;
- width: 100%;
- min-height: 400px;
- max-height: 70vh;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
- }
-
- .attachment-popup-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15px 20px;
- border-bottom: 1px solid #eee;
- background: #f8f9fa;
- }
-
- .attachment-popup-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- }
-
- .close-btn-attachment {
- width: 28px;
- height: 28px;
- border-radius: 50%;
- background: #f5f5f5;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- transition: all 0.3s ease;
- }
-
- .close-btn-attachment:hover {
- background: #e9ecef;
- transform: scale(1.1);
- }
-
- .attachment-popup-body {
- flex: 1;
- padding: 15px 20px;
- overflow-y: auto;
- }
-
- .attachment-tabs {
- display: flex;
- background: #f8f9fa;
- border-radius: 8px;
- margin-bottom: 15px;
- padding: 4px;
- }
-
- .attachment-content {
- min-height: 200px;
- }
-
- .attachment-list {
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
- }
-
- .attachment-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- background: #fff;
- border-radius: 12px;
- padding: 8px;
- border: 1px solid #e9ecef;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
- transition: all 0.3s ease;
- width: calc(33.33% - 8px);
- min-width: 100px;
- cursor: pointer;
- }
-
- .attachment-item:hover {
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
- transform: translateY(-2px);
- }
-
- .attachment-preview-container {
- margin-bottom: 8px;
- }
-
- .attachment-preview {
- width: 80px;
- height: 80px;
- border-radius: 8px;
- object-fit: cover;
- border: 2px solid #f0f0f0;
- }
-
- .attachment-video-preview {
- width: 80px;
- height: 80px;
- background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
- border-radius: 8px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- border: 2px solid #f0f0f0;
- }
-
- .attachment-info {
- text-align: center;
- width: 100%;
- }
-
- .attachment-name {
- font-size: 12px;
- color: #333;
- font-weight: 500;
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 80px;
- }
-
- .attachment-size {
- font-size: 10px;
- color: #999;
- margin-top: 2px;
- display: block;
- }
-
- .attachment-empty {
- text-align: center;
- padding: 60px 20px;
- color: #999;
- font-size: 14px;
- background: #f8f9fa;
- border-radius: 8px;
- border: 2px dashed #ddd;
}
/* 瑙嗛棰勮寮圭獥鏍峰紡 */
diff --git a/src/pages/inspectionUpload/upload.vue b/src/pages/inspectionUpload/upload.vue
new file mode 100644
index 0000000..6d95785
--- /dev/null
+++ b/src/pages/inspectionUpload/upload.vue
@@ -0,0 +1,982 @@
+<template>
+ <view class="inspection-upload-page">
+ <!-- 椤甸潰澶撮儴 -->
+ <PageHeader title="涓婁紶宸℃璁板綍"
+ @back="goBack" />
+ <!-- 椤甸潰鍐呭 -->
+ <view class="upload-content">
+ <!-- 浠诲姟淇℃伅鍗$墖 -->
+ <view class="task-info-card"
+ v-if="taskInfo">
+ <view class="task-info-header">
+ <text class="task-name">{{ taskInfo.taskName }}</text>
+ </view>
+ <view class="task-info-body">
+ <view class="info-item">
+ <text class="info-label">浠诲姟ID</text>
+ <text class="info-value">{{ taskInfo.taskId || taskInfo.id }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">宸℃浣嶇疆</text>
+ <text class="info-value">{{ taskInfo.inspectionLocation || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鎵ц浜�</text>
+ <text class="info-value">{{ taskInfo.inspector || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ <!-- 寮傚父鐘舵�侀�夋嫨 -->
+ <view class="section-card">
+ <view class="section-title">宸℃鐘舵��</view>
+ <view class="exception-options">
+ <view class="exception-option"
+ :class="{ active: hasException === false }"
+ @click="setExceptionStatus(false)">
+ <u-icon name="checkmark-circle"
+ size="20"
+ color="#52c41a"></u-icon>
+ <text class="option-text">姝e父</text>
+ </view>
+ <view class="exception-option"
+ :class="{ active: hasException === true }"
+ @click="setExceptionStatus(true)">
+ <u-icon name="close-circle"
+ size="20"
+ color="#ff4d4f"></u-icon>
+ <text class="option-text">瀛樺湪寮傚父</text>
+ </view>
+ </view>
+ </view>
+ <!-- 寮傚父鎻忚堪锛堜粎鍦ㄥ紓甯告椂鏄剧ず锛� -->
+ <view class="section-card"
+ v-if="hasException === true">
+ <view class="section-title">寮傚父鎻忚堪</view>
+ <textarea v-model="abnormalDescription"
+ class="exception-textarea"
+ maxlength="500"
+ placeholder="璇锋弿杩板紓甯告儏鍐�..." />
+ </view>
+ <!-- 鍒嗙被鏍囩椤碉紙浠呭湪寮傚父鏃舵樉绀猴級 -->
+ <view class="section-card"
+ v-if="hasException === true">
+ <view class="upload-tabs">
+ <view class="tab-item"
+ :class="{ active: currentUploadType === 'before' }"
+ @click="switchUploadType('before')">
+ 鐢熶骇鍓�
+ </view>
+ <view class="tab-item"
+ :class="{ active: currentUploadType === 'after' }"
+ @click="switchUploadType('after')">
+ 鐢熶骇涓�
+ </view>
+ <view class="tab-item"
+ :class="{ active: currentUploadType === 'issue' }"
+ @click="switchUploadType('issue')">
+ 鐢熶骇鍚�
+ </view>
+ </view>
+ <!-- 褰撳墠鍒嗙被鐨勪笂浼犲尯鍩� -->
+ <view class="upload-area">
+ <view class="upload-buttons">
+ <u-button type="primary"
+ @click="chooseMedia('image')"
+ :loading="uploading"
+ :disabled="getCurrentFiles().length >= uploadConfig.limit"
+ :customStyle="{ marginRight: '10px', flex: 1 }">
+ <u-icon name="camera"
+ size="18"
+ color="#fff"
+ style="margin-right: 5px"></u-icon>
+ {{ uploading ? '涓婁紶涓�...' : '鎷嶇収' }}
+ </u-button>
+ <u-button type="success"
+ @click="chooseMedia('video')"
+ :loading="uploading"
+ :disabled="getCurrentFiles().length >= uploadConfig.limit"
+ :customStyle="{ flex: 1 }">
+ <uni-icons type="videocam"
+ size="18"
+ color="#fff"
+ style="margin-right: 5px"></uni-icons>
+ {{ uploading ? '涓婁紶涓�...' : '鎷嶈棰�' }}
+ </u-button>
+ </view>
+ <!-- 涓婁紶杩涘害 -->
+ <view v-if="uploading"
+ class="upload-progress">
+ <u-line-progress :percentage="uploadProgress"
+ :showText="true"
+ activeColor="#409eff"></u-line-progress>
+ </view>
+ <!-- 褰撳墠鍒嗙被鐨勬枃浠跺垪琛� -->
+ <view v-if="getCurrentFiles().length > 0"
+ class="file-list">
+ <view v-for="(file, index) in getCurrentFiles()"
+ :key="index"
+ class="file-item">
+ <view class="file-preview-container">
+ <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)"
+ :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
+ class="file-preview"
+ mode="aspectFill" />
+ <view v-else-if="file.type === 'video'"
+ class="video-preview">
+ <uni-icons type="videocam"
+ size="18"
+ color="#fff"
+ style="margin-right: 5px"></uni-icons>
+ <text class="video-text">瑙嗛</text>
+ </view>
+ <!-- 鍒犻櫎鎸夐挳 -->
+ <view class="delete-btn"
+ @click="removeFile(index)">
+ <u-icon name="close"
+ size="12"
+ color="#fff"></u-icon>
+ </view>
+ </view>
+ <view class="file-info">
+ <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? '鍥剧墖' : '瑙嗛') }}</text>
+ <text class="file-size">{{ formatFileSize(file.size) }}</text>
+ </view>
+ </view>
+ </view>
+ <view v-if="getCurrentFiles().length === 0"
+ class="empty-state">
+ <text>璇烽�夋嫨瑕佷笂浼犵殑{{ getUploadTypeText() }}鍥剧墖鎴栬棰�</text>
+ </view>
+ </view>
+ <!-- 缁熻淇℃伅 -->
+ <view class="upload-summary">
+ <text class="summary-text">
+ 鐢熶骇鍓�: {{ beforeModelValue.length }}涓枃浠� |
+ 鐢熶骇涓�: {{ afterModelValue.length }}涓枃浠� |
+ 鐢熶骇鍚�: {{ issueModelValue.length }}涓枃浠�
+ </text>
+ </view>
+ </view>
+ <!-- 姝e父鐘舵�佹彁绀� -->
+ <view class="normal-tip-card"
+ v-if="hasException === false">
+ <u-icon name="info-circle"
+ size="60"
+ color="#52c41a"></u-icon>
+ <text class="tip-text">璁惧杩愯姝e父锛屾棤闇�涓婁紶鐓х墖</text>
+ </view>
+ </view>
+ <!-- 搴曢儴鎸夐挳 -->
+ <view class="footer-buttons">
+ <u-button @click="goBack"
+ :customStyle="{ marginRight: '10px' }">鍙栨秷</u-button>
+ <u-button v-if="hasException === true"
+ type="warning"
+ @click="goToRepair"
+ :customStyle="{ marginRight: '10px' }">
+ 鏂板鎶ヤ慨
+ </u-button>
+ <u-button type="primary"
+ @click="submitUpload">鎻愪氦</u-button>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, computed, onMounted } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import { uploadInspectionTask } from "@/api/inspectionManagement";
+ import { getToken } from "@/utils/auth";
+ import config from "@/config";
+
+ // 浠诲姟淇℃伅
+ const taskInfo = ref(null);
+
+ // 涓婁紶鐩稿叧鐘舵��
+ const uploading = ref(false);
+ const uploadProgress = ref(0);
+
+ // 涓変釜鍒嗙被鐨勪笂浼犵姸鎬�
+ const beforeModelValue = ref([]); // 鐢熶骇鍓�
+ const afterModelValue = ref([]); // 鐢熶骇涓�
+ const issueModelValue = ref([]); // 鐢熶骇鍚�
+
+ // 褰撳墠婵�娲荤殑涓婁紶绫诲瀷
+ const currentUploadType = ref("before"); // 'before', 'after', 'issue'
+
+ // 寮傚父鐘舵��
+ const hasException = ref(null); // null: 鏈�夋嫨, true: 瀛樺湪寮傚父, false: 姝e父
+ // 寮傚父鎻忚堪
+ const abnormalDescription = ref("");
+
+ // 涓婁紶閰嶇疆
+ const uploadConfig = {
+ action: "/common/upload",
+ limit: 10,
+ fileSize: 50, // MB
+ fileType: ["jpg", "jpeg", "png", "mp4", "mov"],
+ maxVideoDuration: 60, // 绉�
+ };
+
+ // 璁$畻涓婁紶URL
+ const uploadFileUrl = computed(() => {
+ const baseUrl = config.baseUrl;
+ return baseUrl + uploadConfig.action;
+ });
+
+ // 椤甸潰鍔犺浇
+ onLoad(options => {
+ if (options.taskInfo) {
+ try {
+ const info = JSON.parse(decodeURIComponent(options.taskInfo));
+ taskInfo.value = info;
+
+ // 鍥炴樉閫昏緫锛氫粠 taskInfo 涓仮澶嶅凡涓婁紶鐨勬枃浠�
+ const mapFiles = list => {
+ if (!list || !Array.isArray(list)) return [];
+ return list.map(item => {
+ // 澶勭悊 URL锛屽幓闄ゅ彲鑳界殑绌烘牸
+ const finalUrl = (item.url || item.previewURL || "").trim();
+ // 鑷姩鎺ㄦ柇鏂囦欢绫诲瀷
+ let fileType = item.type;
+ if (!fileType && item.contentType) {
+ fileType = item.contentType.startsWith("video") ? "video" : "image";
+ } else if (!fileType) {
+ fileType = "image"; // 榛樿鍥剧墖
+ }
+
+ return {
+ ...item,
+ url: finalUrl,
+ name: item.name || item.originalFilename,
+ tempId: item.tempId || item.id || item.tempFileId,
+ size: item.size || item.byteSize || 0, // 鏄犲皠澶у皬瀛楁
+ type: fileType,
+ status: "success",
+ };
+ });
+ };
+
+ // 鏍规嵁鐢ㄦ埛瑕佹眰鏄犲皠锛欰fterDTO(鐢熶骇鍓�), DTO(鐢熶骇涓�), BeforeDTO(鐢熶骇鍚�)
+ if (
+ info.commonFileListAfterVO &&
+ Array.isArray(info.commonFileListAfterVO)
+ ) {
+ beforeModelValue.value = mapFiles(info.commonFileListAfterVO);
+ }
+ console.log(beforeModelValue.value, "beforeModelValue");
+
+ if (info.commonFileListVO && Array.isArray(info.commonFileListVO)) {
+ afterModelValue.value = mapFiles(info.commonFileListVO);
+ }
+ if (
+ info.commonFileListBeforeVO &&
+ Array.isArray(info.commonFileListBeforeVO)
+ ) {
+ issueModelValue.value = mapFiles(info.commonFileListBeforeVO);
+ }
+
+ // 濡傛灉鏈夊紓甯告弿杩帮紝涔熸仮澶�
+ if (info.abnormalDescription) {
+ abnormalDescription.value = info.abnormalDescription;
+ }
+ // 濡傛灉鏈夊紓甯哥姸鎬侊紝涔熸仮澶�
+ if (info.hasException !== undefined && info.hasException !== null) {
+ hasException.value = info.hasException;
+ } else if (
+ info.inspectionResult !== undefined &&
+ info.inspectionResult !== null
+ ) {
+ // 0-寮傚父锛�1-姝e父
+ hasException.value = String(info.inspectionResult) === "0";
+ }
+
+ // 鑷姩鍏滃簳锛氬鏋滃瓨鍦ㄥ凡涓婁紶鏂囦欢锛屽垯蹇呯劧鏄紓甯哥姸鎬侊紝纭繚 UI 姝e父鏄剧ず
+ if (
+ !hasException.value &&
+ (beforeModelValue.value.length > 0 ||
+ afterModelValue.value.length > 0 ||
+ issueModelValue.value.length > 0)
+ ) {
+ hasException.value = true;
+ }
+ } catch (e) {
+ console.error("瑙f瀽浠诲姟淇℃伅澶辫触:", e);
+ }
+ }
+ });
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鍒囨崲涓婁紶绫诲瀷
+ const switchUploadType = type => {
+ currentUploadType.value = type;
+ };
+
+ // 鑾峰彇褰撳墠鍒嗙被鐨勬枃浠跺垪琛�
+ const getCurrentFiles = () => {
+ switch (currentUploadType.value) {
+ case "before":
+ return beforeModelValue.value || [];
+ case "after":
+ return afterModelValue.value || [];
+ case "issue":
+ return issueModelValue.value || [];
+ default:
+ return [];
+ }
+ };
+
+ // 鑾峰彇涓婁紶绫诲瀷鏂囨湰
+ const getUploadTypeText = () => {
+ switch (currentUploadType.value) {
+ case "before":
+ return "鐢熶骇鍓�";
+ case "after":
+ return "鐢熶骇涓�";
+ case "issue":
+ return "鐢熶骇鍚�";
+ default:
+ return "";
+ }
+ };
+
+ // 璁剧疆寮傚父鐘舵��
+ const setExceptionStatus = status => {
+ hasException.value = status;
+ };
+
+ // 璺宠浆鍒版柊澧炴姤淇〉闈�
+ const goToRepair = () => {
+ try {
+ const taskData = {
+ taskId: taskInfo.value?.taskId || taskInfo.value?.id,
+ taskName: taskInfo.value?.taskName,
+ inspectionLocation: taskInfo.value?.inspectionLocation,
+ inspector: taskInfo.value?.inspector,
+ hasException: hasException.value,
+ inspectionResult: hasException.value ? 0 : 1, // 0-寮傚父锛�1-姝e父
+ commonFileListAfterDTO: beforeModelValue.value,
+ commonFileListDTO: afterModelValue.value,
+ commonFileListBeforeDTO: issueModelValue.value,
+ uploadedFiles: {
+ before: beforeModelValue.value,
+ after: afterModelValue.value,
+ issue: issueModelValue.value,
+ },
+ };
+
+ uni.setStorageSync("repairTaskInfo", JSON.stringify(taskData));
+
+ uni.navigateTo({
+ url: "/pages/equipmentManagement/repair/add",
+ });
+ } catch (error) {
+ console.error("璺宠浆鎶ヤ慨椤甸潰澶辫触:", error);
+ uni.showToast({
+ title: "璺宠浆澶辫触锛岃閲嶈瘯",
+ icon: "error",
+ });
+ }
+ };
+
+ // 鎻愪氦涓婁紶
+ const submitUpload = async () => {
+ try {
+ // 妫�鏌ユ槸鍚﹂�夋嫨浜嗗紓甯哥姸鎬�
+ if (hasException.value === null) {
+ uni.showToast({
+ title: "璇烽�夋嫨宸℃鐘舵��",
+ icon: "none",
+ });
+ return;
+ }
+
+ // 濡傛灉鏄紓甯哥姸鎬侊紝妫�鏌ユ槸鍚︽湁涓婁紶鏂囦欢鍜屾弿杩�
+ if (hasException.value === true) {
+ const totalFiles =
+ beforeModelValue.value.length +
+ afterModelValue.value.length +
+ issueModelValue.value.length;
+ if (totalFiles === 0) {
+ uni.showToast({
+ title: "璇蜂笂浼犲紓甯哥収鐗�",
+ icon: "none",
+ });
+ return;
+ }
+ // 妫�鏌ユ槸鍚﹀~鍐欎簡寮傚父鎻忚堪
+ if (!abnormalDescription.value.trim()) {
+ uni.showToast({
+ title: "璇峰~鍐欏紓甯告弿杩�",
+ icon: "none",
+ });
+ return;
+ }
+ }
+
+ // 鏄剧ず鎻愪氦涓殑鍔犺浇鎻愮ず
+ uni.showLoading({
+ title: "鎻愪氦涓�...",
+ mask: true,
+ });
+
+ // 鎸夌収閫昏緫鍚堝苟鎵�鏈夊垎绫荤殑鏂囦欢鐢ㄤ簬鎻愬彇ID
+ const allFiles = [
+ ...beforeModelValue.value,
+ ...afterModelValue.value,
+ ...issueModelValue.value,
+ ];
+
+ // 浼犵粰鍚庣鐨勪复鏃舵枃浠禝D鍒楄〃
+ let tempFileIds = [];
+ if (allFiles.length > 0) {
+ tempFileIds = allFiles
+ .map(item => item?.tempId ?? item?.tempFileId ?? item?.id)
+ .filter(v => v !== undefined && v !== null && v !== "");
+ }
+
+ // 鎻愪氦鏁版嵁
+ const submitData = {
+ ...taskInfo.value,
+ commonFileListAfterDTO: beforeModelValue.value, // 鐢熶骇鍓�
+ commonFileListDTO: afterModelValue.value, // 鐢熶骇涓�
+ commonFileListBeforeDTO: issueModelValue.value, // 鐢熶骇鍚�
+ hasException: hasException.value,
+ inspectionResult: hasException.value ? 0 : 1, // 0-寮傚父锛�1-姝e父
+ abnormalDescription: abnormalDescription.value,
+ tempFileIds: tempFileIds,
+ };
+
+ const result = await uploadInspectionTask(submitData);
+
+ // 妫�鏌ユ彁浜ょ粨鏋�
+ if (result && (result.code === 200 || result.success)) {
+ uni.hideLoading();
+ uni.showToast({
+ title: "鎻愪氦鎴愬姛",
+ icon: "success",
+ });
+
+ // 杩斿洖鍒楄〃椤靛苟鍒锋柊
+ setTimeout(() => {
+ uni.navigateBack();
+ }, 500);
+ } else {
+ uni.hideLoading();
+ uni.showToast({
+ title: result?.msg || result?.message || "鎻愪氦澶辫触",
+ icon: "error",
+ });
+ }
+ } catch (error) {
+ console.error("鎻愪氦涓婁紶澶辫触:", error);
+ uni.hideLoading();
+ uni.showToast({
+ title: error?.message || "鎻愪氦澶辫触",
+ icon: "error",
+ });
+ }
+ };
+
+ // 鏍煎紡鍖栨枃浠跺ぇ灏�
+ const formatFileSize = size => {
+ if (!size) return "0 B";
+ const units = ["B", "KB", "MB", "GB"];
+ let index = 0;
+ let fileSize = size;
+ while (fileSize >= 1024 && index < units.length - 1) {
+ fileSize /= 1024;
+ index++;
+ }
+ return `${fileSize.toFixed(2)} ${units[index]}`;
+ };
+
+ // 鎷嶇収/鎷嶈棰�
+ const chooseMedia = type => {
+ if (getCurrentFiles().length >= uploadConfig.limit) {
+ uni.showToast({
+ title: `鏈�澶氬彧鑳介�夋嫨${uploadConfig.limit}涓枃浠禶,
+ icon: "none",
+ });
+ return;
+ }
+
+ const remaining = uploadConfig.limit - getCurrentFiles().length;
+
+ // 浼樺厛浣跨敤 chooseMedia
+ if (typeof uni.chooseMedia === "function") {
+ uni.chooseMedia({
+ count: Math.min(remaining, 1),
+ mediaType: [type || "image"],
+ sizeType: ["compressed", "original"],
+ sourceType: ["camera"],
+ success: res => {
+ try {
+ const files = res?.tempFiles || [];
+ if (!files.length) throw new Error("鏈幏鍙栧埌鏂囦欢");
+
+ files.forEach((tf, idx) => {
+ const filePath = tf.tempFilePath || tf.path || "";
+ const fileType = tf.fileType || type || "image";
+ const ext = fileType === "video" ? "mp4" : "jpg";
+ const file = {
+ tempFilePath: filePath,
+ path: filePath,
+ type: fileType,
+ name: `${fileType}_${Date.now()}_${idx}.${ext}`,
+ size: tf.size || 0,
+ duration: tf.duration || 0,
+ createTime: Date.now(),
+ };
+ uploadFile(file);
+ });
+ } catch (err) {
+ uni.showToast({ title: err.message || "澶勭悊鏂囦欢澶辫触", icon: "none" });
+ }
+ },
+ fail: err => {
+ console.error("閫夋嫨濯掍綋澶辫触:", err);
+ uni.showToast({ title: "閫夋嫨澶辫触", icon: "none" });
+ },
+ });
+ } else {
+ // 闄嶇骇鏂规
+ if (type === "video") {
+ uni.chooseVideo({
+ sourceType: ["camera"],
+ success: res => {
+ const file = {
+ tempFilePath: res.tempFilePath,
+ path: res.tempFilePath,
+ type: "video",
+ name: `video_${Date.now()}.mp4`,
+ size: res.size || 0,
+ duration: res.duration || 0,
+ createTime: Date.now(),
+ };
+ uploadFile(file);
+ },
+ fail: () => {
+ uni.showToast({ title: "閫夋嫨瑙嗛澶辫触", icon: "none" });
+ },
+ });
+ } else {
+ uni.chooseImage({
+ count: Math.min(remaining, 9),
+ sizeType: ["compressed"],
+ sourceType: ["camera"],
+ success: res => {
+ const list = res.tempFilePaths || res.tempFiles || [];
+ list.forEach((src, idx) => {
+ const path = typeof src === "string" ? src : src.path;
+ const file = {
+ tempFilePath: path,
+ path: path,
+ type: "image",
+ name: `image_${Date.now()}_${idx}.jpg`,
+ size: 0,
+ createTime: Date.now(),
+ };
+ uploadFile(file);
+ });
+ },
+ fail: () => {
+ uni.showToast({ title: "閫夋嫨鍥剧墖澶辫触", icon: "none" });
+ },
+ });
+ }
+ }
+ };
+
+ // 涓婁紶鍗曚釜鏂囦欢
+ const uploadFile = file => {
+ const token = getToken();
+ if (!token) {
+ uni.showToast({ title: "鐢ㄦ埛鏈櫥褰�", icon: "none" });
+ return;
+ }
+
+ uploading.value = true;
+ uploadProgress.value = 0;
+
+ const uploadTask = uni.uploadFile({
+ url: uploadFileUrl.value,
+ filePath: file.tempFilePath,
+ name: "files",
+ header: {
+ Authorization: `Bearer ${token}`,
+ },
+ formData: {
+ type: getTabType(),
+ },
+ success: res => {
+ try {
+ const data = JSON.parse(res.data);
+ if (data.code === 200) {
+ // 鍏煎 CommonUpload.vue 鐨勫鐞嗛�昏緫
+ const resultData = Array.isArray(data.data)
+ ? data.data[0]
+ : data.data;
+
+ // 澶勭悊 url 鍜� name 璧嬪��
+ const finalUrl = resultData.url || resultData.previewURL;
+ const finalName = resultData.name || resultData.originalFilename;
+ const finalId =
+ resultData.tempId || resultData.id || resultData.tempFileId;
+
+ const uploadedFile = {
+ ...file,
+ ...resultData, // 鍖呭惈鍚庣杩斿洖鐨勬墍鏈夊瓧娈�
+ url: finalUrl,
+ name: finalName,
+ tempId: finalId,
+ status: "success",
+ };
+
+ // 鏍规嵁褰撳墠绫诲瀷娣诲姞鍒板搴旀暟缁�
+ if (currentUploadType.value === "before") {
+ beforeModelValue.value.push(uploadedFile);
+ } else if (currentUploadType.value === "after") {
+ afterModelValue.value.push(uploadedFile);
+ } else if (currentUploadType.value === "issue") {
+ issueModelValue.value.push(uploadedFile);
+ }
+
+ uni.showToast({ title: "涓婁紶鎴愬姛", icon: "success" });
+ } else {
+ uni.showToast({ title: data.msg || "涓婁紶澶辫触", icon: "none" });
+ }
+ } catch (e) {
+ uni.showToast({ title: "瑙f瀽鍝嶅簲澶辫触", icon: "none" });
+ }
+ },
+ fail: err => {
+ console.error("涓婁紶澶辫触:", err);
+ uni.showToast({ title: "涓婁紶澶辫触", icon: "none" });
+ },
+ complete: () => {
+ uploading.value = false;
+ },
+ });
+
+ // 鐩戝惉涓婁紶杩涘害
+ uploadTask.onProgressUpdate(res => {
+ uploadProgress.value = res.progress;
+ });
+ };
+
+ // 鑾峰彇type鍊�
+ const getTabType = () => {
+ switch (currentUploadType.value) {
+ case "before":
+ return 10;
+ case "after":
+ return 11;
+ case "issue":
+ return 12;
+ default:
+ return 10;
+ }
+ };
+
+ // 鍒犻櫎鏂囦欢
+ const removeFile = index => {
+ const files = getCurrentFiles();
+ files.splice(index, 1);
+ };
+</script>
+
+<style scoped>
+ .inspection-upload-page {
+ min-height: 100vh;
+ background-color: #f5f5f5;
+ padding-bottom: 80px;
+ }
+
+ .upload-content {
+ padding: 15px;
+ }
+
+ /* 浠诲姟淇℃伅鍗$墖 */
+ .task-info-card {
+ background: #fff;
+ border-radius: 12px;
+ padding: 15px;
+ margin-bottom: 15px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ }
+
+ .task-info-header {
+ margin-bottom: 12px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .task-name {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .task-info-body {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .info-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .info-label {
+ font-size: 13px;
+ color: #999;
+ }
+
+ .info-value {
+ font-size: 13px;
+ color: #666;
+ }
+
+ /* 閫氱敤鍗$墖鏍峰紡 */
+ .section-card {
+ background: #fff;
+ border-radius: 12px;
+ padding: 15px;
+ margin-bottom: 15px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ }
+
+ .section-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 12px;
+ }
+
+ /* 寮傚父鐘舵�侀�夋嫨 */
+ .exception-options {
+ display: flex;
+ gap: 12px;
+ }
+
+ .exception-option {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 14px 16px;
+ background: #f8f9fa;
+ border: 2px solid #e9ecef;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ }
+
+ .exception-option.active {
+ border-color: #409eff;
+ background: #f0f8ff;
+ }
+
+ .option-text {
+ font-size: 14px;
+ color: #333;
+ font-weight: 500;
+ }
+
+ /* 寮傚父鎻忚堪 */
+ .exception-textarea {
+ width: 100%;
+ min-height: 100px;
+ padding: 12px;
+ background: #f8f9fa;
+ border: 1px solid #e9ecef;
+ border-radius: 8px;
+ font-size: 14px;
+ color: #333;
+ resize: none;
+ box-sizing: border-box;
+ }
+
+ .exception-textarea:focus {
+ outline: none;
+ border-color: #409eff;
+ background: #fff;
+ }
+
+ /* 鍒嗙被鏍囩椤� */
+ .upload-tabs {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 15px;
+ }
+
+ .tab-item {
+ flex: 1;
+ padding: 10px;
+ text-align: center;
+ background: #f5f5f5;
+ border-radius: 6px;
+ font-size: 13px;
+ color: #666;
+ cursor: pointer;
+ transition: all 0.3s;
+ }
+
+ .tab-item.active {
+ background: #409eff;
+ color: #fff;
+ }
+
+ /* 涓婁紶鍖哄煙 */
+ .upload-area {
+ padding: 10px 0;
+ }
+
+ .upload-buttons {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 15px;
+ }
+
+ .upload-progress {
+ margin-bottom: 15px;
+ }
+
+ /* 鏂囦欢鍒楄〃 */
+ .file-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+
+ .file-item {
+ width: calc(33.33% - 7px);
+ }
+
+ .file-preview-container {
+ position: relative;
+ width: 100%;
+ aspect-ratio: 1;
+ border-radius: 8px;
+ overflow: hidden;
+ background: #f5f5f5;
+ }
+
+ .file-preview {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+
+ .video-preview {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: #333;
+ }
+
+ .video-text {
+ font-size: 12px;
+ color: #fff;
+ margin-top: 5px;
+ }
+
+ .delete-btn {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ width: 22px;
+ height: 22px;
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .file-info {
+ margin-top: 5px;
+ }
+
+ .file-name {
+ display: block;
+ font-size: 11px;
+ color: #666;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .file-size {
+ display: block;
+ font-size: 10px;
+ color: #999;
+ margin-top: 2px;
+ }
+
+ .empty-state {
+ text-align: center;
+ padding: 30px;
+ color: #999;
+ font-size: 13px;
+ }
+
+ /* 缁熻淇℃伅 */
+ .upload-summary {
+ margin-top: 15px;
+ padding: 10px;
+ background: #f8f9fa;
+ border-radius: 6px;
+ border-left: 3px solid #409eff;
+ }
+
+ .summary-text {
+ font-size: 12px;
+ color: #666;
+ }
+
+ /* 姝e父鐘舵�佹彁绀� */
+ .normal-tip-card {
+ background: #f6ffed;
+ border: 2px dashed #b7eb8f;
+ border-radius: 12px;
+ padding: 50px 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 15px;
+ }
+
+ .normal-tip-card .tip-text {
+ margin-top: 15px;
+ font-size: 16px;
+ color: #52c41a;
+ font-weight: 500;
+ }
+
+ /* 搴曢儴鎸夐挳 */
+ .footer-buttons {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ padding: 15px;
+ background: #fff;
+ border-top: 1px solid #f0f0f0;
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
+ }
+</style>
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/productionDesign/basicParameters/edit.vue b/src/pages/productionDesign/basicParameters/edit.vue
new file mode 100644
index 0000000..111b465
--- /dev/null
+++ b/src/pages/productionDesign/basicParameters/edit.vue
@@ -0,0 +1,290 @@
+<template>
+ <view class="basic-parameters-edit">
+ <PageHeader :title="pageTitle"
+ @back="goBack" />
+ <up-form ref="formRef"
+ :model="form"
+ :rules="rules"
+ :errorType="['none']"
+ label-width="110">
+ <up-form-item label="鍙傛暟缂栫爜"
+ prop="paramCode">
+ <up-input v-model="form.paramCode"
+ disabled
+ placeholder="鑷姩鐢熸垚" />
+ </up-form-item>
+ <up-form-item label="鍙傛暟鍚嶇О"
+ prop="paramName"
+ required>
+ <up-input v-model="form.paramName"
+ placeholder="璇疯緭鍏ュ弬鏁板悕绉�"
+ clearable />
+ </up-form-item>
+ <up-form-item label="鍙傛暟绫诲瀷"
+ prop="paramType"
+ required>
+ <up-input v-model="paramTypeText"
+ placeholder="璇烽�夋嫨鍙傛暟绫诲瀷"
+ readonly
+ @click="showParamTypeSheet = true" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showParamTypeSheet = true"></up-icon>
+ </template>
+ </up-form-item>
+ <up-form-item label="鍗曚綅"
+ prop="unit"
+ :required="form.paramType === 1">
+ <up-input v-model="form.unit"
+ placeholder="璇疯緭鍏ュ崟浣�"
+ clearable />
+ </up-form-item>
+ <up-form-item label="鍙栧�兼牸寮�"
+ v-if="form.paramType === 1 || form.paramType === 2"
+ prop="paramFormat">
+ <up-input v-model="form.paramFormat"
+ placeholder="璇疯緭鍏ュ彇鍊兼牸寮�"
+ clearable />
+ </up-form-item>
+ <up-form-item label="涓嬫媺瀛楀吀"
+ v-else-if="form.paramType === 3"
+ prop="paramFormat">
+ <up-input v-model="dictTypeText"
+ placeholder="璇烽�夋嫨涓嬫媺瀛楀吀"
+ readonly
+ @click="showDictTypeSheet = true" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showDictTypeSheet = true"></up-icon>
+ </template>
+ </up-form-item>
+ <up-form-item label="鏃堕棿鏍煎紡"
+ v-else-if="form.paramType === 4"
+ prop="paramFormat">
+ <up-input v-model="form.paramFormat"
+ placeholder="璇烽�夋嫨鏃堕棿鏍煎紡"
+ readonly
+ @click="showTimeFormatSheet = true" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showTimeFormatSheet = true"></up-icon>
+ </template>
+ </up-form-item>
+ <up-form-item label="鏄惁蹇呭~"
+ prop="isRequired">
+ <view style="display: flex; justify-content: flex-end; width: 100%;">
+ <up-switch v-model="form.isRequired"
+ :activeValue="1"
+ :inactiveValue="0" />
+ </view>
+ </up-form-item>
+ <up-form-item label="澶囨敞"
+ prop="remark">
+ <up-textarea v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉�"
+ autoHeight />
+ </up-form-item>
+ </up-form>
+ <FooterButtons :loading="loading"
+ :confirmText="paramId ? '淇濆瓨' : '鏂板'"
+ @cancel="goBack"
+ @confirm="handleSubmit" />
+ <!-- 鍙傛暟绫诲瀷閫夋嫨 -->
+ <up-action-sheet :show="showParamTypeSheet"
+ title="閫夋嫨鍙傛暟绫诲瀷"
+ :actions="paramTypeActions"
+ @select="onSelectParamType"
+ @close="showParamTypeSheet = false" />
+ <!-- 涓嬫媺瀛楀吀閫夋嫨 -->
+ <up-action-sheet :show="showDictTypeSheet"
+ title="閫夋嫨涓嬫媺瀛楀吀"
+ :actions="dictTypeActions"
+ @select="onSelectDictType"
+ @close="showDictTypeSheet = false" />
+ <!-- 鏃堕棿鏍煎紡閫夋嫨 -->
+ <up-action-sheet :show="showTimeFormatSheet"
+ title="閫夋嫨鏃堕棿鏍煎紡"
+ :actions="timeFormatActions"
+ @select="onSelectTimeFormat"
+ @close="showTimeFormatSheet = false" />
+ </view>
+</template>
+
+<script setup>
+ import { computed, nextTick, onMounted, ref } from "vue";
+ import { onLoad, onReady } from "@dcloudio/uni-app";
+ import FooterButtons from "@/components/FooterButtons.vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import {
+ addBaseParam,
+ editBaseParam,
+ } from "@/api/basicData/parameterMaintenance";
+ import { listType } from "@/api/system/dict/type";
+
+ const formRef = ref();
+ const loading = ref(false);
+ const paramId = ref("");
+ const showParamTypeSheet = ref(false);
+ const showDictTypeSheet = ref(false);
+ const showTimeFormatSheet = ref(false);
+ const dictTypes = ref([]);
+
+ const form = ref({
+ id: null,
+ paramCode: "",
+ paramName: "",
+ paramType: "",
+ unit: "",
+ remark: "",
+ isRequired: 0,
+ paramFormat: "",
+ });
+
+ const rules = {
+ paramName: [{ required: true, message: "璇疯緭鍏ュ弬鏁板悕绉�" }],
+ paramType: [{ required: true, message: "璇烽�夋嫨鍙傛暟绫诲瀷" }],
+ unit: [
+ {
+ validator: (rule, value, callback) => {
+ if (form.value.paramType === 1 && !value) {
+ callback(new Error("鏁板�肩被鍨嬪繀椤诲~鍐欏崟浣�"));
+ } else {
+ callback();
+ }
+ },
+ },
+ ],
+ };
+
+ const paramTypeActions = [
+ { name: "鏁板�兼牸寮�", value: 1 },
+ { name: "鏂囨湰鏍煎紡", value: 2 },
+ { name: "涓嬫媺閫夐」", value: 3 },
+ { name: "鏃堕棿鏍煎紡", value: 4 },
+ ];
+
+ const timeFormatActions = [
+ { name: "YYYY-MM-DD", value: "YYYY-MM-DD" },
+ { name: "YYYY-MM-DD HH:mm:ss", value: "YYYY-MM-DD HH:mm:ss" },
+ ];
+
+ const dictTypeActions = computed(() => {
+ return dictTypes.value.map(item => ({
+ name: item.dictName,
+ value: item.dictType,
+ }));
+ });
+
+ const pageTitle = computed(() => (paramId.value ? "缂栬緫鍙傛暟" : "鏂板鍙傛暟"));
+
+ const paramTypeText = computed(() => {
+ const action = paramTypeActions.find(
+ item => item.value === form.value.paramType
+ );
+ return action ? action.name : "";
+ });
+
+ const dictTypeText = computed(() => {
+ const action = dictTypes.value.find(
+ item => item.dictType === form.value.paramFormat
+ );
+ return action ? action.dictName : form.value.paramFormat || "";
+ });
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const getDictTypes = () => {
+ listType({ pageNum: 1, pageSize: 1000 }).then(res => {
+ dictTypes.value = res.rows || [];
+ });
+ };
+
+ const onSelectParamType = action => {
+ form.value.paramType = action.value;
+ if (action.value === 1) {
+ form.value.paramFormat = "#.00000";
+ } else if (action.value === 4) {
+ form.value.paramFormat = "YYYY-MM-DD HH:mm:ss";
+ } else {
+ form.value.paramFormat = "";
+ }
+ showParamTypeSheet.value = false;
+ };
+
+ const onSelectDictType = action => {
+ form.value.paramFormat = action.value;
+ showDictTypeSheet.value = false;
+ };
+
+ const onSelectTimeFormat = action => {
+ form.value.paramFormat = action.value;
+ showTimeFormatSheet.value = false;
+ };
+
+ const handleSubmit = () => {
+ formRef.value
+ .validate()
+ .then(() => {
+ if (form.value.paramType === 3 && !form.value.paramFormat) {
+ uni.showToast({ title: "璇烽�夋嫨涓嬫媺瀛楀吀", icon: "none" });
+ return;
+ }
+
+ loading.value = true;
+ const action = paramId.value ? editBaseParam : addBaseParam;
+ action({ ...form.value, id: paramId.value || undefined })
+ .then(() => {
+ uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
+ setTimeout(() => {
+ goBack();
+ }, 1500);
+ })
+ .catch(() => {
+ uni.showToast({ title: "淇濆瓨澶辫触", icon: "none" });
+ })
+ .finally(() => {
+ loading.value = false;
+ });
+ })
+ .catch(errors => {
+ if (errors && errors.length > 0) {
+ uni.showToast({
+ title: errors[0].message,
+ icon: "none",
+ });
+ }
+ });
+ };
+
+ onReady(() => {
+ if (formRef.value) {
+ formRef.value.setRules(rules);
+ }
+ });
+
+ onMounted(() => {
+ getDictTypes();
+ });
+
+ onLoad(options => {
+ if (options?.item) {
+ const item = JSON.parse(decodeURIComponent(options.item));
+ paramId.value = item.id;
+ if (item.paramType) {
+ item.paramType = Number(item.paramType);
+ }
+ Object.assign(form.value, item);
+ }
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/static/scss/form-common.scss";
+
+ .basic-parameters-edit {
+ min-height: 100vh;
+ background: #f5f5f5;
+ }
+</style>
diff --git a/src/pages/productionDesign/basicParameters/index.vue b/src/pages/productionDesign/basicParameters/index.vue
new file mode 100644
index 0000000..24a5db0
--- /dev/null
+++ b/src/pages/productionDesign/basicParameters/index.vue
@@ -0,0 +1,245 @@
+<template>
+ <view class="sales-account">
+ <PageHeader title="鍩虹鍙傛暟"
+ @back="goBack" />
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ v-model="paramName"
+ placeholder="璇疯緭鍏ュ弬鏁板悕绉�"
+ clearable
+ @change="handleSearch" />
+ </view>
+ <view class="filter-button"
+ @click="handleSearch">
+ <up-icon name="search"
+ size="24"
+ color="#999999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <view v-if="list.length > 0"
+ class="ledger-list">
+ <view v-for="item in list"
+ :key="item.id"
+ class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="setting-fill"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.paramName || "-" }}</text>
+ </view>
+ <text class="item-index">{{ item.paramCode || "-" }}</text>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">鍙傛暟绫诲瀷</text>
+ <up-tag :text="getParamTypeLabel(item.paramType)"
+ :type="getParamTypeTag(item.paramType)"
+ size="mini" />
+ </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>
+ <up-tag :text="item.isRequired === 1 ? '鏄�' : '鍚�'"
+ :type="item.isRequired === 1 ? 'success' : 'info'"
+ size="mini" />
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍙栧�兼牸寮�</text>
+ <text class="detail-value">{{ item.paramFormat || "-" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark || "-" }}</text>
+ </view>
+ </view>
+ <view class="action-buttons">
+ <up-button class="action-btn"
+ size="small"
+ type="primary"
+ @click="goEdit(item)">缂栬緫</up-button>
+ <up-button class="action-btn"
+ size="small"
+ type="error"
+ @click="handleDelete(item)">鍒犻櫎</up-button>
+ </view>
+ </view>
+ <up-loadmore :status="page.status" />
+ </view>
+ <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>
+ </view>
+</template>
+
+<script setup>
+ import { reactive, ref } from "vue";
+ import { onReachBottom, onShow } from "@dcloudio/uni-app";
+ import {
+ getBaseParamList,
+ removeBaseParam,
+ } from "@/api/basicData/parameterMaintenance";
+
+ const paramName = ref("");
+ const list = ref([]);
+
+ const page = reactive({
+ current: 1,
+ size: 100,
+ total: 0,
+ status: "loadmore", // loadmore, loading, nomore
+ });
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const getParamTypeLabel = type => {
+ const map = {
+ 1: "鏁板�兼牸寮�",
+ 2: "鏂囨湰鏍煎紡",
+ 3: "涓嬫媺閫夐」",
+ 4: "鏃堕棿鏍煎紡",
+ };
+ return map[type] || type;
+ };
+
+ const getParamTypeTag = type => {
+ const map = {
+ 1: "primary",
+ 2: "info",
+ 3: "warning",
+ 4: "success",
+ };
+ return map[type] || "info";
+ };
+
+ const goAdd = () => {
+ uni.navigateTo({ url: "/pages/productionDesign/basicParameters/edit" });
+ };
+
+ const goEdit = item => {
+ uni.navigateTo({
+ url: `/pages/productionDesign/basicParameters/edit?item=${encodeURIComponent(
+ JSON.stringify(item)
+ )}`,
+ });
+ };
+
+ const handleDelete = item => {
+ uni.showModal({
+ title: "鎻愮ず",
+ content: "纭畾瑕佸垹闄よ鍙傛暟鍚楋紵",
+ success: res => {
+ if (res.confirm) {
+ removeBaseParam(item.id).then(() => {
+ uni.showToast({ title: "鍒犻櫎鎴愬姛" });
+ handleSearch();
+ });
+ }
+ },
+ });
+ };
+
+ const handleSearch = () => {
+ page.current = 1;
+ page.status = "loadmore";
+ list.value = [];
+ getList();
+ };
+
+ const getList = () => {
+ if (page.status === "loading" || page.status === "nomore") return;
+
+ page.status = "loading";
+ getBaseParamList({
+ current: page.current,
+ size: page.size,
+ paramName: paramName.value,
+ })
+ .then(res => {
+ const records = res?.data?.records || res?.records || [];
+ const total = res?.data?.total || res?.total || 0;
+
+ if (page.current === 1) {
+ list.value = records;
+ } else {
+ list.value = [...list.value, ...records];
+ }
+
+ page.total = total;
+ if (list.value.length >= total) {
+ page.status = "nomore";
+ } else {
+ page.status = "loadmore";
+ page.current++;
+ }
+ })
+ .catch(() => {
+ uni.showToast({ title: "鏌ヨ澶辫触", icon: "error" });
+ page.status = "loadmore";
+ });
+ };
+
+ onReachBottom(() => {
+ getList();
+ });
+
+ onShow(() => {
+ handleSearch();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/procurement-common.scss";
+
+ .no-data {
+ padding-top: 100rpx;
+ text-align: center;
+ color: #999;
+ font-size: 28rpx;
+ }
+
+ .action-buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 20rpx;
+ padding-bottom: 30rpx;
+ }
+
+ .action-btn {
+ width: 140rpx;
+ margin: 0 !important;
+ }
+
+ .fab-button {
+ position: fixed;
+ right: 40rpx;
+ bottom: 60rpx;
+ width: 100rpx;
+ height: 100rpx;
+ background: #2979ff;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4rpx 12rpx rgba(41, 121, 255, 0.4);
+ z-index: 100;
+ }
+</style>
diff --git a/src/pages/productionDesign/bom/BomStructureItem.vue b/src/pages/productionDesign/bom/BomStructureItem.vue
new file mode 100644
index 0000000..689010c
--- /dev/null
+++ b/src/pages/productionDesign/bom/BomStructureItem.vue
@@ -0,0 +1,256 @@
+<template>
+ <view class="structure-item-wrapper"
+ :class="{ 'is-root': level === 0, 'is-last': isLast }">
+ <!-- 鏍戝舰杩炴帴绾� (闈炴牴鑺傜偣鏄剧ず) -->
+ <template v-if="level > 0">
+ <view class="line-v"></view>
+ <view class="line-h"></view>
+ </template>
+ <view class="structure-item-card"
+ :class="{ 'has-children': hasChildren }">
+ <view class="card-main">
+ <view class="item-header"
+ @click="toggleExpand">
+ <view class="header-left">
+ <view v-if="hasChildren"
+ class="expand-icon"
+ :class="{ 'is-expanded': isExpanded }">
+ <up-icon name="arrow-right"
+ size="14"
+ color="#999"></up-icon>
+ </view>
+ <view v-else
+ class="dot-icon"></view>
+ <text class="item-title">{{ item.productName || '鏈�夋嫨浜у搧' }}</text>
+ </view>
+ <up-tag v-if="hasChildren"
+ text="缁勫悎"
+ type="primary"
+ size="mini"
+ plain
+ shape="circle" />
+ </view>
+ <view class="item-body">
+ <view class="info-grid">
+ <view class="info-item">
+ <text class="label">瑙勬牸鍨嬪彿锛�</text>
+ <text class="value">{{ item.model || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">娑堣�楀伐搴忥細</text>
+ <text class="value">{{ getProcessName(item.processId) }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">鍗曚綅鏁伴噺锛�</text>
+ <text class="value highlight">{{ item.unitQuantity || 0 }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">闇�姹傛�婚噺锛�</text>
+ <text class="value highlight">{{ item.demandedQuantity || 0 }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">鍗曚綅锛�</text>
+ <text class="value">{{ item.unit || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">鐩樻暟锛�</text>
+ <text class="value">{{ item.diskQuantity || 0 }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ <!-- 閫掑綊灞曠ず瀛愯妭鐐� -->
+ <view v-if="hasChildren && isExpanded"
+ class="children-container">
+ <BomStructureItem v-for="(child, index) in item.children"
+ :key="index"
+ :item="child"
+ :level="level + 1"
+ :isLast="index === item.children.length - 1"
+ :processOptions="processOptions" />
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, computed, defineProps } from "vue";
+
+ const props = defineProps({
+ item: {
+ type: Object,
+ required: true,
+ },
+ level: {
+ type: Number,
+ default: 0,
+ },
+ isLast: {
+ type: Boolean,
+ default: false,
+ },
+ processOptions: {
+ type: Array,
+ default: () => [],
+ },
+ });
+
+ const isExpanded = ref(true);
+ const hasChildren = computed(
+ () => props.item.children && props.item.children.length > 0
+ );
+
+ const toggleExpand = () => {
+ if (hasChildren.value) {
+ isExpanded.value = !isExpanded.value;
+ }
+ };
+
+ const getProcessName = id => {
+ const process = props.processOptions.find(p => p.id === id);
+ return process ? process.name : "-";
+ };
+</script>
+
+<script>
+ export default {
+ name: "BomStructureItem",
+ };
+</script>
+
+<style scoped lang="scss">
+ .structure-item-wrapper {
+ position: relative;
+ padding-left: 44rpx;
+
+ &.is-root {
+ padding-left: 0;
+ }
+ }
+
+ // 鍨傜洿杩炴帴绾挎
+ .line-v {
+ position: absolute;
+ left: 18rpx; // 灞呬腑浜� 44rpx 鐨勭缉杩涘唴
+ top: -20rpx; // 鍚戜笂寤朵几瑕嗙洊涓婁竴涓妭鐐圭殑 margin-bottom
+ bottom: 0;
+ width: 2rpx;
+ background-color: #ddd;
+ z-index: 1;
+ }
+
+ // 鏈�鍚庝竴涓妭鐐圭殑鍨傜洿绾垮彧寤朵几鍒版按骞崇嚎浣嶇疆
+ .is-last > .line-v {
+ bottom: auto;
+ height: 60rpx; // 20rpx (top offset) + 40rpx (to horizontal line)
+ }
+
+ // 姘村钩杩炴帴绾�
+ .line-h {
+ position: absolute;
+ left: 18rpx;
+ top: 40rpx; // 瀵归綈鍒板崱鐗囧唴閮ㄥ浘鏍囦腑蹇� (padding 24 + icon 32/2)
+ width: 26rpx;
+ height: 2rpx;
+ background-color: #ddd;
+ z-index: 1;
+ }
+
+ .structure-item-card {
+ position: relative;
+ background: #fff;
+ border-radius: 16rpx;
+ margin-bottom: 20rpx;
+ padding: 24rpx;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
+ border: 1rpx solid #f0f0f0;
+ transition: all 0.3s;
+ z-index: 2;
+
+ &:active {
+ background-color: #f9f9f9;
+ }
+
+ &.has-children {
+ border-left: 6rpx solid #3c9cff;
+ }
+ }
+
+ .card-main {
+ .item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20rpx;
+
+ .header-left {
+ display: flex;
+ align-items: center;
+ flex: 1;
+
+ .expand-icon {
+ margin-right: 12rpx;
+ transition: transform 0.3s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32rpx;
+ height: 32rpx;
+
+ &.is-expanded {
+ transform: rotate(90deg);
+ }
+ }
+
+ .dot-icon {
+ width: 12rpx;
+ height: 12rpx;
+ border-radius: 50%;
+ background-color: #ccc;
+ margin-right: 20rpx;
+ margin-left: 10rpx;
+ }
+
+ .item-title {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+ line-height: 1.4;
+ }
+ }
+ }
+
+ .item-body {
+ .info-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 12rpx 20rpx;
+
+ .info-item {
+ display: flex;
+ font-size: 24rpx;
+ line-height: 1.5;
+
+ .label {
+ color: #999;
+ white-space: nowrap;
+ }
+
+ .value {
+ color: #666;
+ word-break: break-all;
+
+ &.highlight {
+ color: #3c9cff;
+ font-weight: 500;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .children-container {
+ position: relative;
+ }
+</style>
diff --git a/src/pages/productionDesign/bom/index.vue b/src/pages/productionDesign/bom/index.vue
new file mode 100644
index 0000000..97ba5f6
--- /dev/null
+++ b/src/pages/productionDesign/bom/index.vue
@@ -0,0 +1,179 @@
+<template>
+ <view class="bom-list">
+ <PageHeader title="BOM绠$悊"
+ @back="goBack" />
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ v-model="queryParams.productName"
+ placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+ clearable
+ @change="handleSearch" />
+ </view>
+ <view class="filter-button"
+ @click="handleSearch">
+ <up-icon name="search"
+ size="24"
+ color="#999999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <view v-if="list.length > 0"
+ class="ledger-list">
+ <view v-for="item in list"
+ :key="item.id"
+ class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="list-dot"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.bomNo || "-" }}</text>
+ </view>
+ <up-tag :text="'V' + (item.version || '1.0')"
+ type="primary"
+ size="mini" />
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">浜у搧鍚嶇О</text>
+ <text class="detail-value">{{ item.productName || "-" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瑙勬牸鍨嬪彿</text>
+ <text class="detail-value">{{ item.productModelName || "-" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark || "-" }}</text>
+ </view>
+ </view>
+ <view class="action-buttons">
+ <up-button class="action-btn"
+ size="small"
+ type="primary"
+ @click="goStructure(item)">鏌ョ湅璇︽儏</up-button>
+ </view>
+ </view>
+ <up-loadmore :status="pageStatus" />
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty text="鏆傛棤BOM鏁版嵁"
+ mode="list"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { reactive, ref } from "vue";
+ import { onReachBottom, onShow } from "@dcloudio/uni-app";
+ import { listPage } from "@/api/productionManagement/bom";
+
+ const queryParams = reactive({
+ productName: "",
+ });
+ const list = ref([]);
+ const pageStatus = ref("loadmore");
+
+ const page = reactive({
+ current: 1,
+ size: 3,
+ total: 0,
+ });
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const handleSearch = () => {
+ page.current = 1;
+ pageStatus.value = "loadmore";
+ list.value = [];
+ getList();
+ };
+
+ const getList = () => {
+ if (pageStatus.value === "loading" || pageStatus.value === "nomore") return;
+
+ pageStatus.value = "loading";
+ listPage({
+ current: page.current,
+ size: page.size,
+ productName: queryParams.productName,
+ })
+ .then(res => {
+ const records = res?.data?.records || res?.records || [];
+ const total = res?.data?.total || res?.total || 0;
+
+ if (page.current === 1) {
+ list.value = records;
+ } else {
+ list.value = [...list.value, ...records];
+ }
+
+ page.total = total;
+ if (list.value.length >= total) {
+ pageStatus.value = "nomore";
+ } else {
+ pageStatus.value = "loadmore";
+ page.current++;
+ }
+ })
+ .catch(() => {
+ uni.showToast({ title: "鏌ヨ澶辫触", icon: "error" });
+ pageStatus.value = "loadmore";
+ });
+ };
+
+ const goStructure = item => {
+ uni.navigateTo({
+ url: `/pages/productionDesign/bom/structure?id=${
+ item.id
+ }&bomNo=${encodeURIComponent(item.bomNo)}&productName=${encodeURIComponent(
+ item.productName || ""
+ )}&productModelName=${encodeURIComponent(
+ item.productModelName || ""
+ )}&remark=${encodeURIComponent(
+ item.remark || ""
+ )}&version=${encodeURIComponent(item.version || 1)}`,
+ });
+ };
+
+ onReachBottom(() => {
+ getList();
+ });
+
+ onShow(() => {
+ handleSearch();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/procurement-common.scss";
+
+ .no-data {
+ padding-top: 100rpx;
+ text-align: center;
+ color: #999;
+ font-size: 28rpx;
+ }
+
+ .action-buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 15rpx;
+ padding: 0 30rpx 30rpx;
+ flex-wrap: wrap;
+ }
+
+ .action-btn {
+ width: calc(50% - 15rpx);
+ margin: 0 !important;
+ margin-bottom: 15rpx !important;
+ }
+</style>
diff --git a/src/pages/productionDesign/bom/structure.vue b/src/pages/productionDesign/bom/structure.vue
new file mode 100644
index 0000000..5b7c2a7
--- /dev/null
+++ b/src/pages/productionDesign/bom/structure.vue
@@ -0,0 +1,100 @@
+<template>
+ <view class="structure-page">
+ <PageHeader :title="'BOM缁撴瀯 - ' + bomNo"
+ @back="goBack" />
+ <view class="info-card">
+ <view class="info-row">
+ <text class="info-label">浜у搧鍚嶇О锛�</text>
+ <text class="info-value">{{ productName }}-{{ productModelName }}</text>
+ </view>
+ </view>
+ <view class="structure-list"
+ v-if="dataList.length > 0">
+ <BomStructureItem v-for="(item, index) in dataList"
+ :key="index"
+ :item="item"
+ :level="0"
+ :isLast="index === dataList.length - 1"
+ :processOptions="processOptions" />
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty text="鏆傛棤缁撴瀯鏁版嵁"
+ mode="list"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, computed } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import { queryStructureList } from "@/api/productionManagement/bom";
+ import { list as getProcessList } from "@/api/productionManagement/processManagement";
+ import BomStructureItem from "./BomStructureItem.vue";
+
+ const bomId = ref(null);
+ const bomNo = ref("");
+ const productName = ref("");
+ const dataList = ref([]);
+ const processOptions = ref([]);
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const fetchData = () => {
+ queryStructureList(bomId.value).then(res => {
+ dataList.value = res.data || [];
+ });
+ };
+
+ const fetchProcess = () => {
+ getProcessList().then(res => {
+ processOptions.value = res.data || [];
+ });
+ };
+
+ const productModelName = ref("");
+
+ onLoad(options => {
+ bomId.value = options.id;
+ bomNo.value = decodeURIComponent(options.bomNo);
+ productName.value = decodeURIComponent(options.productName);
+ productModelName.value = decodeURIComponent(options.productModelName);
+ fetchData();
+ fetchProcess();
+ });
+</script>
+
+<style scoped lang="scss">
+ .structure-page {
+ background-color: #f5f5f5;
+ min-height: 100vh;
+ padding-bottom: 120rpx;
+ }
+
+ .info-card {
+ background: #fff;
+ padding: 30rpx;
+ margin-bottom: 20rpx;
+ .info-row {
+ display: flex;
+ font-size: 28rpx;
+ .info-label {
+ color: #666;
+ }
+ .info-value {
+ color: #333;
+ font-weight: bold;
+ }
+ }
+ }
+
+ .structure-list {
+ padding: 20rpx;
+ }
+
+ .no-data {
+ padding-top: 100rpx;
+ }
+</style>
diff --git a/src/pages/productionDesign/processManagement/edit.vue b/src/pages/productionDesign/processManagement/edit.vue
new file mode 100644
index 0000000..425be43
--- /dev/null
+++ b/src/pages/productionDesign/processManagement/edit.vue
@@ -0,0 +1,236 @@
+<template>
+ <view class="process-edit">
+ <PageHeader :title="pageTitle"
+ @back="goBack" />
+ <up-form ref="formRef"
+ :model="form"
+ :rules="rules"
+ :errorType="['none']"
+ label-width="110">
+ <up-form-item label="宸ュ簭缂栫爜"
+ prop="no">
+ <up-input v-model="form.no"
+ placeholder="璇疯緭鍏ュ伐搴忕紪鐮�"
+ clearable />
+ </up-form-item>
+ <up-form-item label="宸ュ簭鍚嶇О"
+ prop="name"
+ required>
+ <up-input v-model="form.name"
+ placeholder="璇疯緭鍏ュ伐搴忓悕绉�"
+ clearable />
+ </up-form-item>
+ <up-form-item label="宸ヨ祫瀹氶"
+ prop="salaryQuota">
+ <up-input v-model="form.salaryQuota"
+ type="number"
+ placeholder="璇疯緭鍏ュ伐璧勫畾棰�"
+ clearable />
+ </up-form-item>
+ <up-form-item label="璁¤垂绫诲瀷"
+ prop="type">
+ <up-input v-model="typeText"
+ placeholder="璇烽�夋嫨璁¤垂绫诲瀷"
+ readonly
+ @click="showTypeSheet = true" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showTypeSheet = true"></up-icon>
+ </template>
+ </up-form-item>
+ <up-form-item label="鏄惁璐ㄦ"
+ prop="isQuality">
+ <view style="display: flex; justify-content: flex-end; width: 100%;">
+ <up-switch v-model="form.isQuality" />
+ </view>
+ </up-form-item>
+ <up-form-item label="鏄惁鐢熶骇"
+ prop="isProduction">
+ <view style="display: flex; justify-content: flex-end; width: 100%;">
+ <up-switch v-model="form.isProduction" />
+ </view>
+ </up-form-item>
+ <up-form-item label="鍏宠仈璁惧"
+ prop="deviceLedgerId">
+ <up-input v-model="deviceText"
+ placeholder="璇烽�夋嫨鍏宠仈璁惧"
+ readonly
+ @click="showDeviceSheet = true" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showDeviceSheet = true"></up-icon>
+ </template>
+ </up-form-item>
+ <up-form-item label="宸ュ簭鎻忚堪"
+ prop="remark">
+ <up-textarea v-model="form.remark"
+ placeholder="璇疯緭鍏ュ伐搴忔弿杩�"
+ autoHeight />
+ </up-form-item>
+ </up-form>
+ <FooterButtons :loading="loading"
+ :confirmText="processId ? '淇濆瓨' : '鏂板'"
+ @cancel="goBack"
+ @confirm="handleSubmit" />
+ <!-- 璁¤垂绫诲瀷閫夋嫨 -->
+ <up-action-sheet :show="showTypeSheet"
+ title="閫夋嫨璁¤垂绫诲瀷"
+ :actions="typeActions"
+ @select="onSelectType"
+ @close="showTypeSheet = false" />
+ <!-- 璁惧閫夋嫨 -->
+ <up-action-sheet :show="showDeviceSheet"
+ title="閫夋嫨鍏宠仈璁惧"
+ :actions="deviceActions"
+ @select="onSelectDevice"
+ @close="showDeviceSheet = false" />
+ </view>
+</template>
+
+<script setup>
+ import { reactive, ref, computed, onMounted } from "vue";
+ import { onLoad, onReady } from "@dcloudio/uni-app";
+ import FooterButtons from "@/components/FooterButtons.vue";
+ import {
+ add,
+ update,
+ getDeviceLedger,
+ } from "@/api/productionManagement/processManagement";
+
+ const formRef = ref(null);
+ const loading = ref(false);
+ const processId = ref(null);
+ const pageTitle = computed(() => (processId.value ? "缂栬緫宸ュ簭" : "鏂板宸ュ簭"));
+
+ const form = ref({
+ no: "",
+ name: "",
+ salaryQuota: "",
+ isQuality: false,
+ isProduction: false,
+ remark: "",
+ deviceLedgerId: null,
+ type: 0,
+ });
+
+ const rules = {
+ name: [{ required: true, message: "璇疯緭鍏ュ伐搴忓悕绉�" }],
+ salaryQuota: [
+ {
+ validator: (rule, value, callback) => {
+ if (value !== "" && value !== null && (isNaN(value) || value < 0)) {
+ callback(new Error("宸ヨ祫瀹氶蹇呴』鏄潪璐熸暟瀛�"));
+ } else {
+ callback();
+ }
+ },
+ },
+ ],
+ };
+
+ const showTypeSheet = ref(false);
+ const typeActions = [
+ { name: "璁℃椂", value: 0 },
+ { name: "璁′欢", value: 1 },
+ ];
+ const typeText = computed(() => {
+ const action = typeActions.find(a => a.value === form.value.type);
+ return action ? action.name : "";
+ });
+
+ const showDeviceSheet = ref(false);
+ const deviceActions = ref([]);
+ const deviceText = ref("");
+
+ const onSelectType = e => {
+ form.value.type = e.value;
+ showTypeSheet.value = false;
+ };
+
+ const onSelectDevice = e => {
+ form.value.deviceLedgerId = e.id;
+ deviceText.value = e.name;
+ showDeviceSheet.value = false;
+ };
+
+ const loadDevices = async () => {
+ try {
+ const { data } = await getDeviceLedger();
+ deviceActions.value = (data || []).map(item => ({
+ name: item.deviceName,
+ id: item.id,
+ }));
+ if (form.value.deviceLedgerId) {
+ const device = deviceActions.value.find(
+ d => d.id === Number(form.value.deviceLedgerId)
+ );
+ if (device) deviceText.value = device.name;
+ }
+ } catch (error) {
+ console.error("鍔犺浇璁惧澶辫触", error);
+ }
+ };
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const handleSubmit = () => {
+ formRef.value
+ .validate()
+ .then(() => {
+ loading.value = true;
+ const promise = processId.value ? update(form.value) : add(form.value);
+ promise
+ .then(() => {
+ uni.showToast({ title: processId.value ? "淇濆瓨鎴愬姛" : "鏂板鎴愬姛" });
+ setTimeout(() => {
+ goBack();
+ }, 1500);
+ })
+ .catch(err => {
+ uni.showToast({ title: err.msg || "鎻愪氦澶辫触", icon: "error" });
+ })
+ .finally(() => {
+ loading.value = false;
+ });
+ })
+ .catch(errors => {
+ if (errors && errors.length > 0) {
+ uni.showToast({
+ title: errors[0].message,
+ icon: "none",
+ });
+ }
+ });
+ };
+
+ onLoad(option => {
+ if (option.item) {
+ const item = JSON.parse(decodeURIComponent(option.item));
+ processId.value = item.id;
+ Object.assign(form.value, item);
+ // 澶勭悊绫诲瀷杞崲锛岀‘淇濇槸鏁板瓧
+ form.value.type = Number(form.value.type);
+ form.value.isQuality = !!form.value.isQuality;
+ form.value.isProduction = !!form.value.isProduction;
+ }
+ });
+
+ onReady(() => {
+ formRef.value.setRules(rules);
+ });
+
+ onMounted(() => {
+ loadDevices();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/static/scss/form-common.scss";
+
+ .process-edit {
+ min-height: 100vh;
+ background: #f5f5f5;
+ }
+</style>
\ No newline at end of file
diff --git a/src/pages/productionDesign/processManagement/index.vue b/src/pages/productionDesign/processManagement/index.vue
new file mode 100644
index 0000000..6ca2f76
--- /dev/null
+++ b/src/pages/productionDesign/processManagement/index.vue
@@ -0,0 +1,261 @@
+<template>
+ <view class="sales-account">
+ <PageHeader title="宸ュ簭绠$悊"
+ @back="goBack" />
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ v-model="queryParams.name"
+ placeholder="璇疯緭鍏ュ伐搴忓悕绉�"
+ clearable
+ @change="handleSearch" />
+ </view>
+ <view class="filter-button"
+ @click="handleSearch">
+ <up-icon name="search"
+ size="24"
+ color="#999999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <view v-if="list.length > 0"
+ class="ledger-list">
+ <view v-for="item in list"
+ :key="item.id"
+ class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="list-dot"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.name || "-" }}</text>
+ </view>
+ <text class="item-index">{{ item.no || "-" }}</text>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">鍏宠仈璁惧</text>
+ <text class="detail-value">{{ getDeviceName(item.deviceLedgerId) }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">宸ヨ祫瀹氶</text>
+ <text class="detail-value highlight">楼{{ item.salaryQuota || 0 }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">宸ュ簭鐘舵��</text>
+ <view class="detail-value">
+ <up-tag :text="item.isQuality ? '璐ㄦ' : '闈炶川妫�'"
+ :type="item.isQuality ? 'warning' : 'info'"
+ size="mini"
+ style="margin-left: 8rpx" />
+ <up-tag :text="item.isProduction ? '鐢熶骇' : '涓嶇敓浜�'"
+ :type="item.isProduction ? 'warning' : 'info'"
+ size="mini"
+ style="margin-left: 8rpx" />
+ <up-tag v-if="item.type !== null && item.type !== undefined"
+ :text="item.type == 0 ? '璁℃椂' : '璁′欢'"
+ :type="item.type == 1 ? 'primary' : 'success'"
+ size="mini"
+ style="margin-left: 8rpx" />
+ </view>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark || "-" }}</text>
+ </view>
+ </view>
+ <view class="action-buttons">
+ <up-button class="action-btn"
+ size="small"
+ type="primary"
+ @click="goEdit(item)">缂栬緫</up-button>
+ <up-button class="action-btn"
+ size="small"
+ type="warning"
+ @click="goParams(item)">鍙傛暟閰嶇疆</up-button>
+ <up-button class="action-btn"
+ size="small"
+ type="error"
+ @click="handleDelete(item)">鍒犻櫎</up-button>
+ </view>
+ </view>
+ <up-loadmore :status="pageStatus" />
+ </view>
+ <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>
+ </view>
+</template>
+
+<script setup>
+ import { reactive, ref } from "vue";
+ import { onReachBottom, onShow } from "@dcloudio/uni-app";
+ import {
+ getProcessList,
+ del,
+ getDeviceLedger,
+ } from "@/api/productionManagement/processManagement";
+
+ const queryParams = reactive({
+ name: "",
+ });
+ const list = ref([]);
+ const deviceOptions = ref([]);
+ const pageStatus = ref("loadmore");
+
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const getDeviceName = deviceId => {
+ if (!deviceId) return "鏈叧鑱�";
+ const device = deviceOptions.value.find(item => item.id === Number(deviceId));
+ return device?.deviceName || "鏈叧鑱�";
+ };
+
+ const loadDevices = async () => {
+ try {
+ const { data } = await getDeviceLedger();
+ deviceOptions.value = data || [];
+ } catch (error) {
+ console.error("鍔犺浇璁惧鍒楄〃澶辫触", error);
+ }
+ };
+
+ const handleSearch = () => {
+ page.current = 1;
+ pageStatus.value = "loadmore";
+ list.value = [];
+ getList();
+ };
+
+ const getList = () => {
+ if (pageStatus.value === "loading" || pageStatus.value === "nomore") return;
+
+ pageStatus.value = "loading";
+ getProcessList({
+ current: page.current,
+ size: page.size,
+ name: queryParams.name,
+ })
+ .then(res => {
+ const records = res?.data?.records || res?.records || [];
+ const total = res?.data?.total || res?.total || 0;
+
+ if (page.current === 1) {
+ list.value = records;
+ } else {
+ list.value = [...list.value, ...records];
+ }
+
+ page.total = total;
+ if (list.value.length >= total) {
+ pageStatus.value = "nomore";
+ } else {
+ pageStatus.value = "loadmore";
+ page.current++;
+ }
+ })
+ .catch(() => {
+ uni.showToast({ title: "鏌ヨ澶辫触", icon: "error" });
+ pageStatus.value = "loadmore";
+ });
+ };
+
+ const goAdd = () => {
+ uni.navigateTo({ url: "/pages/productionDesign/processManagement/edit" });
+ };
+
+ const goEdit = item => {
+ uni.navigateTo({
+ url: `/pages/productionDesign/processManagement/edit?item=${encodeURIComponent(
+ JSON.stringify(item)
+ )}`,
+ });
+ };
+
+ const goParams = item => {
+ uni.navigateTo({
+ url: `/pages/productionDesign/processManagement/params?id=${item.id}&name=${encodeURIComponent(item.name)}`,
+ });
+ };
+
+ const handleDelete = item => {
+ uni.showModal({
+ title: "鎻愮ず",
+ content: "纭畾瑕佸垹闄よ宸ュ簭鍚楋紵",
+ success: res => {
+ if (res.confirm) {
+ del([item.id]).then(() => {
+ uni.showToast({ title: "鍒犻櫎鎴愬姛" });
+ handleSearch();
+ });
+ }
+ },
+ });
+ };
+
+ onReachBottom(() => {
+ getList();
+ });
+
+ onShow(async () => {
+ await loadDevices();
+ handleSearch();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/procurement-common.scss";
+
+ .no-data {
+ padding-top: 100rpx;
+ text-align: center;
+ color: #999;
+ font-size: 28rpx;
+ }
+
+ .action-buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 20rpx;
+ padding-bottom: 30rpx;
+ }
+
+ .action-btn {
+ flex: 1;
+ margin: 0 !important;
+ }
+
+ .fab-button {
+ position: fixed;
+ right: 40rpx;
+ bottom: 60rpx;
+ width: 100rpx;
+ height: 100rpx;
+ background: #2979ff;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4rpx 12rpx rgba(41, 121, 255, 0.4);
+ z-index: 100;
+ }
+</style>
\ No newline at end of file
diff --git a/src/pages/productionDesign/processManagement/params.vue b/src/pages/productionDesign/processManagement/params.vue
new file mode 100644
index 0000000..0fa0bc0
--- /dev/null
+++ b/src/pages/productionDesign/processManagement/params.vue
@@ -0,0 +1,413 @@
+<template>
+ <view class="process-params">
+ <PageHeader :title="processName + ' - 鍙傛暟閰嶇疆'"
+ @back="goBack" />
+ <view class="ledger-list">
+ <view v-if="paramList.length > 0">
+ <view v-for="item in paramList"
+ :key="item.id"
+ class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="setting-fill"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.paramName || "-" }}</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">鏍囧噯鍊�</text>
+ <text class="detail-value highlight">{{ item.standardValue || "-" }}</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>
+ <up-tag :text="getParamTypeText(item.paramType)"
+ :type="getParamTypeTag(item.paramType)"
+ size="mini" />
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍙栧�兼牸寮�</text>
+ <text class="detail-value">{{ item.paramFormat || "-" }}</text>
+ </view>
+ </view>
+ <view class="action-buttons">
+ <up-button class="action-btn"
+ size="small"
+ type="primary"
+ @click="handleEditParam(item)">缂栬緫</up-button>
+ <up-button class="action-btn"
+ size="small"
+ type="error"
+ @click="handleDeleteParam(item)">鍒犻櫎</up-button>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty text="鏆傛棤鍙傛暟閰嶇疆"
+ icon="account"
+ iconSize="60"></up-empty>
+ </view>
+ </view>
+ <!-- 娴姩鏂板鎸夐挳 -->
+ <view class="fab-button"
+ @click="openSelectModal">
+ <up-icon name="plus"
+ size="28"
+ color="#ffffff"></up-icon>
+ </view>
+ <!-- 閫夋嫨鍙傛暟寮圭獥 -->
+ <up-modal :show="selectModalVisible"
+ title="閫夋嫨鍙傛暟"
+ width="650rpx"
+ @confirm="handleSelectSubmit"
+ @cancel="selectModalVisible = false"
+ :closeOnClickOverlay="false"
+ showCancelButton>
+ <view class="modal-content">
+ <view class="search-box">
+ <up-input v-model="searchKeyword"
+ placeholder="鎼滅储鍩虹鍙傛暟鍚嶇О"
+ clearable
+ @confirm="handleSearch"
+ @change="handleSearch" />
+ </view>
+ <scroll-view scroll-y
+ class="param-scroll-list"
+ @scrolltolower="loadMoreParams">
+ <view v-for="param in availableParams"
+ :key="param.id"
+ class="param-select-item"
+ :class="{ active: selectedBaseParam?.id === param.id }"
+ @click="selectParam(param)">
+ <view class="param-main">
+ <text class="param-name">{{ param.paramName }}</text>
+ <up-tag :text="getParamTypeText(param.paramType)"
+ :type="getParamTypeTag(param.paramType)"
+ size="mini" />
+ </view>
+ <text class="param-code">{{ param.paramCode }}</text>
+ </view>
+ <up-loadmore :status="availablePageStatus" />
+ </scroll-view>
+ <view v-if="selectedBaseParam"
+ class="standard-input-box">
+ <text class="label">鏍囧噯鍊硷細</text>
+ <up-input v-model="selectedStandardValue"
+ placeholder="璇疯緭鍏ヨ宸ュ簭鐨勬爣鍑嗗��" />
+ </view>
+ </view>
+ </up-modal>
+ <!-- 缂栬緫鍙傛暟鏍囧噯鍊煎脊绐� -->
+ <up-modal :show="editModalVisible"
+ title="缂栬緫鏍囧噯鍊�"
+ width="500rpx"
+ @confirm="handleEditSubmit"
+ @cancel="editModalVisible = false"
+ :closeOnClickOverlay="false"
+ showCancelButton>
+ <view class="modal-content">
+ <view class="edit-info">
+ <text class="edit-label">鍙傛暟锛歿{ currentEditParam?.paramName }}</text>
+ <up-input v-model="currentEditValue"
+ placeholder="璇疯緭鍏ユ柊鐨勬爣鍑嗗��" />
+ </view>
+ </view>
+ </up-modal>
+ </view>
+</template>
+
+<script setup>
+ import { reactive, ref, onMounted } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import {
+ getProcessParamList,
+ addProcessParam,
+ editProcessParam,
+ deleteProcessParam,
+ getBaseParamList,
+ } from "@/api/productionManagement/processManagement";
+
+ const processId = ref(null);
+ const processName = ref("");
+ const paramList = ref([]);
+ const loading = ref(false);
+
+ // 閫夋嫨鍙傛暟鐩稿叧
+ const selectModalVisible = ref(false);
+ const availableParams = ref([]);
+ const searchKeyword = ref("");
+ const selectedBaseParam = ref(null);
+ const selectedStandardValue = ref("");
+ const availablePage = reactive({ current: 1, size: 20, total: 0 });
+ const availablePageStatus = ref("loadmore");
+
+ // 缂栬緫鍙傛暟鐩稿叧
+ const editModalVisible = ref(false);
+ const currentEditParam = ref(null);
+ const currentEditValue = ref("");
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const getParamList = () => {
+ loading.value = true;
+ getProcessParamList({ technologyOperationId: processId.value })
+ .then(res => {
+ paramList.value = res?.data || [];
+ })
+ .catch(() => {
+ uni.showToast({ title: "鑾峰彇鍒楄〃澶辫触", icon: "none" });
+ })
+ .finally(() => {
+ loading.value = false;
+ });
+ };
+
+ const openSelectModal = () => {
+ searchKeyword.value = "";
+ selectedBaseParam.value = null;
+ selectedStandardValue.value = "";
+ availableParams.value = [];
+ availablePage.current = 1;
+ availablePageStatus.value = "loadmore";
+ selectModalVisible.value = true;
+ loadAvailableParams(true);
+ };
+
+ const handleSearch = () => {
+ availablePage.current = 1;
+ availableParams.value = [];
+ availablePageStatus.value = "loadmore";
+ loadAvailableParams(true);
+ };
+
+ const loadMoreParams = () => {
+ if (
+ availablePageStatus.value === "nomore" ||
+ availablePageStatus.value === "loading"
+ )
+ return;
+ loadAvailableParams(false);
+ };
+
+ const loadAvailableParams = (isReset = false) => {
+ if (availablePageStatus.value === "loading") return;
+ if (isReset) {
+ availablePage.current = 1;
+ availableParams.value = [];
+ availablePageStatus.value = "loading";
+ } else if (availablePageStatus.value === "nomore") {
+ return;
+ } else {
+ availablePageStatus.value = "loading";
+ }
+ getBaseParamList({
+ paramName: searchKeyword.value,
+ current: availablePage.current,
+ size: availablePage.size,
+ })
+ .then(res => {
+ const records = res?.data?.records || res?.records || [];
+ const total = res?.data?.total || res?.total || 0;
+
+ if (isReset || availablePage.current === 1) {
+ availableParams.value = records;
+ } else {
+ availableParams.value = [...availableParams.value, ...records];
+ }
+
+ availablePage.total = total;
+ if (availableParams.value.length >= total) {
+ availablePageStatus.value = "nomore";
+ } else {
+ availablePageStatus.value = "loadmore";
+ availablePage.current++;
+ }
+ })
+ .catch(() => {
+ availablePageStatus.value = "loadmore";
+ });
+ };
+
+ const selectParam = param => {
+ selectedBaseParam.value = param;
+ selectedStandardValue.value = param.standardValue || "";
+ };
+
+ const handleSelectSubmit = () => {
+ if (!selectedBaseParam.value) {
+ uni.showToast({ title: "璇烽�夋嫨涓�涓熀纭�鍙傛暟", icon: "none" });
+ return;
+ }
+ if (!selectedStandardValue.value) {
+ uni.showToast({ title: "璇疯緭鍏ユ爣鍑嗗��", icon: "none" });
+ return;
+ }
+ addProcessParam({
+ technologyOperationId: processId.value,
+ technologyParamId: selectedBaseParam.value.id,
+ standardValue: selectedStandardValue.value,
+ })
+ .then(() => {
+ uni.showToast({ title: "娣诲姞鎴愬姛" });
+ selectModalVisible.value = false;
+ getParamList();
+ })
+ .catch(err => {
+ uni.showToast({ title: err.msg || "娣诲姞澶辫触", icon: "error" });
+ });
+ };
+
+ const handleEditParam = item => {
+ currentEditParam.value = item;
+ currentEditValue.value = item.standardValue;
+ editModalVisible.value = true;
+ };
+
+ const handleEditSubmit = () => {
+ if (!currentEditValue.value) {
+ uni.showToast({ title: "璇疯緭鍏ユ爣鍑嗗��", icon: "none" });
+ return;
+ }
+ editProcessParam({
+ id: currentEditParam.value.id,
+ technologyOperationId: processId.value,
+ technologyParamId: currentEditParam.value.technologyParamId,
+ standardValue: currentEditValue.value,
+ })
+ .then(() => {
+ uni.showToast({ title: "淇敼鎴愬姛" });
+ editModalVisible.value = false;
+ getParamList();
+ })
+ .catch(err => {
+ uni.showToast({ title: err.msg || "淇敼澶辫触", icon: "error" });
+ });
+ };
+
+ const handleDeleteParam = item => {
+ uni.showModal({
+ title: "鎻愮ず",
+ content: "纭畾瑕佸垹闄よ鍙傛暟閰嶇疆鍚楋紵",
+ success: res => {
+ if (res.confirm) {
+ deleteProcessParam(item.id).then(() => {
+ uni.showToast({ title: "鍒犻櫎鎴愬姛" });
+ getParamList();
+ });
+ }
+ },
+ });
+ };
+
+ const getParamTypeText = type => {
+ const typeMap = { 1: "鏁板��", 2: "鏂囨湰", 3: "涓嬫媺", 4: "鏃堕棿" };
+ return typeMap[type] || "鏈煡";
+ };
+
+ const getParamTypeTag = type => {
+ const typeMap = { 1: "primary", 2: "info", 3: "warning", 4: "success" };
+ return typeMap[type] || "default";
+ };
+
+ onLoad(option => {
+ if (option.id) {
+ processId.value = option.id;
+ processName.value = decodeURIComponent(option.name || "");
+ getParamList();
+ }
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/procurement-common.scss";
+
+ .process-params {
+ min-height: 100vh;
+ background: #f5f5f5;
+ }
+
+ .modal-content {
+ padding: 20rpx 0;
+ width: 100%;
+ }
+
+ .param-scroll-list {
+ height: 500rpx;
+ margin-top: 20rpx;
+ border: 1px solid #eee;
+ border-radius: 8rpx;
+ }
+
+ .param-select-item {
+ padding: 20rpx;
+ border-bottom: 1px solid #f5f5f5;
+ width: 100%;
+ &.active {
+ background-color: #e3f2fd;
+ }
+ }
+
+ .param-main {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8rpx;
+ }
+
+ .param-name {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ }
+
+ .param-code {
+ font-size: 24rpx;
+ color: #999;
+ }
+
+ .standard-input-box {
+ margin-top: 30rpx;
+ display: flex;
+ align-items: center;
+ .label {
+ width: 120rpx;
+ font-size: 28rpx;
+ color: #333;
+ }
+ }
+
+ .edit-info {
+ .edit-label {
+ display: block;
+ margin-bottom: 20rpx;
+ font-size: 28rpx;
+ color: #666;
+ }
+ }
+
+ .fab-button {
+ position: fixed;
+ right: 40rpx;
+ bottom: 60rpx;
+ width: 100rpx;
+ height: 100rpx;
+ background: #2979ff;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4rpx 12rpx rgba(41, 121, 255, 0.4);
+ z-index: 100;
+ }
+</style>
\ No newline at end of file
diff --git a/src/pages/productionManagement/mainProductionPlan/detail.vue b/src/pages/productionManagement/mainProductionPlan/detail.vue
new file mode 100644
index 0000000..867dfe6
--- /dev/null
+++ b/src/pages/productionManagement/mainProductionPlan/detail.vue
@@ -0,0 +1,252 @@
+<template>
+ <view class="production-plan-detail">
+ <PageHeader title="璁″垝璇︽儏"
+ @back="goBack" />
+ <view class="detail-container"
+ v-if="detailData">
+ <!-- 鍩烘湰淇℃伅鍗$墖 -->
+ <view class="detail-card">
+ <view class="card-title">
+ <up-icon name="info-circle"
+ size="18"
+ color="#3c9cff"></up-icon>
+ <text class="title-text">鍩烘湰淇℃伅</text>
+ </view>
+ <view class="card-content">
+ <view class="info-item">
+ <text class="label">涓荤敓浜ц鍒掑彿</text>
+ <text class="value">{{ detailData.mpsNo || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">鏉ユ簮</text>
+ <up-tag :text="detailData.source === '閿�鍞�' ? '閿�鍞�' : '鍐呴儴'"
+ :type="detailData.source === '閿�鍞�' ? 'primary' : 'info'"
+ size="mini" />
+ </view>
+ <view class="info-item">
+ <text class="label">涓嬪彂鐘舵��</text>
+ <up-tag :text="getStatusText(detailData.status)"
+ :type="getStatusType(detailData.status)"
+ size="mini" />
+ </view>
+ </view>
+ </view>
+ <!-- 浜у搧淇℃伅鍗$墖 -->
+ <view class="detail-card">
+ <view class="card-title">
+ <up-icon name="order"
+ size="18"
+ color="#3c9cff"></up-icon>
+ <text class="title-text">浜у搧淇℃伅</text>
+ </view>
+ <view class="card-content">
+ <view class="info-item">
+ <text class="label">浜у搧鍚嶇О</text>
+ <text class="value font-bold">{{ detailData.productName || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">瑙勬牸鍨嬪彿</text>
+ <text class="value">{{ detailData.model || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">鎵�闇�鏁伴噺</text>
+ <text class="value highlight">{{ detailData.qtyRequired || 0 }} {{ detailData.unit || '鏂�' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">宸蹭笅鍙戞暟閲�</text>
+ <text class="value">{{ detailData.quantityIssued || 0 }} {{ detailData.unit || '鏂�' }}</text>
+ </view>
+ </view>
+ </view>
+ <!-- 鏃ユ湡涓庡叧鑱斿崱鐗� -->
+ <view class="detail-card">
+ <view class="card-title">
+ <up-icon name="calendar"
+ size="18"
+ color="#3c9cff"></up-icon>
+ <text class="title-text">鏃ユ湡涓庡叧鑱�</text>
+ </view>
+ <view class="card-content">
+ <view class="info-item">
+ <text class="label">闇�姹傛棩鏈�</text>
+ <text class="value">{{ formatDate(detailData.requiredDate) }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">鎵胯鏃ユ湡</text>
+ <text class="value">{{ formatDate(detailData.promisedDeliveryDate) }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">閿�鍞悎鍚屽彿</text>
+ <text class="value">{{ detailData.salesContractNo || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">瀹㈡埛鍚嶇О</text>
+ <text class="value">{{ detailData.customerName || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">椤圭洰鍚嶇О</text>
+ <text class="value">{{ detailData.projectName || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ <!-- 澶囨敞淇℃伅 -->
+ <view class="detail-card">
+ <view class="card-title">
+ <up-icon name="edit-pen"
+ size="18"
+ color="#3c9cff"></up-icon>
+ <text class="title-text">澶囨敞</text>
+ </view>
+ <view class="card-content">
+ <view class="remark-box">
+ <text class="remark-text">{{ detailData.remark || '鏃犲娉�' }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤璇︽儏鏁版嵁"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, onMounted } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import dayjs from "dayjs";
+ import PageHeader from "@/components/PageHeader.vue";
+
+ const detailData = ref(null);
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鏍煎紡鍖栨棩鏈�
+ const formatDate = date => {
+ return date ? dayjs(date).format("YYYY-MM-DD") : "-";
+ };
+
+ // 鑾峰彇鐘舵�佹枃鏈�
+ const getStatusText = status => {
+ const statusMap = {
+ 0: "寰呬笅鍙�",
+ 1: "閮ㄥ垎涓嬪彂",
+ 2: "宸蹭笅鍙�",
+ };
+ return statusMap[status] || "鏈煡";
+ };
+
+ // 鑾峰彇鐘舵�佺被鍨�
+ const getStatusType = status => {
+ const typeMap = {
+ 0: "warning",
+ 1: "primary",
+ 2: "info",
+ };
+ return typeMap[status] || "info";
+ };
+
+ onLoad(options => {
+ if (options.data) {
+ try {
+ detailData.value = JSON.parse(decodeURIComponent(options.data));
+ } catch (e) {
+ console.error("瑙f瀽鏁版嵁澶辫触", e);
+ uni.showToast({
+ title: "鏁版嵁鍔犺浇澶辫触",
+ icon: "error",
+ });
+ }
+ }
+ });
+</script>
+
+<style scoped lang="scss">
+ .production-plan-detail {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 40rpx;
+ }
+
+ .detail-container {
+ padding: 20rpx;
+ }
+
+ .detail-card {
+ background: #fff;
+ border-radius: 16rpx;
+ margin-bottom: 24rpx;
+ overflow: hidden;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.03);
+
+ .card-title {
+ display: flex;
+ align-items: center;
+ padding: 24rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+ background: #fafafa;
+
+ .title-text {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ margin-left: 12rpx;
+ }
+ }
+
+ .card-content {
+ padding: 10rpx 24rpx;
+
+ .info-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20rpx 0;
+ border-bottom: 1rpx solid #f9f9f9;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .label {
+ font-size: 26rpx;
+ color: #999;
+ }
+
+ .value {
+ font-size: 26rpx;
+ color: #333;
+ text-align: right;
+ max-width: 70%;
+
+ &.font-bold {
+ font-weight: bold;
+ }
+
+ &.highlight {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+ }
+ }
+
+ .remark-box {
+ padding: 20rpx 0;
+
+ .remark-text {
+ font-size: 26rpx;
+ color: #666;
+ line-height: 1.5;
+ }
+ }
+ }
+ }
+
+ .no-data {
+ padding-top: 200rpx;
+ }
+</style>
diff --git a/src/pages/productionManagement/mainProductionPlan/index.vue b/src/pages/productionManagement/mainProductionPlan/index.vue
new file mode 100644
index 0000000..5b4eec1
--- /dev/null
+++ b/src/pages/productionManagement/mainProductionPlan/index.vue
@@ -0,0 +1,300 @@
+<template>
+ <view class="main-production-plan">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="涓荤敓浜ц鍒�" @back="goBack" />
+
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input
+ class="search-text"
+ placeholder="璇疯緭鍏ヨ鍒掑彿鎴栦骇鍝佸悕绉�"
+ v-model="searchForm.keyword"
+ @change="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="list-container" v-if="tableData.length > 0" @scrolltolower="loadMore">
+ <view v-for="(item, index) in tableData" :key="item.id || index" @click="goDetail(item)">
+ <view 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.mpsNo }}</text>
+ </view>
+ <view class="item-right">
+ <up-tag :text="getStatusText(item.status)" :type="getStatusType(item.status)" size="mini" />
+ </view>
+ </view>
+ <up-divider></up-divider>
+
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">浜у搧鍚嶇О</text>
+ <text class="detail-value">{{ item.productName || '-' }}</text>
+ </view>
+ <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 highlight">{{ item.qtyRequired || 0 }} {{ item.unit || '鏂�' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">闇�姹傛棩鏈�</text>
+ <text class="detail-value">{{ formatDate(item.requiredDate) }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏉ユ簮</text>
+ <text class="detail-value">{{ item.source === '閿�鍞�' ? '閿�鍞�' : '鍐呴儴' }}</text>
+ </view>
+ </view>
+ <view class="item-footer">
+ <text class="more-detail">鏌ョ湅璇︽儏</text>
+ <up-icon name="arrow-right" size="14" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus" v-if="tableData.length >= page.size" />
+ </scroll-view>
+
+ <view v-else class="no-data">
+ <up-empty mode="data" text="鏆傛棤涓荤敓浜ц鍒掓暟鎹�"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+import { onShow } from '@dcloudio/uni-app';
+import dayjs from "dayjs";
+import { productionPlanListPage } from "@/api/productionManagement/productionPlan.js";
+import PageHeader from "@/components/PageHeader.vue";
+
+const { proxy } = getCurrentInstance();
+
+// 鍔犺浇鐘舵��
+const loading = ref(false);
+const loadStatus = ref('loadmore');
+// 鍒楄〃鏁版嵁
+const tableData = ref([]);
+
+// 鍒嗛〉閰嶇疆
+const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+});
+
+// 鎼滅储琛ㄥ崟鏁版嵁
+const data = reactive({
+ searchForm: {
+ keyword: "",
+ mpsNo: "",
+ productName: ""
+ },
+});
+const { searchForm } = toRefs(data);
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.navigateBack();
+};
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (date) => {
+ return date ? dayjs(date).format('YYYY-MM-DD') : '-';
+};
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const statusMap = {
+ 0: "寰呬笅鍙�",
+ 1: "閮ㄥ垎涓嬪彂",
+ 2: "宸蹭笅鍙�",
+ };
+ return statusMap[status] || "鏈煡";
+};
+
+// 鑾峰彇鐘舵�佺被鍨� (uView tag type)
+const getStatusType = (status) => {
+ const typeMap = {
+ 0: "warning",
+ 1: "primary",
+ 2: "info",
+ };
+ return typeMap[status] || "info";
+};
+
+// 鏌ヨ鍒楄〃
+const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
+ getList();
+};
+
+// 鍔犺浇鏇村
+const loadMore = () => {
+ if (loadStatus.value === 'nomore' || loading.value) return;
+ page.current++;
+ getList();
+};
+
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = () => {
+ loading.value = true;
+ loadStatus.value = 'loading';
+
+ // 鏋勯�犺姹傚弬鏁�
+ // PC绔帴鍙f敮鎸� mpsNo, productName 绛夛紝杩欓噷绠�鍗曞鐞嗭紝濡傛灉 keyword 瀛樺湪锛屽垯灏濊瘯鍖归厤
+ const params = {
+ current: page.current,
+ size: page.size,
+ mpsNo: searchForm.value.keyword, // 绠�鍗曞鐞嗭細鎼滅储鍙�
+ productName: searchForm.value.keyword // 绠�鍗曞鐞嗭細鎼滅储鍚嶇О
+ };
+
+ productionPlanListPage(params).then((res) => {
+ loading.value = false;
+ const records = res.data.records || [];
+ if (page.current === 1) {
+ tableData.value = records;
+ } else {
+ tableData.value = [...tableData.value, ...records];
+ }
+
+ if (records.length < page.size) {
+ loadStatus.value = 'nomore';
+ } else {
+ loadStatus.value = 'loadmore';
+ }
+ page.total = res.data.total || 0;
+ }).catch(() => {
+ loading.value = false;
+ loadStatus.value = 'loadmore';
+ uni.showToast({
+ title: '鍔犺浇澶辫触',
+ icon: 'error'
+ });
+ });
+};
+
+// 璺宠浆璇︽儏
+const goDetail = (item) => {
+ uni.navigateTo({
+ url: `/pages/productionManagement/mainProductionPlan/detail?data=${encodeURIComponent(JSON.stringify(item))}`
+ });
+};
+
+// 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
+onShow(() => {
+ handleQuery();
+});
+</script>
+
+<style scoped lang="scss">
+@import '@/styles/sales-common.scss';
+
+.main-production-plan {
+ min-height: 100vh;
+ background: #f8f9fa;
+ display: flex;
+ flex-direction: column;
+}
+
+.list-container {
+ flex: 1;
+ height: 0;
+}
+
+.ledger-item {
+ background: #fff;
+ margin: 20rpx;
+ padding: 20rpx;
+ border-radius: 12rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+ .item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 10rpx;
+
+ .item-left {
+ display: flex;
+ align-items: center;
+
+ .document-icon {
+ width: 40rpx;
+ height: 40rpx;
+ background: #3c9cff;
+ border-radius: 8rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 16rpx;
+ }
+
+ .item-id {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+ }
+
+ .item-details {
+ padding: 10rpx 0;
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+
+ .detail-label {
+ font-size: 26rpx;
+ color: #999;
+ }
+
+ .detail-value {
+ font-size: 26rpx;
+ color: #333;
+
+ &.highlight {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+ }
+ }
+ }
+
+ .item-footer {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ padding-top: 16rpx;
+ border-top: 1rpx solid #f0f0f0;
+
+ .more-detail {
+ font-size: 24rpx;
+ color: #999;
+ margin-right: 8rpx;
+ }
+ }
+}
+
+.no-data {
+ padding-top: 200rpx;
+}
+</style>
diff --git a/src/pages/productionManagement/processRoute/index.vue b/src/pages/productionManagement/processRoute/index.vue
new file mode 100644
index 0000000..b993d99
--- /dev/null
+++ b/src/pages/productionManagement/processRoute/index.vue
@@ -0,0 +1,287 @@
+<template>
+ <view class="process-route">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="宸ヨ壓璺嚎"
+ @back="goBack" />
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ヨ鏍煎悕绉版悳绱�"
+ v-model="searchForm.model"
+ @change="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="list-container"
+ v-if="tableData.length > 0"
+ @scrolltolower="loadMore">
+ <view v-for="(item, index) in tableData"
+ :key="item.id || index"
+ @click="goDetail(item)">
+ <view class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="share-square"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.processRouteCode }}</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">浜у搧鍚嶇О</text>
+ <text class="detail-value font-bold">{{ item.productName || '-' }}</text>
+ </view>
+ <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">BOM缂栧彿</text>
+ <text class="detail-value">{{ item.bomNo || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎻忚堪</text>
+ <text class="detail-value">{{ item.description || '-' }}</text>
+ </view>
+ </view>
+ <view class="item-footer">
+ <text class="more-detail">璺嚎椤圭洰</text>
+ <up-icon name="arrow-right"
+ size="14"
+ color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus"
+ v-if="tableData.length >= page.size" />
+ </scroll-view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤宸ヨ壓璺嚎鏁版嵁"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import { listPage } from "@/api/productionManagement/processRoute.js";
+ import PageHeader from "@/components/PageHeader.vue";
+
+ const { proxy } = getCurrentInstance();
+
+ // 鍔犺浇鐘舵��
+ const loading = ref(false);
+ const loadStatus = ref("loadmore");
+ // 鍒楄〃鏁版嵁
+ const tableData = ref([]);
+
+ // 鍒嗛〉閰嶇疆
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+
+ // 鎼滅储琛ㄥ崟鏁版嵁
+ const data = reactive({
+ searchForm: {
+ model: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鏌ヨ鍒楄〃
+ const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
+ getList();
+ };
+
+ // 鍔犺浇鏇村
+ const loadMore = () => {
+ if (loadStatus.value === "nomore" || loading.value) return;
+ page.current++;
+ getList();
+ };
+
+ // 鑾峰彇鍒楄〃鏁版嵁
+ const getList = () => {
+ loading.value = true;
+ loadStatus.value = "loading";
+
+ const params = {
+ current: page.current,
+ size: page.size,
+ model: searchForm.value.model,
+ };
+
+ listPage(params)
+ .then(res => {
+ loading.value = false;
+ const records = res.data.records || [];
+ if (page.current === 1) {
+ tableData.value = records;
+ } else {
+ tableData.value = [...tableData.value, ...records];
+ }
+
+ if (records.length < page.size) {
+ loadStatus.value = "nomore";
+ } else {
+ loadStatus.value = "loadmore";
+ }
+ page.total = res.data.total || 0;
+ })
+ .catch(() => {
+ loading.value = false;
+ loadStatus.value = "loadmore";
+ uni.showToast({
+ title: "鍔犺浇澶辫触",
+ icon: "error",
+ });
+ });
+ };
+
+ // 璺宠浆璺嚎椤圭洰
+ const goDetail = item => {
+ uni.navigateTo({
+ url: `/pages/productionManagement/processRoute/items?id=${
+ item.id
+ }&processRouteCode=${
+ item.processRouteCode
+ }&productName=${encodeURIComponent(
+ item.productName || ""
+ )}&model=${encodeURIComponent(item.model || "")}&bomNo=${
+ item.bomNo || ""
+ }&bomId=${item.bomId || ""}&description=${encodeURIComponent(
+ item.description || ""
+ )}`,
+ });
+ };
+
+ // 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
+ onShow(() => {
+ handleQuery();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/sales-common.scss";
+
+ .process-route {
+ min-height: 100vh;
+ background: #f8f9fa;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .list-container {
+ flex: 1;
+ height: 0;
+ }
+
+ .ledger-item {
+ background: #fff;
+ margin: 20rpx;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
+
+ .item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 12rpx;
+
+ .item-left {
+ display: flex;
+ align-items: center;
+
+ .document-icon {
+ width: 44rpx;
+ height: 44rpx;
+ background: #3c9cff;
+ border-radius: 10rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 20rpx;
+ }
+
+ .item-id {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+ }
+
+ .item-details {
+ padding: 16rpx 0;
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 16rpx;
+
+ .detail-label {
+ font-size: 26rpx;
+ color: #999;
+ min-width: 140rpx;
+ }
+
+ .detail-value {
+ font-size: 26rpx;
+ color: #333;
+ text-align: right;
+ flex: 1;
+
+ &.font-bold {
+ font-weight: bold;
+ }
+ }
+ }
+ }
+
+ .item-footer {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ padding-top: 16rpx;
+ border-top: 1rpx solid #f0f0f0;
+
+ .more-detail {
+ font-size: 24rpx;
+ color: #3c9cff;
+ margin-right: 8rpx;
+ }
+ }
+ }
+
+ .no-data {
+ padding-top: 200rpx;
+ }
+</style>
diff --git a/src/pages/productionManagement/processRoute/items.vue b/src/pages/productionManagement/processRoute/items.vue
new file mode 100644
index 0000000..9c2b266
--- /dev/null
+++ b/src/pages/productionManagement/processRoute/items.vue
@@ -0,0 +1,554 @@
+<template>
+ <view class="process-route-items">
+ <PageHeader title="璺嚎椤圭洰"
+ @back="goBack" />
+ <!-- 璺嚎鍩虹淇℃伅鍗$墖 -->
+ <view class="route-info-card">
+ <view class="info-row">
+ <text class="label">宸ヨ壓璺嚎缂栧彿</text>
+ <text class="value">{{ routeInfo.processRouteCode || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">浜у搧鍚嶇О</text>
+ <text class="value">{{ routeInfo.productName || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">瑙勬牸鍚嶇О</text>
+ <text class="value">{{ routeInfo.model || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">BOM缂栧彿</text>
+ <text class="value">{{ routeInfo.bomNo || '-' }}</text>
+ </view>
+ </view>
+ <!-- 閫夐」鍗″垏鎹� -->
+ <view class="tabs-box">
+ <up-tabs :list="tabsList"
+ @click="handleTabClick"
+ :current="currentTab"></up-tabs>
+ </view>
+ <!-- 宸ュ簭椤圭洰鍒楄〃 -->
+ <scroll-view scroll-y
+ class="content-scroll"
+ v-if="currentTab === 0">
+ <view v-if="itemsList.length > 0">
+ <view v-for="(item, index) in itemsList"
+ :key="index"
+ class="process-card">
+ <view class="card-header">
+ <view class="index-badge">{{ index + 1 }}</view>
+ <text class="process-name">{{ item.technologyOperationName || item.operationName || '-' }}</text>
+ </view>
+ <view class="card-content">
+ <view class="detail-row">
+ <text class="detail-label">鍏宠仈浜у搧</text>
+ <text class="detail-value">{{ item.productName || '-' }}</text>
+ </view>
+ <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="tag-row">
+ <up-tag v-if="item.isQuality"
+ text="璐ㄦ"
+ type="primary"
+ size="mini"
+ plain />
+ <up-tag v-if="item.isProduction"
+ text="鐢熶骇"
+ 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"
+ @click="showParams(item)">
+ <text class="action-text">鏌ョ湅鍙傛暟鍒楄〃</text>
+ <up-icon name="arrow-right"
+ size="14"
+ color="#3c9cff"></up-icon>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤璺嚎椤圭洰"></up-empty>
+ </view>
+ </scroll-view>
+ <!-- BOM 缁撴瀯灞曠ず -->
+ <scroll-view scroll-y
+ class="content-scroll"
+ v-if="currentTab === 1">
+ <view v-if="bomList.length > 0"
+ class="bom-tree">
+ <view v-for="(node, nIndex) in flatBomList"
+ :key="nIndex"
+ class="bom-node"
+ :style="{ paddingLeft: (node.level * 40) + 'rpx' }">
+ <view class="bom-node-inner">
+ <view class="bom-line"
+ v-if="node.level > 0"></view>
+ <view class="bom-content">
+ <view class="bom-header">
+ <text class="bom-product">{{ node.productName }}</text>
+ <text class="bom-model"
+ v-if="node.model">({{ node.model }})</text>
+ </view>
+ <view class="bom-details">
+ <text class="bom-info">宸ュ簭: {{ node.operationName || '-' }}</text>
+ <text class="bom-info">鎵�闇�: {{ node.unitQuantity || 0 }} {{ node.unit || '' }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤 BOM 缁撴瀯"></up-empty>
+ </view>
+ </scroll-view>
+ <!-- 鍙傛暟鍒楄〃寮圭獥 -->
+ <up-popup :show="showPopup"
+ mode="bottom"
+ @close="showPopup = false"
+ round="10">
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="title">鍙傛暟鍒楄〃 - {{ currentItem.technologyOperationName || currentItem.operationName }}</text>
+ <up-icon name="close"
+ size="20"
+ @click="showPopup = false"></up-icon>
+ </view>
+ <scroll-view scroll-y
+ class="param-list">
+ <view v-if="paramList.length > 0">
+ <view v-for="(param, pIndex) in paramList"
+ :key="pIndex"
+ class="param-item">
+ <view class="param-row">
+ <text class="param-label">鍙傛暟鍚嶇О锛�</text>
+ <text class="param-value">{{ param.paramName || '-' }}</text>
+ </view>
+ <view class="param-row">
+ <text class="param-label">鏍囧噯鍊硷細</text>
+ <text class="param-value">{{ param.standardValue || '-' }}</text>
+ </view>
+ <view class="param-row">
+ <text class="param-label">鍗曚綅锛�</text>
+ <text class="param-value">{{ param.unit || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-record">
+ <text>鏆傛棤鍙傛暟璁板綍</text>
+ </view>
+ </scroll-view>
+ </view>
+ </up-popup>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, computed } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import {
+ findProcessRouteItemList,
+ getProcessParamList,
+ queryBomList,
+ } from "@/api/productionManagement/processRoute.js";
+ import {
+ queryOrderBomList,
+ findProcessParamListOrder,
+ } from "@/api/productionManagement/productionOrder.js";
+ import PageHeader from "@/components/PageHeader.vue";
+
+ const routeInfo = ref({});
+ const itemsList = ref([]);
+ const bomList = ref([]);
+ const loading = ref(false);
+ const pageType = ref("route"); // route | order
+
+ // 閫夐」鍗�
+ const tabsList = reactive([{ name: "璺嚎椤圭洰" }, { name: "BOM缁撴瀯" }]);
+ const currentTab = ref(0);
+
+ // 寮圭獥鐩稿叧
+ const showPopup = ref(false);
+ const currentItem = ref({});
+ const paramList = ref([]);
+ const paramLoading = ref(false);
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const handleTabClick = item => {
+ currentTab.value = item.index;
+ if (item.index === 1 && bomList.value.length === 0) {
+ fetchBom();
+ }
+ };
+
+ // 鎵佸钩鍖� BOM 鏍戠敤浜庡睍绀�
+ const flatBomList = computed(() => {
+ const result = [];
+ const flatten = (nodes, level = 0) => {
+ nodes.forEach(node => {
+ result.push({ ...node, level });
+ if (node.children && node.children.length > 0) {
+ flatten(node.children, level + 1);
+ }
+ });
+ };
+ flatten(bomList.value);
+ return result;
+ });
+
+ onLoad(options => {
+ if (options.id) {
+ pageType.value = options.type || "route";
+ routeInfo.value = {
+ id: options.id,
+ processRouteCode: options.processRouteCode || "",
+ productName: decodeURIComponent(options.productName || ""),
+ model: decodeURIComponent(options.model || ""),
+ bomNo: options.bomNo || "",
+ bomId: options.bomId || "",
+ description: decodeURIComponent(options.description || ""),
+ orderId: options.orderId || "",
+ };
+ fetchItems(options.id);
+ }
+ });
+
+ const fetchItems = id => {
+ loading.value = true;
+ findProcessRouteItemList({ routeId: id, orderId: routeInfo.value.orderId })
+ .then(res => {
+ itemsList.value = res.data || [];
+ loading.value = false;
+ })
+ .catch(() => {
+ loading.value = false;
+ uni.showToast({
+ title: "鑾峰彇椤圭洰澶辫触",
+ icon: "error",
+ });
+ });
+ };
+
+ const fetchBom = () => {
+ console.log(routeInfo.value.bomId, "routeInfo.value.bomId");
+
+ if (!routeInfo.value.bomId) return;
+ loading.value = true;
+ const api = pageType.value === "order" ? queryOrderBomList : queryBomList;
+ api(routeInfo.value.bomId)
+ .then(res => {
+ bomList.value = res.data || [];
+ loading.value = false;
+ })
+ .catch(() => {
+ loading.value = false;
+ uni.showToast({
+ title: "鑾峰彇 BOM 澶辫触",
+ icon: "error",
+ });
+ });
+ };
+
+ const showParams = item => {
+ currentItem.value = item;
+ showPopup.value = true;
+ paramLoading.value = true;
+ paramList.value = [];
+
+ const api =
+ pageType.value === "order"
+ ? findProcessParamListOrder
+ : getProcessParamList;
+ const params =
+ pageType.value === "order"
+ ? {
+ productionOrderRoutingOperationId: item.id,
+ productionOrderId: routeInfo.value.orderId,
+ }
+ : { technologyRoutingOperationId: item.id };
+
+ api(params)
+ .then(res => {
+ paramList.value = res.data || [];
+ paramLoading.value = false;
+ })
+ .catch(() => {
+ paramLoading.value = false;
+ uni.showToast({
+ title: "鑾峰彇鍙傛暟澶辫触",
+ icon: "error",
+ });
+ });
+ };
+</script>
+
+<style scoped lang="scss">
+ .process-route-items {
+ min-height: 100vh;
+ background: #f8f9fa;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .route-info-card {
+ background: #fff;
+ margin: 20rpx;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+ .info-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .label {
+ font-size: 26rpx;
+ color: #999;
+ }
+ .value {
+ font-size: 26rpx;
+ color: #333;
+ font-weight: bold;
+ }
+ }
+ }
+
+ .tabs-box {
+ background: #fff;
+ margin-bottom: 10rpx;
+ }
+
+ .content-scroll {
+ flex: 1;
+ height: 0;
+ padding: 0 20rpx;
+ }
+
+ .process-card {
+ background: #fff;
+ margin-bottom: 24rpx;
+ border-radius: 16rpx;
+ overflow: hidden;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
+
+ .card-header {
+ display: flex;
+ align-items: center;
+ padding: 20rpx 24rpx;
+ background: #fcfcfc;
+ border-bottom: 1rpx solid #f5f5f5;
+
+ .index-badge {
+ width: 40rpx;
+ height: 40rpx;
+ background: #3c9cff;
+ color: #fff;
+ border-radius: 20rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 24rpx;
+ margin-right: 20rpx;
+ }
+
+ .process-name {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+
+ .card-content {
+ padding: 24rpx;
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+
+ .detail-label {
+ font-size: 24rpx;
+ color: #999;
+ }
+ .detail-value {
+ font-size: 24rpx;
+ color: #666;
+ }
+ }
+
+ .tag-row {
+ display: flex;
+ gap: 16rpx;
+ margin-top: 10rpx;
+ }
+ }
+
+ .card-footer {
+ padding: 16rpx 24rpx;
+ border-top: 1rpx dashed #eee;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .action-text {
+ font-size: 24rpx;
+ color: #3c9cff;
+ }
+ }
+ }
+
+ /* BOM 鏍戞牱寮� */
+ .bom-tree {
+ padding: 20rpx 0;
+ }
+
+ .bom-node {
+ position: relative;
+ margin-bottom: 20rpx;
+ }
+
+ .bom-node-inner {
+ background: #fff;
+ padding: 20rpx;
+ border-radius: 12rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
+ display: flex;
+ align-items: center;
+ }
+
+ .bom-line {
+ position: absolute;
+ left: -20rpx;
+ top: 50%;
+ width: 20rpx;
+ height: 2rpx;
+ background: #ddd;
+ }
+
+ .bom-content {
+ flex: 1;
+ }
+
+ .bom-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8rpx;
+
+ .bom-product {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ }
+
+ .bom-model {
+ font-size: 24rpx;
+ color: #999;
+ margin-left: 10rpx;
+ }
+ }
+
+ .bom-details {
+ display: flex;
+ justify-content: space-between;
+
+ .bom-info {
+ font-size: 24rpx;
+ color: #666;
+ }
+ }
+
+ .no-data {
+ padding-top: 100rpx;
+ }
+
+ /* 寮圭獥鏍峰紡 */
+ .popup-content {
+ background: #fff;
+ padding: 30rpx;
+ max-height: 70vh;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 30rpx;
+ border-bottom: 1rpx solid #eee;
+
+ .title {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+
+ .param-list {
+ flex: 1;
+ height: 0;
+ padding-top: 20rpx;
+ }
+
+ .param-item {
+ padding: 20rpx;
+ background: #f9f9f9;
+ border-radius: 12rpx;
+ margin-bottom: 16rpx;
+
+ .param-row {
+ display: flex;
+ margin-bottom: 8rpx;
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .param-label {
+ font-size: 24rpx;
+ color: #999;
+ width: 140rpx;
+ }
+ .param-value {
+ font-size: 24rpx;
+ color: #333;
+ flex: 1;
+ }
+ }
+ }
+
+ .no-record {
+ padding: 100rpx 0;
+ text-align: center;
+ color: #999;
+ font-size: 26rpx;
+ }
+</style>
diff --git a/src/pages/productionManagement/processStatistics/index.vue b/src/pages/productionManagement/processStatistics/index.vue
new file mode 100644
index 0000000..a7cdd95
--- /dev/null
+++ b/src/pages/productionManagement/processStatistics/index.vue
@@ -0,0 +1,370 @@
+<template>
+ <view class="process-statistics">
+ <PageHeader title="宸ュ簭鐢熶骇瀹炲喌"
+ @back="goBack" />
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="date-picker-container"
+ @click="showCalendar = true">
+ <view class="date-input">
+ <up-icon name="calendar"
+ size="20"
+ color="#999"></up-icon>
+ <text class="date-text"
+ :class="{ 'placeholder': !searchForm.startDate }">{{ dateRangeText }}</text>
+ <view v-if="searchForm.startDate"
+ class="clear-icon-wrapper"
+ @click.stop="handleClearDate">
+ <up-icon name="close-circle-fill"
+ size="18"
+ color="#c0c4cc"></up-icon>
+ </view>
+ </view>
+ <view class="search-btn-wrapper">
+ <up-button type="primary"
+ size="small"
+ text="鎼滅储"
+ @click.stop="handleQuery"></up-button>
+ </view>
+ </view>
+ </view>
+ <!-- 缁熻鍗$墖鍒楄〃 -->
+ <scroll-view scroll-y
+ class="stats-list">
+ <view v-if="loading"
+ class="loading-box">
+ <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+ </view>
+ <view v-else-if="statsData.length > 0"
+ class="card-grid">
+ <view v-for="(item, index) in statsData"
+ :key="index"
+ class="stats-card">
+ <view class="card-header">
+ <text class="process-tag">{{ item.name }}</text>
+ <view class="header-details">
+ <view class="detail-row">
+ <text class="label">璁″垝鏁�</text>
+ <text class="value">{{ item.planned }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="label">鑹搧鏁�</text>
+ <text class="value good">{{ item.good }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="label">涓嶈壇鍝�</text>
+ <text class="value bad">{{ item.bad }}</text>
+ </view>
+ </view>
+ </view>
+ <view class="card-body">
+ <view class="main-stat">
+ <text class="big-number">{{ item.total }}</text>
+ <text class="sub-label">鐢熶骇浠诲姟鏁�</text>
+ </view>
+ </view>
+ <view class="card-footer">
+ <view class="progress-section">
+ <view class="progress-header">
+ <text class="progress-label">鐢熶骇杩涘害</text>
+ <text class="percentage-text">{{ item.percentage }}%</text>
+ </view>
+ <up-line-progress :percentage="Math.min(item.percentage, 100)"
+ :activeColor="getProgressColor(item.percentage)"
+ :show-text="false"
+ height="8"></up-line-progress>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤宸ュ簭缁熻鏁版嵁"></up-empty>
+ </view>
+ </scroll-view>
+ <!-- 鏃ュ巻閫夋嫨鍣� -->
+ <up-calendar :show="showCalendar"
+ mode="range"
+ :maxDate="maxDate"
+ minDate="2026-01-01"
+ :monthNum="monthNum"
+ @confirm="onDateConfirm"
+ @close="showCalendar = false"></up-calendar>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, onMounted, computed } from "vue";
+ import { getOperationStatistics } from "@/api/productionManagement/workOrder.js";
+ import PageHeader from "@/components/PageHeader.vue";
+ import dayjs from "dayjs";
+
+ const loading = ref(false);
+ const showCalendar = ref(false);
+ const dateRange = ref([]);
+ const maxDate = dayjs().format("YYYY-MM-DD");
+ const monthNum = computed(() => {
+ const min = dayjs("2022-02-01");
+ const max = dayjs(maxDate);
+ return max.diff(min, "month") + 1;
+ });
+ // const minDate = dayjs().subtract(7, "day").format("YYYY-MM-DD");
+
+ const searchForm = reactive({
+ startDate: "",
+ endDate: "",
+ });
+
+ const statsData = ref([]);
+
+ const dateRangeText = computed(() => {
+ if (searchForm.startDate && searchForm.endDate) {
+ return `${searchForm.startDate} 鑷� ${searchForm.endDate}`;
+ }
+ return "璇烽�夋嫨鏃ユ湡鍖洪棿";
+ });
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const getProgressColor = percentage => {
+ if (percentage >= 100) return "#67c23a";
+ if (percentage >= 50) return "#3c9cff";
+ if (percentage >= 25) return "#e6a23c";
+ return "#f56c6c";
+ };
+
+ const onDateConfirm = e => {
+ searchForm.startDate = e[0];
+ searchForm.endDate = e[e.length - 1];
+ showCalendar.value = false;
+ handleQuery();
+ };
+
+ const getList = () => {
+ loading.value = true;
+ const params = {
+ startDate: searchForm.startDate,
+ endDate: searchForm.endDate,
+ };
+ getOperationStatistics(params)
+ .then(res => {
+ statsData.value = (res.data || []).map(item => ({
+ name: item.operationName || "-",
+ total: item.productionTaskCount || 0,
+ planned: item.planQuantity || 0,
+ good: item.goodQuantity || 0,
+ bad: item.scrapQty || 0,
+ percentage: Number(item.completionStatus || 0),
+ }));
+ })
+ .finally(() => {
+ loading.value = false;
+ });
+ };
+
+ const handleQuery = () => {
+ getList();
+ };
+
+ const handleClearDate = () => {
+ searchForm.startDate = "";
+ searchForm.endDate = "";
+ handleQuery();
+ };
+
+ onMounted(() => {
+ // 榛樿鏃堕棿缃┖
+ searchForm.startDate = "";
+ searchForm.endDate = "";
+ getList();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/procurement-common.scss";
+
+ .process-statistics {
+ min-height: 100vh;
+ background-color: #f5f7fa;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .search-section {
+ background-color: #fff;
+ padding: 24rpx 30rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.02);
+ }
+
+ .date-picker-container {
+ display: flex;
+ align-items: center;
+ width: 100%;
+
+ .date-input {
+ flex: 1;
+ height: 80rpx;
+ background-color: #f5f7fa;
+ border: 1rpx solid #e4e7ed;
+ border-radius: 12rpx;
+ display: flex;
+ align-items: center;
+ padding: 0 24rpx;
+ margin-right: 20rpx;
+ transition: all 0.3s;
+
+ &:active {
+ background-color: #ebedf0;
+ }
+
+ .date-text {
+ font-size: 28rpx;
+ color: #303133;
+ margin-left: 16rpx;
+ flex: 1;
+
+ &.placeholder {
+ color: #c0c4cc;
+ }
+ }
+
+ .clear-icon {
+ padding: 10rpx;
+ margin-right: -10rpx;
+ }
+ }
+
+ .search-btn-wrapper {
+ width: 140rpx;
+ }
+ }
+
+ .stats-list {
+ flex: 1;
+ height: 0;
+ padding: 0 24rpx 40rpx;
+ }
+
+ .loading-box {
+ display: flex;
+ justify-content: center;
+ padding-top: 100rpx;
+ }
+
+ .card-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 24rpx;
+ }
+
+ .stats-card {
+ background: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 30rpx;
+
+ .process-tag {
+ background-color: #e6f7ff;
+ color: #1890ff;
+ padding: 6rpx 16rpx;
+ border-radius: 8rpx;
+ font-size: 26rpx;
+ font-weight: bold;
+ }
+
+ .header-details {
+ display: flex;
+ flex-direction: column;
+ gap: 4rpx;
+
+ .detail-row {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 12rpx;
+
+ .label {
+ font-size: 22rpx;
+ color: #999;
+ }
+
+ .value {
+ font-size: 24rpx;
+ color: #333;
+ font-weight: bold;
+ min-width: 60rpx;
+ text-align: right;
+
+ &.good {
+ color: #52c41a;
+ }
+ &.bad {
+ color: #f56c6c;
+ }
+ }
+ }
+ }
+ }
+
+ .card-body {
+ padding-bottom: 30rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+
+ .main-stat {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ .big-number {
+ font-size: 56rpx;
+ font-weight: bold;
+ color: #333;
+ line-height: 1;
+ }
+
+ .sub-label {
+ font-size: 26rpx;
+ color: #666;
+ margin-top: 12rpx;
+ }
+ }
+ }
+
+ .card-footer {
+ padding-top: 24rpx;
+
+ .progress-section {
+ .progress-header {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+
+ .progress-label {
+ font-size: 24rpx;
+ color: #999;
+ }
+
+ .percentage-text {
+ font-size: 24rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+ }
+ }
+ }
+
+ .no-data {
+ padding-top: 100rpx;
+ }
+</style>
diff --git a/src/pages/productionManagement/productionAccounting/index.vue b/src/pages/productionManagement/productionAccounting/index.vue
new file mode 100644
index 0000000..87ec382
--- /dev/null
+++ b/src/pages/productionManagement/productionAccounting/index.vue
@@ -0,0 +1,506 @@
+<template>
+ <view class="production-accounting">
+ <PageHeader title="鐢熶骇鏍哥畻"
+ @back="goBack" />
+ <!-- 绛涢�夊尯鍩� -->
+ <view class="filter-section">
+ <view class="date-type-selector">
+ <up-tabs :list="dateTypeList"
+ :current="currentDateTypeIndex"
+ @change="handleDateTypeChange"
+ :activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
+ lineWidth="30"
+ lineHeight="3" />
+ </view>
+ <view class="date-picker-bar"
+ @click="showDatePicker = true">
+ <view class="date-display">
+ <up-icon name="calendar"
+ size="20"
+ color="#2979ff"></up-icon>
+ <text class="date-text">{{ dateDisplayText }}</text>
+ </view>
+ <up-icon name="arrow-right"
+ size="16"
+ color="#999"></up-icon>
+ </view>
+ </view>
+ <!-- 姹囨�诲垪琛� -->
+ <view class="summary-section"
+ v-if="!showDetail">
+ <view class="section-header">
+ <text class="section-title">鐢熶骇浜哄憳姹囨��</text>
+ </view>
+ <view class="ledger-list"
+ v-if="summaryList.length > 0">
+ <view v-for="(item, index) in summaryList"
+ :key="index"
+ class="ledger-item"
+ @click="handleRowClick(item)">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="user-icon">
+ <up-icon name="account"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.schedulingUserName || '鏈煡' }}</text>
+ </view>
+ <view class="item-right">
+ <up-icon name="arrow-right"
+ size="16"
+ color="#999"></up-icon>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-grid">
+ <view class="grid-item">
+ <text class="grid-label">浜ч噺</text>
+ <text class="grid-value">{{ item.finishedNum || 0 }}</text>
+ </view>
+ <view class="grid-item">
+ <text class="grid-label">宸ヨ祫</text>
+ <text class="grid-value highlight">楼{{ item.wages || 0 }}</text>
+ </view>
+ <view class="grid-item">
+ <text class="grid-label">鍚堟牸鐜�</text>
+ <text class="grid-value">{{ formatOutputRate(item.outputRate) }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤姹囨�绘暟鎹�" />
+ </view>
+ </view>
+ <!-- 鏄庣粏鍒楄〃 (鐐瑰嚮姹囨�昏鍚庢樉绀�) -->
+ <view class="detail-section"
+ v-else>
+ <view class="section-header back-bar"
+ @click="showDetail = false">
+ <up-icon name="arrow-left"
+ size="16"
+ 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"
+ :key="index"
+ class="ledger-item no-click">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="product-icon">
+ <up-icon name="shopping-cart"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.productName }}</text>
+ </view>
+ <view class="item-tag">
+ <text class="tag-text">{{ item.schedulingDate }}</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.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>
+ <view class="detail-row">
+ <text class="detail-label">宸ュ簭</text>
+ <text class="detail-value">{{ item.process }}</text>
+ </view>
+ <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>
+ <text class="detail-value">{{ item.workHours }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">宸ヨ祫</text>
+ <text class="detail-value highlight">楼{{ item.wages }}</text>
+ </view>
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus"
+ @loadmore="getDetailList" />
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤鏄庣粏鏁版嵁" />
+ </view>
+ </view>
+ <!-- 鏃ユ湡閫夋嫨鍣� -->
+ <up-datetime-picker :show="showDatePicker"
+ v-model="pickerValue"
+ :mode="currentDateType === 'day' ? 'date' : 'year-month'"
+ @confirm="handleDateConfirm"
+ @cancel="showDatePicker = false" />
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, computed, onMounted } from "vue";
+ import {
+ salesLedgerProductionAccountingList,
+ salesLedgerProductionAccountingListProductionDetails,
+ } from "@/api/productionManagement/productionCosting";
+ import PageHeader from "@/components/PageHeader.vue";
+ import dayjs from "dayjs";
+
+ // 绛涢�夌浉鍏�
+ const dateTypeList = [{ name: "鏃�" }, { name: "鏈�" }];
+ const currentDateTypeIndex = ref(0);
+ const currentDateType = computed(() =>
+ currentDateTypeIndex.value === 0 ? "day" : "month"
+ );
+ const showDatePicker = ref(false);
+ const pickerValue = ref(Date.now());
+ const selectedDate = ref(dayjs().format("YYYY-MM-DD"));
+
+ const dateDisplayText = computed(() => {
+ return currentDateType.value === "day"
+ ? selectedDate.value
+ : dayjs(selectedDate.value).format("YYYY-MM");
+ });
+
+ // 鏁版嵁鐩稿叧
+ const summaryList = ref([]);
+ const detailList = ref([]);
+ const showDetail = ref(false);
+ const currentUserName = ref("");
+ const loadStatus = ref("loadmore");
+
+ const page = reactive({
+ current: 1,
+ size: 20,
+ total: 0,
+ });
+
+ const page1 = reactive({
+ current: 1,
+ size: 20,
+ total: 0,
+ });
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鍒囨崲鏃ユ湡绫诲瀷
+ const handleDateTypeChange = index => {
+ currentDateTypeIndex.value = index.index;
+ if (currentDateType.value === "day") {
+ selectedDate.value = dayjs().format("YYYY-MM-DD");
+ } else {
+ selectedDate.value = dayjs().startOf("month").format("YYYY-MM-DD");
+ }
+ reloadData();
+ };
+
+ // 纭鏃ユ湡閫夋嫨
+ const handleDateConfirm = e => {
+ selectedDate.value = dayjs(e.value).format("YYYY-MM-DD");
+ showDatePicker.value = false;
+ reloadData();
+ };
+
+ // 鏍煎紡鍖栧悎鏍肩巼
+ const formatOutputRate = val => {
+ if (val == null || val === "") return "-";
+ return parseFloat(val).toFixed(2) + "%";
+ };
+
+ // 鍔犺浇姹囨�诲垪琛�
+ const getSummaryList = () => {
+ uni.showLoading({ title: "鍔犺浇涓�..." });
+ const params = {
+ dateType: currentDateType.value,
+ entryDate: currentDateType.value === "day" ? selectedDate.value : undefined,
+ entryDateStart:
+ currentDateType.value === "month"
+ ? dayjs(selectedDate.value).startOf("month").format("YYYY-MM-DD")
+ : undefined,
+ entryDateEnd:
+ currentDateType.value === "month"
+ ? dayjs(selectedDate.value).endOf("month").format("YYYY-MM-DD")
+ : undefined,
+ pageNum: page.current,
+ pageSize: page.size,
+ };
+
+ salesLedgerProductionAccountingList(params)
+ .then(res => {
+ summaryList.value = res.data.records || [];
+ page.total = res.data.total || 0;
+ })
+ .finally(() => {
+ uni.hideLoading();
+ });
+ };
+
+ // 鍔犺浇鏄庣粏鍒楄〃
+ const getDetailList = (isLoadMore = false) => {
+ if (!isLoadMore) {
+ page1.current = 1;
+ detailList.value = [];
+ }
+ loadStatus.value = "loading";
+
+ const params = {
+ schedulingUserName: currentUserName.value,
+ dateType: currentDateType.value,
+ entryDate: currentDateType.value === "day" ? selectedDate.value : undefined,
+ entryDateStart:
+ currentDateType.value === "month"
+ ? dayjs(selectedDate.value).startOf("month").format("YYYY-MM-DD")
+ : undefined,
+ entryDateEnd:
+ currentDateType.value === "month"
+ ? dayjs(selectedDate.value).endOf("month").format("YYYY-MM-DD")
+ : undefined,
+ pageNum: page1.current,
+ pageSize: page1.size,
+ };
+
+ salesLedgerProductionAccountingListProductionDetails(params)
+ .then(res => {
+ const records = res.data.records || [];
+ detailList.value = isLoadMore
+ ? [...detailList.value, ...records]
+ : records;
+ page1.total = res.data.total || 0;
+
+ if (detailList.value.length >= page1.total) {
+ loadStatus.value = "nomore";
+ } else {
+ loadStatus.value = "loadmore";
+ page1.current++;
+ }
+ })
+ .catch(() => {
+ loadStatus.value = "loadmore";
+ });
+ };
+
+ // 鐐瑰嚮姹囨�昏
+ const handleRowClick = item => {
+ currentUserName.value = item.schedulingUserName;
+ showDetail.value = true;
+ getDetailList();
+ };
+
+ // 閲嶆柊鍔犺浇鏁版嵁
+ const reloadData = () => {
+ page.current = 1;
+ showDetail.value = false;
+ getSummaryList();
+ };
+
+ onMounted(() => {
+ getSummaryList();
+ });
+</script>
+
+<style scoped lang="scss">
+ .production-accounting {
+ background-color: #f5f7fa;
+ min-height: 100vh;
+ padding-bottom: 30rpx;
+
+ .filter-section {
+ background-color: #ffffff;
+ padding: 20rpx 30rpx;
+ margin-bottom: 20rpx;
+
+ .date-type-selector {
+ margin-bottom: 20rpx;
+ }
+
+ .date-picker-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #f0f4ff;
+ padding: 16rpx 24rpx;
+ border-radius: 8rpx;
+
+ .date-display {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+
+ .date-text {
+ font-size: 28rpx;
+ color: #2979ff;
+ font-weight: bold;
+ }
+ }
+ }
+ }
+
+ .section-header {
+ padding: 20rpx 30rpx;
+
+ .section-title {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+ border-left: 8rpx solid #2979ff;
+ padding-left: 16rpx;
+ }
+
+ &.back-bar {
+ display: flex;
+ align-items: center;
+ gap: 10rpx;
+ background-color: #ffffff;
+ margin-bottom: 20rpx;
+
+ .back-text {
+ font-size: 28rpx;
+ color: #2979ff;
+ }
+ }
+ }
+
+ .ledger-list {
+ padding: 0 20rpx;
+
+ .ledger-item {
+ background-color: #ffffff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
+
+ &:active {
+ background-color: #f9f9f9;
+ }
+
+ &.no-click:active {
+ background-color: #ffffff;
+ }
+
+ .item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16rpx;
+
+ .item-left {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+
+ .user-icon,
+ .product-icon {
+ width: 48rpx;
+ height: 48rpx;
+ background: linear-gradient(135deg, #2979ff, #64a1ff);
+ border-radius: 8rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .item-id {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+
+ .item-tag {
+ background-color: #f0f4ff;
+ padding: 4rpx 12rpx;
+ border-radius: 4rpx;
+
+ .tag-text {
+ font-size: 24rpx;
+ color: #2979ff;
+ }
+ }
+ }
+
+ .item-details {
+ padding-top: 10rpx;
+
+ .detail-grid {
+ display: flex;
+ justify-content: space-between;
+
+ .grid-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex: 1;
+
+ .grid-label {
+ font-size: 24rpx;
+ color: #999;
+ margin-bottom: 8rpx;
+ }
+
+ .grid-value {
+ font-size: 28rpx;
+ color: #333;
+ font-weight: 500;
+
+ &.highlight {
+ color: #ff5a5f;
+ }
+ }
+ }
+ }
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+
+ .detail-label {
+ font-size: 26rpx;
+ color: #999;
+ }
+
+ .detail-value {
+ font-size: 26rpx;
+ color: #333;
+
+ &.highlight {
+ color: #ff5a5f;
+ font-weight: bold;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .no-data {
+ padding-top: 100rpx;
+ }
+ }
+</style>
diff --git a/src/pages/productionManagement/productionDispatching/components/DispatchModal.vue b/src/pages/productionManagement/productionDispatching/components/DispatchModal.vue
index 95c3705..2a836e9 100644
--- a/src/pages/productionManagement/productionDispatching/components/DispatchModal.vue
+++ b/src/pages/productionManagement/productionDispatching/components/DispatchModal.vue
@@ -1,399 +1,379 @@
<template>
- <up-popup
- v-model:show="show"
- mode="bottom"
- :round="20"
- :safeAreaInsetBottom="true"
- @close="handleClose"
- @open="handleOpen"
- >
- <view class="dispatch-modal">
- <!-- 澶撮儴 -->
- <view class="modal-header">
- <text class="modal-title">鐢熶骇娲惧伐</text>
- <view class="close-btn" @click="handleClose">
- <up-icon name="close" size="20" color="#999"></up-icon>
- </view>
- </view>
-
- <!-- 琛ㄥ崟鍐呭 -->
- <view class="modal-content">
- <up-form
- :model="form"
- ref="formRef"
- :rules="rules"
- labelWidth="120"
- >
- <!-- 椤圭洰鍩烘湰淇℃伅 -->
- <view class="form-section">
- <text class="section-title">椤圭洰淇℃伅</text>
- <up-form-item label="椤圭洰鍚嶇О" prop="projectName">
- <up-input
- v-model="form.projectName"
- disabled
- placeholder="椤圭洰鍚嶇О"
- />
- </up-form-item>
- <up-form-item label="浜у搧澶х被" prop="productCategory">
- <up-input
- v-model="form.productCategory"
- disabled
- placeholder="浜у搧澶х被"
- />
- </up-form-item>
- </view>
-
- <!-- 鏁伴噺淇℃伅 -->
- <view class="form-section">
- <text class="section-title">鏁伴噺淇℃伅</text>
- <up-form-item label="鎬绘暟閲�" prop="quantity">
- <up-input
- v-model="form.quantity"
- disabled
- placeholder="鎬绘暟閲�"
- />
- </up-form-item>
- <up-form-item label="寰呮帓浜ф暟閲�" prop="pendingQuantity">
- <up-input
- v-model="form.pendingQuantity"
- disabled
- placeholder="寰呮帓浜ф暟閲�"
- />
- </up-form-item>
- <up-form-item label="鏈鎺掍骇鏁伴噺" prop="schedulingNum" required>
- <up-number-box
- v-model="form.schedulingNum"
- :min="0"
- :max="form.pendingQuantity"
- :step="0.1"
- :precision="2"
- @change="handleNumChange"
- />
- </up-form-item>
- </view>
-
- <!-- 娲惧伐淇℃伅 -->
- <view class="form-section">
- <text class="section-title">娲惧伐淇℃伅</text>
- <up-form-item label="娲惧伐浜�" prop="schedulingUserId" required>
- <up-input
- v-model="selectedUserName"
- placeholder="璇烽�夋嫨娲惧伐浜�"
- readonly
- @click="showUserPicker = true"
- suffixIcon="arrow-down"
- />
- </up-form-item>
- <up-form-item label="娲惧伐鏃ユ湡" prop="schedulingDate" required>
- <up-input
- v-model="form.schedulingDate"
- placeholder="璇烽�夋嫨娲惧伐鏃ユ湡"
- readonly
- @click="showDatePicker = true"
- suffixIcon="calendar"
- />
- </up-form-item>
- </view>
- </up-form>
- </view>
-
- <!-- 搴曢儴鎸夐挳 -->
- <view class="modal-footer">
- <up-button
- @click="handleClose"
- text="鍙栨秷"
- type="info"
- plain
- :customStyle="{ marginRight: '12px', flex: 1 }"
- />
- <up-button
- @click="handleConfirm"
- text="纭娲惧伐"
- type="primary"
- :customStyle="{ flex: 1 }"
- :loading="submitting"
- />
- </view>
- </view>
-
- <!-- 浜哄憳閫夋嫨鍣� -->
- <up-picker
- v-model="showUserPicker"
- :columns="userColumns"
- @confirm="handleUserSelect"
- @cancel="showUserPicker = false"
- />
-
- <!-- 鏃ユ湡閫夋嫨鍣� -->
- <up-datetime-picker
- v-model="showDatePicker"
- mode="date"
- @confirm="handleDateSelect"
- @cancel="showDatePicker = false"
- />
- </up-popup>
+ <up-popup v-model:show="show"
+ mode="bottom"
+ :round="20"
+ :safeAreaInsetBottom="true"
+ @close="handleClose"
+ @open="handleOpen">
+ <view class="dispatch-modal">
+ <!-- 澶撮儴 -->
+ <view class="modal-header">
+ <text class="modal-title">鐢熶骇娲惧伐</text>
+ <view class="close-btn"
+ @click="handleClose">
+ <up-icon name="close"
+ size="20"
+ color="#999"></up-icon>
+ </view>
+ </view>
+ <!-- 琛ㄥ崟鍐呭 -->
+ <view class="modal-content">
+ <up-form :model="form"
+ ref="formRef"
+ :rules="rules"
+ labelWidth="120">
+ <!-- 椤圭洰鍩烘湰淇℃伅 -->
+ <view class="form-section">
+ <text class="section-title">椤圭洰淇℃伅</text>
+ <up-form-item label="椤圭洰鍚嶇О"
+ prop="projectName">
+ <up-input v-model="form.projectName"
+ disabled
+ placeholder="椤圭洰鍚嶇О" />
+ </up-form-item>
+ <up-form-item label="浜у搧澶х被"
+ prop="productCategory">
+ <up-input v-model="form.productCategory"
+ disabled
+ placeholder="浜у搧澶х被" />
+ </up-form-item>
+ </view>
+ <!-- 鏁伴噺淇℃伅 -->
+ <view class="form-section">
+ <text class="section-title">鏁伴噺淇℃伅</text>
+ <up-form-item label="鎬绘暟閲�"
+ prop="quantity">
+ <up-input v-model="form.quantity"
+ disabled
+ placeholder="鎬绘暟閲�" />
+ </up-form-item>
+ <up-form-item label="寰呮帓浜ф暟閲�"
+ prop="pendingQuantity">
+ <up-input v-model="form.pendingQuantity"
+ disabled
+ placeholder="寰呮帓浜ф暟閲�" />
+ </up-form-item>
+ <up-form-item label="鏈鎺掍骇鏁伴噺"
+ prop="schedulingNum"
+ required>
+ <up-number-box v-model="form.schedulingNum"
+ :min="0"
+ :max="form.pendingQuantity"
+ :step="0.1"
+ :precision="2"
+ @change="handleNumChange" />
+ </up-form-item>
+ </view>
+ <!-- 娲惧伐淇℃伅 -->
+ <view class="form-section">
+ <text class="section-title">娲惧伐淇℃伅</text>
+ <up-form-item label="娲惧伐浜�"
+ prop="schedulingUserId"
+ required>
+ <up-input v-model="selectedUserName"
+ placeholder="璇烽�夋嫨娲惧伐浜�"
+ readonly
+ @click="showUserPicker = true"
+ suffixIcon="arrow-down" />
+ </up-form-item>
+ <up-form-item label="娲惧伐鏃ユ湡"
+ prop="schedulingDate"
+ required>
+ <up-input v-model="form.schedulingDate"
+ placeholder="璇烽�夋嫨娲惧伐鏃ユ湡"
+ readonly
+ @click="showDatePicker = true"
+ suffixIcon="calendar" />
+ </up-form-item>
+ </view>
+ </up-form>
+ </view>
+ <!-- 搴曢儴鎸夐挳 -->
+ <view class="modal-footer">
+ <up-button @click="handleClose"
+ text="鍙栨秷"
+ type="info"
+ plain
+ :customStyle="{ marginRight: '12px', flex: 1 }" />
+ <up-button @click="handleConfirm"
+ text="纭娲惧伐"
+ type="primary"
+ :customStyle="{ flex: 1 }"
+ :loading="submitting" />
+ </view>
+ </view>
+ <!-- 浜哄憳閫夋嫨鍣� -->
+ <up-picker v-model="showUserPicker"
+ :columns="userColumns"
+ @confirm="handleUserSelect"
+ @cancel="showUserPicker = false" />
+ <!-- 鏃ユ湡閫夋嫨鍣� -->
+ <up-datetime-picker v-model="showDatePicker"
+ mode="date"
+ @confirm="handleDateSelect"
+ @cancel="showDatePicker = false" />
+ </up-popup>
</template>
<script setup>
-import { ref, reactive, computed, getCurrentInstance } from 'vue';
-import { userListNoPageByTenantId } from "@/api/system/user.js";
-import { productionDispatch } from "@/api/productionManagement/productionOrder.js";
-import useUserStore from "@/store/modules/user";
-import dayjs from "dayjs";
+ import { ref, reactive, computed, getCurrentInstance } from "vue";
+ import { userListNoPageByTenantId } from "@/api/system/user.js";
+ // import { productionDispatch } from "@/api/productionManagement/productionOrder.js";
+ import useUserStore from "@/store/modules/user";
+ import dayjs from "dayjs";
-const { proxy } = getCurrentInstance();
-const userStore = useUserStore();
-const emit = defineEmits(['confirm']);
+ const { proxy } = getCurrentInstance();
+ const userStore = useUserStore();
+ const emit = defineEmits(["confirm"]);
-// 寮圭獥鏄剧ず鐘舵��
-const show = ref(false);
-const submitting = ref(false);
+ // 寮圭獥鏄剧ず鐘舵��
+ const show = ref(false);
+ const submitting = ref(false);
-// 閫夋嫨鍣ㄦ樉绀虹姸鎬�
-const showUserPicker = ref(false);
-const showDatePicker = ref(false);
+ // 閫夋嫨鍣ㄦ樉绀虹姸鎬�
+ const showUserPicker = ref(false);
+ const showDatePicker = ref(false);
-// 鐢ㄦ埛鍒楄〃
-const userList = ref([]);
-const userColumns = computed(() => [
- userList.value.map(user => ({
- label: user.nickName,
- value: user.userId
- }))
-]);
+ // 鐢ㄦ埛鍒楄〃
+ const userList = ref([]);
+ const userColumns = computed(() => [
+ userList.value.map(user => ({
+ label: user.nickName,
+ value: user.userId,
+ })),
+ ]);
-// 閫変腑鐨勭敤鎴峰悕绉帮紙鐢ㄤ簬鏄剧ず锛�
-const selectedUserName = computed(() => {
- const user = userList.value.find(u => u.userId === form.schedulingUserId);
- return user ? user.nickName : '';
-});
+ // 閫変腑鐨勭敤鎴峰悕绉帮紙鐢ㄤ簬鏄剧ず锛�
+ const selectedUserName = computed(() => {
+ const user = userList.value.find(u => u.userId === form.schedulingUserId);
+ return user ? user.nickName : "";
+ });
-// 琛ㄥ崟鏁版嵁
-const form = reactive({
- projectName: "",
- productCategory: "",
- quantity: "",
- schedulingNum: 0,
- schedulingUserId: "",
- schedulingDate: "",
- pendingQuantity: 0,
- id: "" // 鍘熷璁板綍ID
-});
+ // 琛ㄥ崟鏁版嵁
+ const form = reactive({
+ projectName: "",
+ productCategory: "",
+ quantity: "",
+ schedulingNum: 0,
+ schedulingUserId: "",
+ schedulingDate: "",
+ pendingQuantity: 0,
+ id: "", // 鍘熷璁板綍ID
+ });
-// 琛ㄥ崟楠岃瘉瑙勫垯
-const rules = reactive({
- schedulingNum: [
- { required: true, message: "璇疯緭鍏ユ帓浜ф暟閲�", trigger: "blur" }
- ],
- schedulingUserId: [
- { required: true, message: "璇烽�夋嫨娲惧伐浜�", trigger: "change" }
- ],
- schedulingDate: [
- { required: true, message: "璇烽�夋嫨娲惧伐鏃ユ湡", trigger: "change" }
- ]
-});
+ // 琛ㄥ崟楠岃瘉瑙勫垯
+ const rules = reactive({
+ schedulingNum: [
+ { required: true, message: "璇疯緭鍏ユ帓浜ф暟閲�", trigger: "blur" },
+ ],
+ schedulingUserId: [
+ { required: true, message: "璇烽�夋嫨娲惧伐浜�", trigger: "change" },
+ ],
+ schedulingDate: [
+ { required: true, message: "璇烽�夋嫨娲惧伐鏃ユ湡", trigger: "change" },
+ ],
+ });
-// 琛ㄥ崟寮曠敤
-const formRef = ref();
+ // 琛ㄥ崟寮曠敤
+ const formRef = ref();
-// 鎵撳紑寮圭獥
-const open = async (rowData) => {
- try {
- // 鍔犺浇鐢ㄦ埛鍒楄〃
- const res = await userListNoPageByTenantId();
- userList.value = res.data || [];
-
- // 濉厖琛ㄥ崟鏁版嵁
- Object.assign(form, {
- ...rowData,
- schedulingNum: 0,
- schedulingUserId: userStore.id,
- schedulingDate: dayjs().format("YYYY-MM-DD")
- });
-
- show.value = true;
- } catch (error) {
- uni.showToast({
- title: '鍔犺浇鐢ㄦ埛鍒楄〃澶辫触',
- icon: 'error'
- });
- }
-};
+ // 鎵撳紑寮圭獥
+ const open = async rowData => {
+ try {
+ // 鍔犺浇鐢ㄦ埛鍒楄〃
+ const res = await userListNoPageByTenantId();
+ userList.value = res.data || [];
-// 澶勭悊鏁伴噺鍙樺寲
-const handleNumChange = (value) => {
- if (value > form.pendingQuantity) {
- form.schedulingNum = form.pendingQuantity;
- uni.showToast({
- title: '鎺掍骇鏁伴噺涓嶅彲澶т簬寰呮帓浜ф暟閲�',
- icon: 'none'
- });
- }
-};
+ // 濉厖琛ㄥ崟鏁版嵁
+ Object.assign(form, {
+ ...rowData,
+ schedulingNum: 0,
+ schedulingUserId: userStore.id,
+ schedulingDate: dayjs().format("YYYY-MM-DD"),
+ });
-// 澶勭悊鐢ㄦ埛閫夋嫨
-const handleUserSelect = (params) => {
- if (params.value && params.value.length > 0) {
- form.schedulingUserId = params.value[0];
- }
- showUserPicker.value = false;
-};
+ show.value = true;
+ } catch (error) {
+ uni.showToast({
+ title: "鍔犺浇鐢ㄦ埛鍒楄〃澶辫触",
+ icon: "error",
+ });
+ }
+ };
-// 澶勭悊鏃ユ湡閫夋嫨
-const handleDateSelect = (params) => {
- if (params.value) {
- form.schedulingDate = dayjs(params.value).format("YYYY-MM-DD");
- }
- showDatePicker.value = false;
-};
+ // 澶勭悊鏁伴噺鍙樺寲
+ const handleNumChange = value => {
+ if (value > form.pendingQuantity) {
+ form.schedulingNum = form.pendingQuantity;
+ uni.showToast({
+ title: "鎺掍骇鏁伴噺涓嶅彲澶т簬寰呮帓浜ф暟閲�",
+ icon: "none",
+ });
+ }
+ };
-// 纭娲惧伐
-const handleConfirm = async () => {
- try {
- // 琛ㄥ崟楠岃瘉
- const valid = await formRef.value?.validate();
- if (!valid) return;
-
- if (form.schedulingNum <= 0) {
- uni.showToast({
- title: '鎺掍骇鏁伴噺蹇呴』澶т簬0',
- icon: 'none'
- });
- return;
- }
-
- submitting.value = true;
-
- // 鎻愪氦娲惧伐鏁版嵁
- await productionDispatch(form);
-
- uni.showToast({
- title: '娲惧伐鎴愬姛',
- icon: 'success'
- });
-
- handleClose();
- emit('confirm');
-
- } catch (error) {
- uni.showToast({
- title: '娲惧伐澶辫触',
- icon: 'error'
- });
- } finally {
- submitting.value = false;
- }
-};
+ // 澶勭悊鐢ㄦ埛閫夋嫨
+ const handleUserSelect = params => {
+ if (params.value && params.value.length > 0) {
+ form.schedulingUserId = params.value[0];
+ }
+ showUserPicker.value = false;
+ };
-// 寮圭獥鎵撳紑浜嬩欢
-const handleOpen = () => {
- // 寮圭獥鎵撳紑鏃剁殑澶勭悊
-};
+ // 澶勭悊鏃ユ湡閫夋嫨
+ const handleDateSelect = params => {
+ if (params.value) {
+ form.schedulingDate = dayjs(params.value).format("YYYY-MM-DD");
+ }
+ showDatePicker.value = false;
+ };
-// 鍏抽棴寮圭獥
-const handleClose = () => {
- show.value = false;
- showUserPicker.value = false;
- showDatePicker.value = false;
-
- // 閲嶇疆琛ㄥ崟
- Object.assign(form, {
- projectName: "",
- productCategory: "",
- quantity: "",
- schedulingNum: 0,
- schedulingUserId: "",
- schedulingDate: "",
- pendingQuantity: 0,
- id: ""
- });
-};
+ // 纭娲惧伐
+ const handleConfirm = async () => {
+ try {
+ // 琛ㄥ崟楠岃瘉
+ const valid = await formRef.value?.validate();
+ if (!valid) return;
-// 鏆撮湶鏂规硶
-defineExpose({
- open
-});
+ if (form.schedulingNum <= 0) {
+ uni.showToast({
+ title: "鎺掍骇鏁伴噺蹇呴』澶т簬0",
+ icon: "none",
+ });
+ return;
+ }
+
+ submitting.value = true;
+
+ // 鎻愪氦娲惧伐鏁版嵁
+ // await productionDispatch(form);
+
+ uni.showToast({
+ title: "娲惧伐鎴愬姛",
+ icon: "success",
+ });
+
+ handleClose();
+ emit("confirm");
+ } catch (error) {
+ uni.showToast({
+ title: "娲惧伐澶辫触",
+ icon: "error",
+ });
+ } finally {
+ submitting.value = false;
+ }
+ };
+
+ // 寮圭獥鎵撳紑浜嬩欢
+ const handleOpen = () => {
+ // 寮圭獥鎵撳紑鏃剁殑澶勭悊
+ };
+
+ // 鍏抽棴寮圭獥
+ const handleClose = () => {
+ show.value = false;
+ showUserPicker.value = false;
+ showDatePicker.value = false;
+
+ // 閲嶇疆琛ㄥ崟
+ Object.assign(form, {
+ projectName: "",
+ productCategory: "",
+ quantity: "",
+ schedulingNum: 0,
+ schedulingUserId: "",
+ schedulingDate: "",
+ pendingQuantity: 0,
+ id: "",
+ });
+ };
+
+ // 鏆撮湶鏂规硶
+ defineExpose({
+ open,
+ });
</script>
<style scoped lang="scss">
-.dispatch-modal {
- background: #ffffff;
- border-radius: 20px 20px 0 0;
- max-height: 80vh;
- overflow: hidden;
- display: flex;
- flex-direction: column;
-}
+ .dispatch-modal {
+ background: #ffffff;
+ border-radius: 20px 20px 0 0;
+ max-height: 80vh;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ }
-.modal-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 20px 20px 0 20px;
- border-bottom: 1px solid #f0f0f0;
- padding-bottom: 16px;
- margin-bottom: 20px;
-}
+ .modal-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 20px 0 20px;
+ border-bottom: 1px solid #f0f0f0;
+ padding-bottom: 16px;
+ margin-bottom: 20px;
+ }
-.modal-title {
- font-size: 18px;
- font-weight: 600;
- color: #333;
-}
+ .modal-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
-.close-btn {
- padding: 4px;
-
- &:active {
- opacity: 0.7;
- }
-}
+ .close-btn {
+ padding: 4px;
-.modal-content {
- flex: 1;
- padding: 0 20px;
- overflow-y: auto;
-}
+ &:active {
+ opacity: 0.7;
+ }
+ }
-.form-section {
- margin-bottom: 24px;
-}
+ .modal-content {
+ flex: 1;
+ padding: 0 20px;
+ overflow-y: auto;
+ }
-.section-title {
- display: block;
- font-size: 16px;
- font-weight: 600;
- color: #333;
- margin-bottom: 16px;
- padding-left: 8px;
- border-left: 3px solid #2979ff;
-}
+ .form-section {
+ margin-bottom: 24px;
+ }
-.modal-footer {
- display: flex;
- gap: 12px;
- padding: 20px;
- border-top: 1px solid #f0f0f0;
- background: #fafafa;
-}
+ .section-title {
+ display: block;
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 16px;
+ padding-left: 8px;
+ border-left: 3px solid #2979ff;
+ }
-// uView 缁勪欢鏍峰紡璋冩暣
-:deep(.up-form-item) {
- margin-bottom: 20px;
-}
+ .modal-footer {
+ display: flex;
+ gap: 12px;
+ padding: 20px;
+ border-top: 1px solid #f0f0f0;
+ background: #fafafa;
+ }
-:deep(.up-input) {
- background: #f8f9fa;
- border-radius: 8px;
-}
+ // uView 缁勪欢鏍峰紡璋冩暣
+ :deep(.up-form-item) {
+ margin-bottom: 20px;
+ }
-:deep(.up-input--disabled) {
- background: #f0f0f0;
- color: #999;
-}
+ :deep(.up-input) {
+ background: #f8f9fa;
+ border-radius: 8px;
+ }
-:deep(.up-number-box) {
- background: #f8f9fa;
- border-radius: 8px;
-}
+ :deep(.up-input--disabled) {
+ background: #f0f0f0;
+ color: #999;
+ }
+
+ :deep(.up-number-box) {
+ background: #f8f9fa;
+ border-radius: 8px;
+ }
</style>
diff --git a/src/pages/productionManagement/productionDispatching/components/formDia.vue b/src/pages/productionManagement/productionDispatching/components/formDia.vue
index 72227ac..e7e6662 100644
--- a/src/pages/productionManagement/productionDispatching/components/formDia.vue
+++ b/src/pages/productionManagement/productionDispatching/components/formDia.vue
@@ -1,87 +1,101 @@
<template>
<div>
- <el-dialog
- v-model="dialogFormVisible"
- title="鐢熶骇娲惧伐"
- width="50%"
- @close="closeDia"
- >
- <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
+ <el-dialog v-model="dialogFormVisible"
+ title="鐢熶骇娲惧伐"
+ width="50%"
+ @close="closeDia">
+ <el-form :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
- <el-input v-model="form.projectName" placeholder="璇疯緭鍏�" clearable disabled/>
+ <el-form-item label="椤圭洰鍚嶇О锛�"
+ prop="projectName">
+ <el-input v-model="form.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ disabled />
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
- <el-input v-model="form.productCategory" placeholder="璇疯緭鍏�" clearable disabled/>
+ <el-form-item label="浜у搧澶х被锛�"
+ prop="productCategory">
+ <el-input v-model="form.productCategory"
+ placeholder="璇疯緭鍏�"
+ clearable
+ disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="鎬绘暟閲忥細" prop="quantity">
- <el-input v-model="form.quantity" placeholder="璇疯緭鍏�" clearable disabled/>
+ <el-form-item label="鎬绘暟閲忥細"
+ prop="quantity">
+ <el-input v-model="form.quantity"
+ placeholder="璇疯緭鍏�"
+ clearable
+ disabled />
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="寰呮帓浜ф暟閲忥細" prop="pendingQuantity">
- <el-input v-model="form.pendingQuantity" placeholder="璇疯緭鍏�" clearable disabled/>
- </el-form-item>
+ <el-form-item label="寰呮帓浜ф暟閲忥細"
+ prop="pendingQuantity">
+ <el-input v-model="form.pendingQuantity"
+ placeholder="璇疯緭鍏�"
+ clearable
+ disabled />
+ </el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="鏈鎺掍骇鏁伴噺锛�" prop="schedulingNum">
- <el-input-number
- v-model="form.schedulingNum"
- placeholder="璇疯緭鍏�"
- :min="0"
- :step="0.1"
- :precision="2"
- clearable
- @change="changeNum"
- style="width: 100%"
- />
- </el-form-item>
+ <el-form-item label="鏈鎺掍骇鏁伴噺锛�"
+ prop="schedulingNum">
+ <el-input-number v-model="form.schedulingNum"
+ placeholder="璇疯緭鍏�"
+ :min="0"
+ :step="0.1"
+ :precision="2"
+ clearable
+ @change="changeNum"
+ style="width: 100%" />
+ </el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="娲惧伐浜猴細" prop="schedulingUserId">
- <el-select
- v-model="form.schedulingUserId"
- placeholder="閫夋嫨浜哄憳"
- style="width: 100%;"
- >
- <el-option
- v-for="user in userList"
- :key="user.userId"
- :label="user.nickName"
- :value="user.userId"
- />
- </el-select>
- </el-form-item>
- </el-col>
<el-col :span="12">
- <el-form-item label="娲惧伐鏃ユ湡锛�" prop="schedulingDate">
- <el-date-picker
- v-model="form.schedulingDate"
- type="date"
- placeholder="璇烽�夋嫨鏃ユ湡"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- clearable
- style="width: 100%"
- />
+ <el-form-item label="娲惧伐浜猴細"
+ prop="schedulingUserId">
+ <el-select v-model="form.schedulingUserId"
+ placeholder="閫夋嫨浜哄憳"
+ style="width: 100%;">
+ <el-option v-for="user in userList"
+ :key="user.userId"
+ :label="user.nickName"
+ :value="user.userId" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="娲惧伐鏃ユ湡锛�"
+ prop="schedulingDate">
+ <el-date-picker v-model="form.schedulingDate"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
- <el-button type="primary" @click="submitForm">纭</el-button>
+ <el-button type="primary"
+ @click="submitForm">纭</el-button>
<el-button @click="closeDia">鍙栨秷</el-button>
</div>
</template>
@@ -90,80 +104,85 @@
</template>
<script setup>
-import {ref} from "vue";
-import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
-import {userListNoPageByTenantId} from "@/api/system/user.js";
-import {productionDispatch} from "@/api/productionManagement/productionOrder.js";
-import useUserStore from "@/store/modules/user";
-import dayjs from "dayjs";
-const { proxy } = getCurrentInstance()
-const emit = defineEmits(['close'])
+ import { ref } from "vue";
+ import {
+ getStaffJoinInfo,
+ staffJoinAdd,
+ staffJoinUpdate,
+ } from "@/api/personnelManagement/onboarding.js";
+ import { userListNoPageByTenantId } from "@/api/system/user.js";
+ // import {productionDispatch} from "@/api/productionManagement/productionOrder.js";
+ import useUserStore from "@/store/modules/user";
+ import dayjs from "dayjs";
+ const { proxy } = getCurrentInstance();
+ const emit = defineEmits(["close"]);
-const dialogFormVisible = ref(false);
-const operationType = ref('')
-const data = reactive({
- form: {
- projectName: "",
- productCategory: "",
- quantity: "",
- schedulingNum: "",
- schedulingUserId: "",
- schedulingDate: "",
- pendingQuantity: "",
- },
- rules: {
- schedulingNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" },],
- schedulingUserId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" },],
- schedulingDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" },],
- },
-});
-const { form, rules } = toRefs(data);
-const userList = ref([])
-const userStore = useUserStore()
+ const dialogFormVisible = ref(false);
+ const operationType = ref("");
+ const data = reactive({
+ form: {
+ projectName: "",
+ productCategory: "",
+ quantity: "",
+ schedulingNum: "",
+ schedulingUserId: "",
+ schedulingDate: "",
+ pendingQuantity: "",
+ },
+ rules: {
+ schedulingNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ schedulingUserId: [
+ { required: true, message: "璇烽�夋嫨", trigger: "change" },
+ ],
+ schedulingDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+ });
+ const { form, rules } = toRefs(data);
+ const userList = ref([]);
+ const userStore = useUserStore();
-// 鎵撳紑寮规
-const openDialog = (type, row) => {
- operationType.value = type;
- dialogFormVisible.value = true;
- userListNoPageByTenantId().then((res) => {
- userList.value = res.data;
- });
- form.value = {...row}
- form.value.schedulingNum = 0
- form.value.schedulingUserId = userStore.id
- form.value.schedulingDate = dayjs().format("YYYY-MM-DD");
-}
+ // 鎵撳紑寮规
+ const openDialog = (type, row) => {
+ operationType.value = type;
+ dialogFormVisible.value = true;
+ userListNoPageByTenantId().then(res => {
+ userList.value = res.data;
+ });
+ form.value = { ...row };
+ form.value.schedulingNum = 0;
+ form.value.schedulingUserId = userStore.id;
+ form.value.schedulingDate = dayjs().format("YYYY-MM-DD");
+ };
-//
-const changeNum = (value) => {
- if (value > form.value.pendingQuantity) {
- form.value.schedulingNum = form.value.pendingQuantity;
- proxy.$modal.msgWarning('鎺掍骇鏁伴噺涓嶅彲澶т簬寰呮帓浜ф暟閲�')
- }
-}
-// 鎻愪氦浜у搧琛ㄥ崟
-const submitForm = () => {
- proxy.$refs.formRef.validate(valid => {
- if (valid) {
- productionDispatch(form.value).then(res => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeDia();
- })
+ //
+ const changeNum = value => {
+ if (value > form.value.pendingQuantity) {
+ form.value.schedulingNum = form.value.pendingQuantity;
+ proxy.$modal.msgWarning("鎺掍骇鏁伴噺涓嶅彲澶т簬寰呮帓浜ф暟閲�");
}
- })
-}
+ };
+ // 鎻愪氦浜у搧琛ㄥ崟
+ const submitForm = () => {
+ proxy.$refs.formRef.validate(valid => {
+ if (valid) {
+ // productionDispatch(form.value).then(res => {
+ // proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ // closeDia();
+ // })
+ }
+ });
+ };
-// 鍏抽棴寮规
-const closeDia = () => {
- proxy.resetForm("formRef");
- dialogFormVisible.value = false;
- emit('close')
-};
-defineExpose({
- openDialog,
-});
+ // 鍏抽棴寮规
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ emit("close");
+ };
+ defineExpose({
+ openDialog,
+ });
</script>
<style scoped>
-
</style>
\ No newline at end of file
diff --git a/src/pages/productionManagement/productionDispatching/index.vue b/src/pages/productionManagement/productionDispatching/index.vue
index 7aea197..d6ae5e3 100644
--- a/src/pages/productionManagement/productionDispatching/index.vue
+++ b/src/pages/productionManagement/productionDispatching/index.vue
@@ -1,235 +1,236 @@
<template>
- <view class="production-dispatching">
- <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
- <PageHeader title="鐢熶骇娲惧伐" @back="goBack" />
-
- <!-- 鎼滅储鍖哄煙 -->
- <view class="search-section">
- <view class="search-bar">
- <view class="search-input">
- <up-input
- class="search-text"
- placeholder="璇疯緭鍏ュ鎴峰悕绉版悳绱�"
- v-model="searchForm.customerName"
- @change="handleQuery"
- clearable
- />
- </view>
- <view class="filter-button" @click="handleQuery">
- <up-icon name="search" size="24" color="#999"></up-icon>
- </view>
- </view>
- </view>
-
- <!-- 鐢熶骇娲惧伐鍒楄〃 -->
- <view class="ledger-list" v-if="tableData.length > 0">
- <view v-for="(item, index) in tableData" :key="item.id || index">
- <view 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.salesContractNo }}</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.entryDate }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">瀹㈡埛鍚堝悓鍙�</text>
- <text class="detail-value">{{ item.customerContractNo }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">瀹㈡埛鍚嶇О</text>
- <text class="detail-value">{{ item.customerName }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">椤圭洰鍚嶇О</text>
- <text class="detail-value">{{ item.projectName }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">浜у搧澶х被</text>
- <text class="detail-value">{{ item.productCategory }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">瑙勬牸鍨嬪彿</text>
- <text class="detail-value">{{ item.specificationModel }}</text>
- </view>
- <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">鎺掍骇鏁伴噺</text>
- <text class="detail-value highlight">{{ item.schedulingNum }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">寰呮帓鏁伴噺</text>
- <text class="detail-value" :class="{ 'danger': item.pendingQuantity <= 0 }">{{ item.pendingQuantity }}</text>
- </view>
- </view>
-
- <!-- 鎿嶄綔鎸夐挳鍖哄煙 -->
- <view class="action-buttons">
- <up-button
- type="primary"
- size="small"
- @click="handleDispatch(item)"
- class="action-btn"
- :disabled="item.pendingQuantity <= 0"
- >
- 鐢熶骇娲惧伐
- </up-button>
- </view>
- </view>
- </view>
- </view>
- <view v-else class="no-data">
- <text>鏆傛棤鐢熶骇娲惧伐鏁版嵁</text>
- </view>
-
- <!-- 娲惧伐寮圭獥 -->
- <DispatchModal ref="dispatchModalRef" @confirm="handleDispatchConfirm" />
- </view>
+ <view class="production-dispatching">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="鐢熶骇娲惧伐"
+ @back="goBack" />
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ュ鎴峰悕绉版悳绱�"
+ v-model="searchForm.customerName"
+ @change="handleQuery"
+ clearable />
+ </view>
+ <view class="filter-button"
+ @click="handleQuery">
+ <up-icon name="search"
+ size="24"
+ color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <!-- 鐢熶骇娲惧伐鍒楄〃 -->
+ <view class="ledger-list"
+ v-if="tableData.length > 0">
+ <view v-for="(item, index) in tableData"
+ :key="item.id || index">
+ <view 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.salesContractNo }}</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.entryDate }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀹㈡埛鍚堝悓鍙�</text>
+ <text class="detail-value">{{ item.customerContractNo }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀹㈡埛鍚嶇О</text>
+ <text class="detail-value">{{ item.customerName }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">椤圭洰鍚嶇О</text>
+ <text class="detail-value">{{ item.projectName }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">浜у搧澶х被</text>
+ <text class="detail-value">{{ item.productCategory }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瑙勬牸鍨嬪彿</text>
+ <text class="detail-value">{{ item.specificationModel }}</text>
+ </view>
+ <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">鎺掍骇鏁伴噺</text>
+ <text class="detail-value highlight">{{ item.schedulingNum }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">寰呮帓鏁伴噺</text>
+ <text class="detail-value"
+ :class="{ 'danger': item.pendingQuantity <= 0 }">{{ item.pendingQuantity }}</text>
+ </view>
+ </view>
+ <!-- 鎿嶄綔鎸夐挳鍖哄煙 -->
+ <view class="action-buttons">
+ <up-button type="primary"
+ size="small"
+ @click="handleDispatch(item)"
+ class="action-btn"
+ :disabled="item.pendingQuantity <= 0">
+ 鐢熶骇娲惧伐
+ </up-button>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <text>鏆傛棤鐢熶骇娲惧伐鏁版嵁</text>
+ </view>
+ <!-- 娲惧伐寮圭獥 -->
+ <DispatchModal ref="dispatchModalRef"
+ @confirm="handleDispatchConfirm" />
+ </view>
</template>
<script setup>
-import { ref, reactive, toRefs, getCurrentInstance } from "vue";
-import { onShow } from '@dcloudio/uni-app';
-import dayjs from "dayjs";
-import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
-import PageHeader from "@/components/PageHeader.vue";
-import DispatchModal from "./components/DispatchModal.vue";
-const { proxy } = getCurrentInstance();
+ import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import dayjs from "dayjs";
+ // import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
+ import PageHeader from "@/components/PageHeader.vue";
+ import DispatchModal from "./components/DispatchModal.vue";
+ const { proxy } = getCurrentInstance();
-// 鍔犺浇鐘舵��
-const loading = ref(false);
+ // 鍔犺浇鐘舵��
+ const loading = ref(false);
-// 鍒楄〃鏁版嵁
-const tableData = ref([]);
+ // 鍒楄〃鏁版嵁
+ const tableData = ref([]);
+ // 鎼滅储琛ㄥ崟鏁版嵁
+ const data = reactive({
+ searchForm: {
+ customerName: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
-// 鎼滅储琛ㄥ崟鏁版嵁
-const data = reactive({
- searchForm: {
- customerName: "",
- },
-});
-const { searchForm } = toRefs(data);
+ // 鍒嗛〉閰嶇疆
+ const page = reactive({
+ current: -1,
+ size: -1,
+ });
-// 鍒嗛〉閰嶇疆
-const page = reactive({
- current: -1,
- size: -1,
-});
+ // 娲惧伐寮圭獥寮曠敤
+ const dispatchModalRef = ref();
-// 娲惧伐寮圭獥寮曠敤
-const dispatchModalRef = ref();
+ // 閫氱敤鎻愮ず鍑芥暟
+ const showLoadingToast = message => {
+ uni.showLoading({
+ title: message,
+ mask: true,
+ });
+ };
-// 閫氱敤鎻愮ず鍑芥暟
-const showLoadingToast = (message) => {
- uni.showLoading({
- title: message,
- mask: true
- });
-};
+ const closeToast = () => {
+ uni.hideLoading();
+ };
-const closeToast = () => {
- uni.hideLoading();
-};
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
-// 杩斿洖涓婁竴椤�
-const goBack = () => {
- uni.navigateBack();
-};
+ // 鏌ヨ鍒楄〃
+ const handleQuery = () => {
+ getList();
+ };
-// 鏌ヨ鍒楄〃
-const handleQuery = () => {
- getList();
-};
+ // 鑾峰彇鍒楄〃鏁版嵁
+ const getList = () => {
+ loading.value = true;
+ showLoadingToast("鍔犺浇涓�...");
-// 鑾峰彇鍒楄〃鏁版嵁
-const getList = () => {
- loading.value = true;
- showLoadingToast('鍔犺浇涓�...');
-
- // 鏋勯�犺姹傚弬鏁�
- const params = { ...searchForm.value, ...page };
-
- schedulingListPage(params).then((res) => {
- loading.value = false;
- closeToast();
-
- // 澶勭悊姣忔潯鏁版嵁锛屽鍔爌endingQuantity瀛楁
- tableData.value = (res.data.records || []).map(item => ({
- ...item,
- pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0)
- }));
- }).catch(() => {
- loading.value = false;
- closeToast();
- uni.showToast({
- title: '鍔犺浇澶辫触',
- icon: 'error'
- });
- });
-};
+ // 鏋勯�犺姹傚弬鏁�
+ const params = { ...searchForm.value, ...page };
-// 澶勭悊娲惧伐鎿嶄綔
-const handleDispatch = (item) => {
- if (item.pendingQuantity <= 0) {
- uni.showToast({
- title: '璇ラ」鐩棤闇�鍐嶆淳宸�',
- icon: 'none'
- });
- return;
- }
-
- dispatchModalRef.value?.open(item);
-};
+ // schedulingListPage(params).then((res) => {
+ // loading.value = false;
+ // closeToast();
-// 澶勭悊娲惧伐纭
-const handleDispatchConfirm = () => {
- getList(); // 鍒锋柊鍒楄〃
-};
+ // // 澶勭悊姣忔潯鏁版嵁锛屽鍔爌endingQuantity瀛楁
+ // tableData.value = (res.data.records || []).map(item => ({
+ // ...item,
+ // pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0)
+ // }));
+ // }).catch(() => {
+ // loading.value = false;
+ // closeToast();
+ // uni.showToast({
+ // title: '鍔犺浇澶辫触',
+ // icon: 'error'
+ // });
+ // });
+ };
-// 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
-onShow(() => {
- // 鍔犺浇鍒楄〃鏁版嵁
- getList();
-});
+ // 澶勭悊娲惧伐鎿嶄綔
+ const handleDispatch = item => {
+ if (item.pendingQuantity <= 0) {
+ uni.showToast({
+ title: "璇ラ」鐩棤闇�鍐嶆淳宸�",
+ icon: "none",
+ });
+ return;
+ }
+
+ dispatchModalRef.value?.open(item);
+ };
+
+ // 澶勭悊娲惧伐纭
+ const handleDispatchConfirm = () => {
+ getList(); // 鍒锋柊鍒楄〃
+ };
+
+ // 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
+ onShow(() => {
+ // 鍔犺浇鍒楄〃鏁版嵁
+ getList();
+ });
</script>
<style scoped lang="scss">
-@import '@/styles/sales-common.scss';
+ @import "@/styles/sales-common.scss";
-// 鐢熶骇娲惧伐椤甸潰鏍峰紡
-.production-dispatching {
- min-height: 100vh;
- background: #f8f9fa;
- position: relative;
-}
+ // 鐢熶骇娲惧伐椤甸潰鏍峰紡
+ .production-dispatching {
+ min-height: 100vh;
+ background: #f8f9fa;
+ position: relative;
+ }
-// 鍒楄〃椤规牱寮�
-.ledger-item {
- .detail-value.highlight {
- color: #ff6b35;
- font-weight: 600;
- }
-
- .detail-value.danger {
- color: #ee0a24;
- font-weight: 600;
- }
-}
+ // 鍒楄〃椤规牱寮�
+ .ledger-item {
+ .detail-value.highlight {
+ color: #ff6b35;
+ font-weight: 600;
+ }
-// 閫傞厤 uView 缁勪欢鏍峰紡
-:deep(.up-input) {
- background: transparent;
-}
+ .detail-value.danger {
+ color: #ee0a24;
+ font-weight: 600;
+ }
+ }
+
+ // 閫傞厤 uView 缁勪欢鏍峰紡
+ :deep(.up-input) {
+ background: transparent;
+ }
</style>
diff --git a/src/pages/productionManagement/productionOrder/index.vue b/src/pages/productionManagement/productionOrder/index.vue
index 7adcc4d..5f31fb1 100644
--- a/src/pages/productionManagement/productionOrder/index.vue
+++ b/src/pages/productionManagement/productionOrder/index.vue
@@ -1,193 +1,546 @@
<template>
- <view class="production-order">
- <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
- <PageHeader title="鐢熶骇璁㈠崟" @back="goBack" />
-
- <!-- 鎼滅储鍖哄煙 -->
- <view class="search-section">
- <view class="search-bar">
- <view class="search-input">
- <up-input
- class="search-text"
- placeholder="璇疯緭鍏ュ鎴峰悕绉版悳绱�"
- v-model="searchForm.customerName"
- @change="handleQuery"
- clearable
- />
- </view>
- <view class="filter-button" @click="handleQuery">
- <up-icon name="search" size="24" color="#999"></up-icon>
- </view>
- </view>
- </view>
-
- <!-- 鐢熶骇璁㈠崟鍒楄〃 -->
- <view class="ledger-list" v-if="tableData.length > 0">
- <view v-for="(item, index) in tableData" :key="item.id || index">
- <view 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.salesContractNo }}</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.entryDate }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">瀹㈡埛鍚堝悓鍙�</text>
- <text class="detail-value">{{ item.customerContractNo }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">瀹㈡埛鍚嶇О</text>
- <text class="detail-value">{{ item.customerName }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">椤圭洰鍚嶇О</text>
- <text class="detail-value">{{ item.projectName }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">浜у搧澶х被</text>
- <text class="detail-value">{{ item.productCategory }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">瑙勬牸鍨嬪彿</text>
- <text class="detail-value">{{ item.specificationModel }}</text>
- </view>
- <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">鎺掍骇鏁伴噺</text>
- <text class="detail-value highlight">{{ item.schedulingNum }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">瀹屽伐鏁伴噺</text>
- <text class="detail-value highlight">{{ item.successNum }}</text>
- </view>
- </view>
- </view>
- </view>
- </view>
- <view v-else class="no-data">
- <text>鏆傛棤鐢熶骇璁㈠崟鏁版嵁</text>
- </view>
- </view>
+ <view class="production-order">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="鐢熶骇璁㈠崟"
+ @back="goBack" />
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ヨ鍗曞彿鎴栦骇鍝佸悕绉�"
+ v-model="searchForm.keyword"
+ @change="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="list-container"
+ v-if="tableData.length > 0"
+ @scrolltolower="loadMore">
+ <view v-for="(item, index) in tableData"
+ :key="item.id || index">
+ <view 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.npsNo }}</text>
+ </view>
+ <view class="item-right">
+ <up-tag :text="getStatusText(item.status)"
+ :type="getStatusType(item.status)"
+ size="mini" />
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">浜у搧鍚嶇О</text>
+ <text class="detail-value font-bold">{{ item.productName || '-' }}</text>
+ </view>
+ <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.quantity || 0 }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀹屾垚杩涘害</text>
+ <view class="progress-box">
+ <up-line-progress :percentage="toProgressPercentage(item.completionStatus)"
+ :activeColor="progressColor(item.completionStatus)"
+ height="10"></up-line-progress>
+ <text class="progress-text">{{ item.completeQuantity || 0 }} / {{ item.quantity || 0 }}</text>
+ </view>
+ </view>
+ <!-- 宸ュ簭鐢熶骇杩涘害灞曠ず -->
+ <view class="detail-row process-row">
+ <text class="detail-label">宸ュ簭杩涘害</text>
+ <scroll-view scroll-x
+ class="process-scroll">
+ <view class="process-container">
+ <view v-for="(process, pIdx) in item.processRouteStatus"
+ :key="pIdx"
+ class="process-item">
+ <view class="process-node">
+ <view class="node-circle"
+ :class="{ 'is-complete': process.percentage >= 100 }">
+ <text class="node-percentage"
+ :style="{ color: process.percentage >= 100 ? '#52c41a' : (process.percentage >= 70 ? '#f56c6c' : '#3c9cff') }">{{ process.percentage }}%</text>
+ </view>
+ <text class="node-name">{{ process.name }}</text>
+ </view>
+ <view v-if="pIdx < item.processRouteStatus.length - 1"
+ class="node-line"></view>
+ </view>
+ <view v-if="!item.processRouteStatus || !item.processRouteStatus.length"
+ class="no-process">-</view>
+ </view>
+ </scroll-view>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">璁″垝瀹屾垚</text>
+ <text class="detail-value">{{ formatDate(item.planCompleteTime) }}</text>
+ </view>
+ </view>
+ <view class="item-footer">
+ <view class="action-btns">
+ <up-button type="info"
+ size="small"
+ plain
+ text="鐢熶骇杩芥函"
+ @click="goTraceability(item)"></up-button>
+ <up-button type="info"
+ size="small"
+ plain
+ text="宸ヨ壓璺嚎"
+ @click="goProcessRoute(item)"></up-button>
+ <up-button type="primary"
+ size="small"
+ plain
+ text="鏉ユ簮"
+ @click="goSource(item)"></up-button>
+ <up-button type="success"
+ size="small"
+ plain
+ text="棰嗘枡璇︽儏"
+ @click="goPickingDetail(item)"></up-button>
+ </view>
+ </view>
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus"
+ v-if="tableData.length >= page.size" />
+ </scroll-view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤鐢熶骇璁㈠崟鏁版嵁"></up-empty>
+ </view>
+ </view>
</template>
<script setup>
-import { ref, reactive, toRefs, getCurrentInstance } from "vue";
-import { onShow } from '@dcloudio/uni-app';
-import dayjs from "dayjs";
-import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
-import PageHeader from "@/components/PageHeader.vue";
-const { proxy } = getCurrentInstance();
+ import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import dayjs from "dayjs";
+ import {
+ productOrderListPage,
+ getOrderProcessRouteMain,
+ } from "@/api/productionManagement/productionOrder.js";
+ import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
+ import PageHeader from "@/components/PageHeader.vue";
-// 鍔犺浇鐘舵��
-const loading = ref(false);
-// 鍒楄〃鏁版嵁
-const tableData = ref([]);
+ const { proxy } = getCurrentInstance();
-// 鍒嗛〉閰嶇疆
-const page = reactive({
- current: -1,
- size: -1,
- total: 0,
-});
+ // 鍔犺浇鐘舵��
+ const loading = ref(false);
+ const loadStatus = ref("loadmore");
+ // 鍒楄〃鏁版嵁
+ const tableData = ref([]);
-// 鎼滅储琛ㄥ崟鏁版嵁
-const data = reactive({
- searchForm: {
- customerName: "",
- },
-});
-const { searchForm } = toRefs(data);
+ // 鍒嗛〉閰嶇疆
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
-// 閫氱敤鎻愮ず鍑芥暟
-const showLoadingToast = (message) => {
- uni.showLoading({
- title: message,
- mask: true
- });
-};
+ // 鎼滅储琛ㄥ崟鏁版嵁
+ const data = reactive({
+ searchForm: {
+ keyword: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
-const closeToast = () => {
- uni.hideLoading();
-};
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
-// 杩斿洖涓婁竴椤�
-const goBack = () => {
- uni.navigateBack();
-};
+ // 鏍煎紡鍖栨棩鏈�
+ const formatDate = date => {
+ return date ? dayjs(date).format("YYYY-MM-DD") : "-";
+ };
-// 鏌ヨ鍒楄〃
-const handleQuery = () => {
- page.current = 1;
- tableData.value = []; // 閲嶇疆鍒楄〃鏁版嵁
- getList();
-};
+ // 鑾峰彇鐘舵�佹枃鏈�
+ const getStatusText = status => {
+ const statusMap = {
+ 1: "寰呭紑濮�",
+ 2: "杩涜涓�",
+ 3: "宸插畬鎴�",
+ 4: "宸插彇娑�",
+ 5: "宸茬粨鏉�",
+ };
+ return statusMap[status] || "鏈煡";
+ };
-// 鑾峰彇鍒楄〃鏁版嵁
-const getList = () => {
- loading.value = true;
- showLoadingToast('鍔犺浇涓�...');
-
- // 鏋勯�犺姹傚弬鏁�
- const params = { ...searchForm.value, ...page };
-
- schedulingListPage(params).then((res) => {
- loading.value = false;
- closeToast();
-
- tableData.value = res.data.records || [];
- }).catch(() => {
- loading.value = false;
- closeToast();
- uni.showToast({
- title: '鍔犺浇澶辫触',
- icon: 'error'
- });
- });
-};
+ // 鑾峰彇鐘舵�佺被鍨�
+ const getStatusType = status => {
+ const typeMap = {
+ 1: "primary",
+ 2: "warning",
+ 3: "success",
+ 4: "info",
+ 5: "error",
+ };
+ return typeMap[status] || "info";
+ };
-// 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
-onShow(() => {
- // 鍔犺浇鍒楄〃鏁版嵁
- getList();
-});
+ // 瀹屾垚杩涘害鐧惧垎姣�
+ const toProgressPercentage = val => {
+ const n = Number(val);
+ if (!Number.isFinite(n)) return 0;
+ if (n <= 0) return 0;
+ if (n >= 100) return 100;
+ return Math.round(n);
+ };
+
+ // 杩涘害鏉¢鑹�
+ const progressColor = percentage => {
+ const p = toProgressPercentage(percentage);
+ if (p < 30) return "#f56c6c";
+ if (p < 50) return "#e6a23c";
+ if (p < 80) return "#409eff";
+ return "#67c23a";
+ };
+
+ // 鏌ヨ鍒楄〃
+ const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
+ getList();
+ };
+
+ // 鍔犺浇鏇村
+ const loadMore = () => {
+ if (loadStatus.value === "nomore" || loading.value) return;
+ page.current++;
+ getList();
+ };
+
+ // 鑾峰彇鍒楄〃鏁版嵁
+ const getList = () => {
+ loading.value = true;
+ loadStatus.value = "loading";
+
+ const params = {
+ current: page.current,
+ size: page.size,
+ npsNo: searchForm.value.keyword,
+ productName: searchForm.value.keyword,
+ };
+
+ productOrderListPage(params)
+ .then(async res => {
+ const records = res.data.records || [];
+
+ // 涓烘瘡涓鍗曞苟琛屾煡璇㈠伐搴忚繘搴�
+ const processPromises = records.map(async item => {
+ if (item.npsNo) {
+ try {
+ const workOrderRes = await productWorkOrderPage({
+ npsNo: item.npsNo,
+ size: 100,
+ });
+ const workOrders = workOrderRes.data.records || [];
+ const processRouteStatus = workOrders.map(wo => ({
+ name: wo.operationName || "鏈煡宸ュ簭",
+ percentage:
+ Number(wo.completionStatus) > 100
+ ? 100
+ : Number(wo.completionStatus || 0),
+ }));
+ return { ...item, processRouteStatus };
+ } catch (error) {
+ console.error(`鑾峰彇宸ュ崟 ${item.npsNo} 杩涘害澶辫触:`, error);
+ return { ...item, processRouteStatus: [] };
+ }
+ }
+ return { ...item, processRouteStatus: [] };
+ });
+
+ const updatedRecords = await Promise.all(processPromises);
+
+ loading.value = false;
+ if (page.current === 1) {
+ tableData.value = updatedRecords;
+ } else {
+ tableData.value = [...tableData.value, ...updatedRecords];
+ }
+
+ if (updatedRecords.length < page.size) {
+ loadStatus.value = "nomore";
+ } else {
+ loadStatus.value = "loadmore";
+ }
+ page.total = res.data.total || 0;
+ })
+ .catch(() => {
+ loading.value = false;
+ loadStatus.value = "loadmore";
+ uni.showToast({
+ title: "鍔犺浇澶辫触",
+ icon: "error",
+ });
+ });
+ };
+
+ // 璺宠浆宸ヨ壓璺嚎 (BOM)
+ const goProcessRoute = item => {
+ getOrderProcessRouteMain(item.id)
+ .then(res => {
+ const data = res.data || {};
+ if (!data.id) {
+ uni.showToast({ title: "鏈壘鍒板伐鑹鸿矾绾�", icon: "none" });
+ return;
+ }
+ uni.navigateTo({
+ url: `/pages/productionManagement/processRoute/items?id=${
+ data.id
+ }&bomId=${data.orderBomId}&processRouteCode=${
+ data.processRouteCode || ""
+ }&productName=${encodeURIComponent(
+ item.productName || ""
+ )}&model=${encodeURIComponent(item.model || "")}&orderId=${
+ item.id
+ }&type=order`,
+ });
+ })
+ .catch(() => {
+ uni.showToast({ title: "鑾峰彇璺嚎澶辫触", icon: "none" });
+ });
+ };
+
+ // 璺宠浆鏉ユ簮
+ const goSource = item => {
+ uni.navigateTo({
+ url: `/pages/productionManagement/productionOrder/source?id=${
+ item.id
+ }&productName=${encodeURIComponent(
+ item.productName
+ )}&model=${encodeURIComponent(item.model)}&quantity=${item.quantity}`,
+ });
+ };
+
+ // 璺宠浆棰嗘枡璇︽儏
+ const goPickingDetail = item => {
+ uni.navigateTo({
+ url: `/pages/productionManagement/productionOrder/pickingDetail?id=${item.id}&npsNo=${item.npsNo}`,
+ });
+ };
+
+ // 璺宠浆鐢熶骇杩芥函
+ const goTraceability = item => {
+ uni.navigateTo({
+ url: `/pages/productionManagement/productionTraceability/index?npsNo=${item.npsNo}`,
+ });
+ };
+
+ // 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
+ onShow(() => {
+ handleQuery();
+ });
</script>
<style scoped lang="scss">
-@import '@/styles/sales-common.scss';
+ @import "@/styles/sales-common.scss";
-// 鐢熶骇璁㈠崟椤甸潰鏍峰紡
-.production-order {
- min-height: 100vh;
- background: #f8f9fa;
- position: relative;
-}
+ .production-order {
+ min-height: 100vh;
+ background: #f8f9fa;
+ display: flex;
+ flex-direction: column;
+ }
-// 閲嶅啓閮ㄥ垎鏍峰紡浠ラ�傞厤鐢熶骇璁㈠崟
-.ledger-item {
- .detail-value.highlight {
- color: #ff6b35;
- font-weight: 600;
- }
-}
+ .list-container {
+ flex: 1;
+ height: 0;
+ }
-// 閫傞厤 uView 缁勪欢鏍峰紡
-:deep(.up-input) {
- background: transparent;
-}
+ .ledger-item {
+ background: #fff;
+ margin: 20rpx;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
-:deep(.up-datetime-picker) {
- width: 100%;
-}
+ .item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 12rpx;
+
+ .item-left {
+ display: flex;
+ align-items: center;
+
+ .document-icon {
+ width: 44rpx;
+ height: 44rpx;
+ background: #3c9cff;
+ border-radius: 10rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 20rpx;
+ }
+
+ .item-id {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+ }
+
+ .item-details {
+ padding: 16rpx 0;
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 16rpx;
+
+ .detail-label {
+ font-size: 26rpx;
+ color: #999;
+ min-width: 140rpx;
+ }
+
+ .detail-value {
+ font-size: 26rpx;
+ color: #333;
+ text-align: right;
+
+ &.font-bold {
+ font-weight: bold;
+ }
+ }
+
+ .progress-box {
+ flex: 1;
+ margin-left: 40rpx;
+
+ .progress-text {
+ font-size: 22rpx;
+ color: #999;
+ margin-top: 4rpx;
+ display: block;
+ text-align: right;
+ }
+ }
+
+ &.process-row {
+ flex-direction: column;
+ margin: 20rpx 0;
+
+ .process-scroll {
+ width: 100%;
+ margin-top: 16rpx;
+
+ .process-container {
+ display: flex;
+ align-items: flex-start;
+ padding: 10rpx 0;
+ min-height: 120rpx;
+
+ .process-item {
+ display: flex;
+ align-items: center;
+
+ .process-node {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100rpx;
+
+ .node-circle {
+ width: 60rpx;
+ height: 60rpx;
+ border-radius: 50%;
+ border: 2rpx solid #3c9cff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #fff;
+ margin-bottom: 8rpx;
+
+ .node-percentage {
+ font-size: 18rpx;
+ color: #3c9cff;
+ font-weight: bold;
+ }
+
+ &.is-complete {
+ border-color: #52c41a;
+ background: #f6ffed;
+
+ .node-percentage {
+ color: #52c41a;
+ }
+ }
+ }
+
+ .node-name {
+ font-size: 20rpx;
+ color: #666;
+ text-align: center;
+ width: 120rpx;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ .node-line {
+ width: 40rpx;
+ height: 2rpx;
+ background: #e8e8e8;
+ margin: -30rpx 0 0 0;
+ }
+ }
+
+ .no-process {
+ font-size: 24rpx;
+ color: #ccc;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .item-footer {
+ padding-top: 20rpx;
+ border-top: 1rpx solid #f0f0f0;
+ display: flex;
+ justify-content: flex-end;
+
+ .action-btns {
+ display: flex;
+ gap: 20rpx;
+ }
+ }
+ }
+
+ .no-data {
+ padding-top: 200rpx;
+ }
</style>
diff --git a/src/pages/productionManagement/productionOrder/pickingDetail.vue b/src/pages/productionManagement/productionOrder/pickingDetail.vue
new file mode 100644
index 0000000..7aad62d
--- /dev/null
+++ b/src/pages/productionManagement/productionOrder/pickingDetail.vue
@@ -0,0 +1,350 @@
+<template>
+ <view class="picking-detail">
+ <PageHeader title="棰嗘枡璇︽儏"
+ @back="goBack" />
+ <scroll-view scroll-y
+ class="detail-list"
+ v-if="detailList.length > 0">
+ <view v-for="(item, index) in detailList"
+ :key="index"
+ class="material-card">
+ <view class="card-header">
+ <text class="material-name">{{ item.materialName || item.productName || '-' }}</text>
+ <up-tag :text="item.operationName || '-'"
+ type="info"
+ size="mini"
+ plain />
+ </view>
+ <view class="card-content">
+ <view class="info-grid">
+ <view class="info-item">
+ <text class="label">瑙勬牸鍨嬪彿</text>
+ <text class="value">{{ item.model || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">鍗曚綅</text>
+ <text class="value">{{ item.unit || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">闇�棰嗘暟閲�</text>
+ <text class="value highlight">{{ item.qtyRequired || item.demandedQuantity || 0 }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">宸查鏁伴噺</text>
+ <text class="value success">{{ item.qtyPicked || item.pickQuantity || 0 }}</text>
+ </view>
+ <view class="info-item">
+ <text class="label">琛ユ枡鏁伴噺</text>
+ <view class="value link"
+ @click="showSupplementDetail(item)">
+ {{ item.qtySupplement || item.feedingQty || 0 }}
+ </view>
+ </view>
+ <view class="info-item">
+ <text class="label">閫�鏂欐暟閲�</text>
+ <text class="value">{{ item.returnQty || 0 }}</text>
+ </view>
+ </view>
+ <view class="remark-row"
+ v-if="item.remark">
+ <text class="label">澶囨敞锛�</text>
+ <text class="value">{{ item.remark }}</text>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="data"
+ text="鏆傛棤棰嗘枡璇︽儏"></up-empty>
+ </view>
+ <!-- 琛ユ枡璁板綍寮圭獥 -->
+ <up-popup :show="showPopup"
+ mode="bottom"
+ @close="showPopup = false"
+ round="10">
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="title">琛ユ枡璁板綍</text>
+ <up-icon name="close"
+ size="20"
+ @click="showPopup = false"></up-icon>
+ </view>
+ <scroll-view scroll-y
+ class="record-list">
+ <view v-if="supplementRecords.length > 0">
+ <view v-for="(record, rIndex) in supplementRecords"
+ :key="rIndex"
+ class="record-item">
+ <view class="record-row">
+ <text class="record-label">琛ユ枡鏁伴噺锛�</text>
+ <text class="record-value highlight">{{ record.pickQuantity || 0 }}</text>
+ </view>
+ <view class="record-row">
+ <text class="record-label">琛ユ枡浜猴細</text>
+ <text class="record-value">{{ record.supplementUserName || '-' }}</text>
+ </view>
+ <view class="record-row">
+ <text class="record-label">琛ユ枡鏃ユ湡锛�</text>
+ <text class="record-value">{{ record.supplementTime || '-' }}</text>
+ </view>
+ <view class="record-row">
+ <text class="record-label">琛ユ枡鍘熷洜锛�</text>
+ <text class="record-value">{{ record.feedingReason || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-record">
+ <text>鏆傛棤琛ユ枡璁板綍</text>
+ </view>
+ </scroll-view>
+ </view>
+ </up-popup>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import {
+ listMaterialPickingDetail,
+ listMaterialSupplementRecord,
+ } from "@/api/productionManagement/productionOrder.js";
+ import PageHeader from "@/components/PageHeader.vue";
+
+ const npsNo = ref("");
+ const productionOrderId = ref("");
+ const detailList = ref([]);
+ const loading = ref(false);
+
+ // 寮圭獥鐩稿叧
+ const showPopup = ref(false);
+ const supplementRecords = ref([]);
+ const recordLoading = ref(false);
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const calculatePending = item => {
+ const required = Number(item.qtyRequired || item.demandedQuantity || 0);
+ const picked = Number(item.qtyPicked || item.pickQuantity || 0);
+ return Math.max(0, required - picked);
+ };
+
+ onLoad(options => {
+ if (options.id) {
+ productionOrderId.value = options.id;
+ npsNo.value = options.npsNo || "";
+ fetchDetail(options.id);
+ }
+ });
+
+ const fetchDetail = id => {
+ loading.value = true;
+ listMaterialPickingDetail(id)
+ .then(res => {
+ detailList.value = res.data?.records || res.data || [];
+ loading.value = false;
+ })
+ .catch(() => {
+ loading.value = false;
+ uni.showToast({
+ title: "鑾峰彇璇︽儏澶辫触",
+ icon: "error",
+ });
+ });
+ };
+
+ const showSupplementDetail = item => {
+ const qty = Number(item.qtySupplement || item.feedingQty || 0);
+ if (qty <= 0) return;
+
+ showPopup.value = true;
+ recordLoading.value = true;
+ supplementRecords.value = [];
+
+ listMaterialSupplementRecord({
+ pickId: item.id,
+ productionOrderId: productionOrderId.value,
+ })
+ .then(res => {
+ supplementRecords.value = res.data || [];
+ recordLoading.value = false;
+ })
+ .catch(() => {
+ recordLoading.value = false;
+ uni.showToast({
+ title: "鑾峰彇琛ユ枡璁板綍澶辫触",
+ icon: "error",
+ });
+ });
+ };
+</script>
+
+<style scoped lang="scss">
+ .picking-detail {
+ min-height: 100vh;
+ background: #f8f9fa;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .detail-list {
+ flex: 1;
+ height: 0;
+ padding: 20rpx;
+ }
+
+ .material-card {
+ background: #fff;
+ margin-bottom: 24rpx;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 20rpx;
+ border-bottom: 1rpx solid #f5f5f5;
+ margin-bottom: 20rpx;
+
+ .material-name {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+
+ .info-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 20rpx;
+
+ .info-item {
+ display: flex;
+ flex-direction: column;
+
+ .label {
+ font-size: 24rpx;
+ color: #999;
+ margin-bottom: 4rpx;
+ }
+
+ .value {
+ font-size: 26rpx;
+ color: #333;
+
+ &.highlight {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+ &.success {
+ color: #67c23a;
+ font-weight: bold;
+ }
+ &.warning {
+ color: #e6a23c;
+ font-weight: bold;
+ }
+ &.link {
+ color: #3c9cff;
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+
+ .remark-row {
+ margin-top: 20rpx;
+ padding-top: 16rpx;
+ border-top: 1rpx dashed #eee;
+ display: flex;
+
+ .label {
+ font-size: 24rpx;
+ color: #999;
+ }
+ .value {
+ font-size: 24rpx;
+ color: #666;
+ flex: 1;
+ }
+ }
+ }
+
+ .no-data {
+ padding-top: 200rpx;
+ }
+
+ /* 寮圭獥鏍峰紡 */
+ .popup-content {
+ background: #fff;
+ padding: 30rpx;
+ max-height: 70vh;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 30rpx;
+ border-bottom: 1rpx solid #eee;
+
+ .title {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+
+ .record-list {
+ flex: 1;
+ height: 0;
+ padding-top: 20rpx;
+ }
+
+ .record-item {
+ padding: 24rpx;
+ background: #f9f9f9;
+ border-radius: 12rpx;
+ margin-bottom: 20rpx;
+
+ .record-row {
+ display: flex;
+ margin-bottom: 10rpx;
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .record-label {
+ font-size: 26rpx;
+ color: #999;
+ width: 140rpx;
+ }
+
+ .record-value {
+ font-size: 26rpx;
+ color: #333;
+ flex: 1;
+
+ &.highlight {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+ }
+ }
+ }
+
+ .no-record {
+ padding: 100rpx 0;
+ text-align: center;
+ color: #999;
+ font-size: 28rpx;
+ }
+</style>
diff --git a/src/pages/productionManagement/productionOrder/source.vue b/src/pages/productionManagement/productionOrder/source.vue
new file mode 100644
index 0000000..bd06845
--- /dev/null
+++ b/src/pages/productionManagement/productionOrder/source.vue
@@ -0,0 +1,166 @@
+<template>
+ <view class="production-order-source">
+ <PageHeader title="鏉ユ簮鏁版嵁" @back="goBack" />
+
+ <view class="summary-card" v-if="summary">
+ <view class="summary-item">
+ <text class="label">浜у搧鍚嶇О</text>
+ <up-tag :text="summary.productName || '-'" type="primary" size="mini" />
+ </view>
+ <view class="summary-item">
+ <text class="label">瑙勬牸鍨嬪彿</text>
+ <text class="value">{{ summary.model || '-' }}</text>
+ </view>
+ <view class="summary-item">
+ <text class="label">闇�姹傛暟閲�</text>
+ <text class="value highlight">{{ summary.quantity || 0 }}</text>
+ </view>
+ </view>
+
+ <scroll-view scroll-y class="source-list" v-if="sourceList.length > 0">
+ <view v-for="(item, index) in sourceList" :key="index" class="source-card">
+ <view class="card-header">
+ <text class="plan-no">璁″垝鍙�: {{ item.mpsNo || '-' }}</text>
+ <up-tag :text="item.source || '鏈煡'" :type="item.source === '閿�鍞�' ? 'primary' : 'warning'" size="mini" />
+ </view>
+ <view class="card-content">
+ <view class="info-row">
+ <text class="info-label">鍚堝悓鍙�</text>
+ <text class="info-value">{{ item.salesContractNo || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="info-label">瀹㈡埛鍚嶇О</text>
+ <text class="info-value">{{ item.customerName || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="info-label">椤圭洰鍚嶇О</text>
+ <text class="info-value">{{ item.projectName || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="info-label">闇�姹傛暟閲�</text>
+ <text class="info-value">{{ item.qtyRequired || 0 }} {{ item.unit || '' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="info-label">闇�姹傛棩鏈�</text>
+ <text class="info-value">{{ formatDate(item.requiredDate) }}</text>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+
+ <view v-else class="no-data">
+ <up-empty mode="data" text="鏆傛棤鏉ユ簮鏁版嵁"></up-empty>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { onLoad } from '@dcloudio/uni-app';
+import dayjs from "dayjs";
+import { getProductOrderSource } from "@/api/productionManagement/productionOrder.js";
+import PageHeader from "@/components/PageHeader.vue";
+
+const summary = ref(null);
+const sourceList = ref([]);
+const loading = ref(false);
+
+const goBack = () => {
+ uni.navigateBack();
+};
+
+const formatDate = (date) => {
+ return date ? dayjs(date).format('YYYY-MM-DD') : '-';
+};
+
+onLoad((options) => {
+ if (options.id) {
+ summary.value = {
+ productName: decodeURIComponent(options.productName || ''),
+ model: decodeURIComponent(options.model || ''),
+ quantity: options.quantity || 0
+ };
+ fetchSource(options.id);
+ }
+});
+
+const fetchSource = (id) => {
+ loading.value = true;
+ getProductOrderSource(id).then(res => {
+ sourceList.value = res.data || [];
+ loading.value = false;
+ }).catch(() => {
+ loading.value = false;
+ uni.showToast({
+ title: '鑾峰彇鏉ユ簮澶辫触',
+ icon: 'error'
+ });
+ });
+};
+</script>
+
+<style scoped lang="scss">
+.production-order-source {
+ min-height: 100vh;
+ background: #f8f9fa;
+ display: flex;
+ flex-direction: column;
+}
+
+.summary-card {
+ background: #fff;
+ margin: 20rpx;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05);
+
+ .summary-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12rpx;
+ &:last-child { margin-bottom: 0; }
+
+ .label { font-size: 26rpx; color: #999; }
+ .value { font-size: 26rpx; color: #333; }
+ .highlight { color: #f56c6c; font-weight: bold; }
+ }
+}
+
+.source-list {
+ flex: 1;
+ height: 0;
+ padding: 0 20rpx;
+}
+
+.source-card {
+ background: #fff;
+ margin-bottom: 20rpx;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.03);
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 16rpx;
+ border-bottom: 1rpx solid #f9f9f9;
+ margin-bottom: 16rpx;
+
+ .plan-no { font-size: 28rpx; font-weight: bold; color: #333; }
+ }
+
+ .info-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+ &:last-child { margin-bottom: 0; }
+
+ .info-label { font-size: 24rpx; color: #999; }
+ .info-value { font-size: 24rpx; color: #666; }
+ }
+}
+
+.no-data { padding-top: 100rpx; }
+</style>
diff --git a/src/pages/productionManagement/productionReport/index.vue b/src/pages/productionManagement/productionReport/index.vue
index dcd0fd7..ac92038 100644
--- a/src/pages/productionManagement/productionReport/index.vue
+++ b/src/pages/productionManagement/productionReport/index.vue
@@ -18,7 +18,7 @@
placeholder="鑷姩濉厖"
disabled />
</u-form-item>
- <u-form-item label="鏈鐢熶骇鏁伴噺"
+ <u-form-item label="鐢熶骇鍚堟牸鏁伴噺"
prop="quantity"
required>
<u-input v-model="form.quantity"
@@ -40,6 +40,67 @@
@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"
+ v-if="params.length > 0">
+ <view class="section-title">宸ュ簭鍙傛暟</view>
+ <u-form-item v-for="param in params"
+ :key="param.id"
+ :label="param.paramName"
+ :label-width="110"
+ :required="param.required === '1'">
+ <!-- 鏁板瓧绫诲瀷 -->
+ <template v-if="param.paramType == '1'">
+ <u-input v-model="form.paramGroups[param.id]"
+ type="number"
+ :placeholder="'璇疯緭鍏�' + param.paramName"
+ :key="param.id" />
+ <text v-if="param.unit && param.unit != '/'"
+ class="param-unit">{{ param.unit }}</text>
+ </template>
+ <!-- 鏂囨湰绫诲瀷 -->
+ <template v-else-if="param.paramType == '2'">
+ <u-input v-model="form.paramGroups[param.id]"
+ :placeholder="'璇疯緭鍏�' + param.paramName"
+ :key="param.id" />
+ <text v-if="param.unit && param.unit != '/'"
+ class="param-unit">{{ param.unit }}</text>
+ </template>
+ <!-- 閫夋嫨绫诲瀷 -->
+ <template v-else-if="param.paramType == '3'">
+ <u-input v-model="form.paramGroups[param.id]"
+ readonly
+ :placeholder="'璇烽�夋嫨' + param.paramName"
+ @click="openParamSelect(param)"
+ suffix-icon="arrow-down" />
+ </template>
+ <!-- 鏃ユ湡绫诲瀷 -->
+ <template v-else-if="param.paramType == '4'">
+ <u-input v-model="form.paramGroups[param.id]"
+ readonly
+ :placeholder="'璇烽�夋嫨' + param.paramName"
+ @click="openDateParamPicker(param)"
+ suffix-icon="arrow-down" />
+ <text v-if="param.unit && param.unit != '/'"
+ class="param-unit">{{ param.unit }}</text>
+ </template>
+ <!-- 榛樿鏂囨湰 -->
+ <template v-else>
+ <u-input v-model="form.paramGroups[param.id]"
+ :placeholder="'璇疯緭鍏�' + param.paramName"
+ :key="param.id" />
+ </template>
+ </u-form-item>
</view>
<!-- 浣跨敤FooterButtons缁勪欢 -->
<FooterButtons @cancel="goBack"
@@ -54,6 +115,18 @@
title="閫夋嫨鐢熶骇浜�"
@select="onProducerConfirm"
@close="showProducerPicker = false" />
+ <!-- 鍙傛暟閫夋嫨鍣� -->
+ <up-action-sheet :show="showParamSelect"
+ :actions="paramOptions"
+ :title="currentParam?.paramName || '閫夋嫨'"
+ @select="onParamConfirm"
+ @close="showParamSelect = false" />
+ <!-- 鏃ユ湡閫夋嫨鍣� -->
+ <up-datetime-picker :show="showDatePicker"
+ v-model="datePickerValue"
+ :mode="datePickerMode"
+ @confirm="onDateConfirm"
+ @cancel="showDatePicker = false" />
</view>
</template>
@@ -71,34 +144,50 @@
import { addProductMain } from "@/api/productionManagement/productionReporting";
import { getInfo } from "@/api/login";
import { userListNoPageByTenantId } from "@/api/system/user";
+ import {
+ findProcessParamListOrder,
+ listMaterialPickingDetail,
+ } from "@/api/productionManagement/productionOrder.js";
+ import { getDicts } from "@/api/system/dict/data";
+ import { formatDateToYMD, parseTime } from "@/utils/ruoyi";
- // 琛ㄥ崟寮曠敤
const formRef = ref();
- // 琛ㄥ崟鏁版嵁
let form = ref({
planQuantity: "",
quantity: "",
scrapQty: "",
userName: "",
workOrderId: "",
- productProcessRouteItemId: "",
userId: "",
schedulingUserId: "",
+ reportWork: "",
+ productionOrderRoutingOperationId: "",
+ productionOrderId: "",
+ workHour: 0,
+ type: null,
+ paramGroups: {},
});
- // 鐢熶骇浜洪�夋嫨鍣ㄧ姸鎬�
const showProducerPicker = ref(false);
const producerList = ref([]);
- // 鎵撳紑鐢熶骇浜洪�夋嫨鍣�
+ const params = ref([]);
+ const dictOptions = ref({});
+ const showParamSelect = ref(false);
+ const currentParam = ref(null);
+ const paramOptions = ref([]);
+
+ const showDatePicker = ref(false);
+ const datePickerValue = ref(Date.now());
+ const datePickerMode = ref("date");
+ const currentDateParam = ref(null);
+
const openProducerPicker = async () => {
if (producerList.value.length === 0) {
- // 濡傛灉鍒楄〃涓虹┖锛屽厛鍔犺浇鐢ㄦ埛鍒楄〃
try {
const res = await userListNoPageByTenantId();
const users = res.data || [];
- // 杞崲涓� action-sheet 闇�瑕佺殑鏍煎紡
producerList.value = users.map(user => ({
name: user.nickName || user.userName,
value: user.userId,
@@ -112,102 +201,265 @@
showProducerPicker.value = true;
};
- // 鐢熶骇浜洪�夋嫨纭
const onProducerConfirm = e => {
form.value.schedulingUserId = e.value;
form.value.userName = e.name;
- form.value.userId = e.value; // 鍚屾椂鏇存柊 userId
+ form.value.userId = e.value;
showProducerPicker.value = false;
};
- // 鎻愪氦鐘舵��
+ const openParamSelect = async param => {
+ currentParam.value = param;
+ if (param.paramType == "3" && param.paramFormat) {
+ const options = await getDictOptions(param.paramFormat);
+ paramOptions.value = options.map(opt => ({
+ name: opt.dictLabel,
+ value: opt.dictLabel,
+ }));
+ }
+ showParamSelect.value = true;
+ };
+
+ const onParamConfirm = e => {
+ if (currentParam.value) {
+ form.value.paramGroups[currentParam.value.id] = e.value;
+ }
+ showParamSelect.value = false;
+ };
+
+ const openDateParamPicker = param => {
+ currentDateParam.value = param;
+ const currentValue = form.value.paramGroups[param.id];
+ datePickerValue.value = currentValue
+ ? new Date(currentValue).getTime()
+ : Date.now();
+ // 鍙傜収 PC 绔�昏緫锛氬鏋滄牸寮忔槸 yyyy-MM-dd 鍒欎负 date 妯″紡锛屽惁鍒欎负 datetime 妯″紡
+ datePickerMode.value =
+ param.paramFormat === "yyyy-MM-dd" ? "date" : "datetime";
+ showDatePicker.value = true;
+ };
+
+ const onDateConfirm = e => {
+ if (currentDateParam.value) {
+ const format =
+ currentDateParam.value.paramFormat === "yyyy-MM-dd"
+ ? "{y}-{m}-{d}"
+ : "{y}-{m}-{d} {h}:{i}:{s}";
+ form.value.paramGroups[currentDateParam.value.id] = parseTime(
+ e.value,
+ format
+ );
+ }
+ showDatePicker.value = false;
+ };
+
+ const getDictOptions = async dictType => {
+ if (!dictType) return [];
+ if (dictOptions.value[dictType]) return dictOptions.value[dictType];
+ try {
+ const res = await getDicts(dictType);
+ if (res.code === 200) {
+ dictOptions.value[dictType] = res.data;
+ return res.data;
+ }
+ return [];
+ } catch (error) {
+ console.error("鑾峰彇瀛楀吀鏁版嵁澶辫触:", error);
+ return [];
+ }
+ };
+
+ const loadParams = (productionOrderRoutingOperationId, productionOrderId) => {
+ findProcessParamListOrder({
+ productionOrderRoutingOperationId,
+ productionOrderId,
+ })
+ .then(res => {
+ if (res.code === 200) {
+ console.log(res.data, "res.data========");
+
+ const paramList = res.data || [];
+ params.value = paramList;
+ form.value.paramGroups = {};
+ paramList.forEach(param => {
+ if (!form.value.paramGroups[param.id]) {
+ form.value.paramGroups[param.id] = "";
+ }
+ if (param.paramType == "3" && param.paramFormat) {
+ getDictOptions(param.paramFormat);
+ }
+ });
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭鍙傛暟澶辫触:", err);
+ });
+ };
+
const submitting = ref(false);
- // 杩斿洖涓婁竴椤�
const goBack = () => {
uni.navigateBack();
};
- // 鎻愪氦琛ㄥ崟
+
const submitForm = async () => {
submitting.value = true;
- // 鏍¢獙琛ㄥ崟
+
if (!form.value.quantity) {
submitting.value = false;
- showToast("璇疯緭鍏ユ湰娆$敓浜ф暟閲�");
+ showToast("璇疯緭鍏ョ敓浜у悎鏍兼暟閲�");
return;
}
+
if (!form.value.schedulingUserId) {
submitting.value = false;
showToast("璇烽�夋嫨鐢熶骇浜�");
return;
}
- // 杞崲涓烘暟瀛楄繘琛屾瘮杈�
+
const quantity = Number(form.value.quantity) || 0;
const scrapQty = Number(form.value.scrapQty) || 0;
const planQuantity = Number(form.value.planQuantity);
- // 楠岃瘉鐢熶骇鏁伴噺鍜屾姤搴熸暟閲忕殑鍜屼笉鑳借秴杩囧緟鐢熶骇鏁伴噺
- if (quantity + scrapQty > planQuantity) {
+
+ if (quantity < 0) {
submitting.value = false;
- showToast("鐢熶骇鏁伴噺鍜屾姤搴熸暟閲忕殑鍜屼笉鑳借秴杩囧緟鐢熶骇鏁伴噺");
+ showToast("鐢熶骇鍚堟牸鏁伴噺蹇呴』澶т簬绛変簬0");
return;
}
- if (quantity > planQuantity) {
+
+ // if (quantity + scrapQty > planQuantity) {
+ // submitting.value = false;
+ // showToast("鐢熶骇鏁伴噺鍜屾姤搴熸暟閲忕殑鍜屼笉鑳借秴杩囧緟鐢熶骇鏁伴噺");
+ // return;
+ // }
+
+ if (scrapQty < 0) {
submitting.value = false;
- showToast("鏈鐢熶骇鏁伴噺涓嶈兘澶т簬寰呯敓浜ф暟閲�");
+ showToast("鎶ュ簾鏁伴噺涓嶈兘灏忎簬0");
return;
}
- // 鍑嗗鎻愪氦鏁版嵁锛岀‘淇濇暟閲忓瓧娈典负鏁板瓧绫诲瀷
+
+ // if (scrapQty > quantity) {
+ // submitting.value = false;
+ // showToast("鎶ュ簾鏁伴噺涓嶈兘澶т簬鏈鐢熶骇鏁伴噺");
+ // return;
+ // }
+
+ const productionOperationParamList = params.value.map(param => ({
+ ...param,
+ inputValue: form.value.paramGroups[param.id] ?? "",
+ }));
+
const submitData = {
- ...form.value,
- quantity: Number(form.value.quantity),
- scrapQty: Number(form.value.scrapQty) || 0,
- planQuantity: Number(form.value.planQuantity) || 0,
+ quantity: quantity,
+ scrapQty: scrapQty,
+ userId: form.value.userId,
+ userName: form.value.userName,
+ productionOperationTaskId: form.value.workOrderId,
+ reportWork: form.value.reportWork,
+ productionOrderRoutingOperationId:
+ form.value.productionOrderRoutingOperationId,
+ productionOrderId: form.value.productionOrderId,
+ workHour: form.value.workHour,
+ productionOperationParamList: productionOperationParamList,
};
+
console.log(submitData, "submitData");
- addProductMain(submitData).then(res => {
- if (res.code === 200) {
- showToast("鎶ュ伐鎴愬姛");
+ addProductMain(submitData)
+ .then(res => {
+ if (res.code === 200) {
+ showToast("鎶ュ伐鎴愬姛");
+ submitting.value = false;
+ setTimeout(() => {
+ goBack();
+ }, 1000);
+ } else {
+ showToast(res.msg || "鎶ュ伐澶辫触");
+ submitting.value = false;
+ }
+ })
+ .catch(() => {
+ showToast("鎶ュ伐澶辫触");
submitting.value = false;
- setTimeout(() => {
- goBack();
- }, 1000);
- } else {
- showToast(res.msg || "鎶ュ伐澶辫触");
- submitting.value = false;
- }
- });
+ });
};
- // 椤甸潰鍔犺浇鏃跺垵濮嬪寲鏁版嵁
- onLoad(options => {
+ onLoad(async options => {
console.log(options, "options");
- // 濡傛灉娌℃湁 orderRow 鍙傛暟锛岃鏄庢槸浠庨椤电洿鎺ヨ烦杞紝闇�瑕佺敤鎴锋墜鍔ㄩ�夋嫨璁㈠崟
if (!options.orderRow) {
console.log("浠庨椤佃烦杞紝鏃犺鍗曟暟鎹�");
getInfo().then(res => {
- // 榛樿浣跨敤褰撳墠鐧诲綍鐢ㄦ埛
form.value.userId = res.user.userId;
- form.value.userName = res.user.userName;
+ form.value.userName = res.user.nickName || res.user.userName;
form.value.schedulingUserId = res.user.userId;
});
return;
}
try {
- const orderRow = JSON.parse(options.orderRow);
+ const orderRow = JSON.parse(decodeURIComponent(options.orderRow));
console.log("鏋勯�犵殑orderRow:", orderRow);
- console.log(orderRow, "orderRow======########");
- // 纭繚 planQuantity 杞崲涓哄瓧绗︿覆锛屼互渚垮湪 u-input 涓纭樉绀�
- form.value.planQuantity = orderRow.planQuantity != null ? String(orderRow.planQuantity) : "";
- form.value.productProcessRouteItemId = orderRow.productProcessRouteItemId || "";
+
+ // 鍙傜収 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);
+ }
+ }
+
+ const planQuantity = Number(orderRow.planQuantity || 0);
+ const completeQuantity = Number(orderRow.completeQuantity || 0);
+ form.value.planQuantity = String(
+ Math.max(0, planQuantity - completeQuantity)
+ );
form.value.workOrderId = orderRow.id || "";
+ form.value.reportWork = orderRow.reportWork || "";
+ 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;
- form.value.userName = res.user.userName;
+ form.value.userName = res.user.nickName || res.user.userName;
form.value.schedulingUserId = res.user.userId;
});
- // 浣跨敤 nextTick 纭繚 DOM 鏇存柊
+ console.log(orderRow, "orderRow=====");
+
+ if (
+ orderRow.productionOrderRoutingOperationId &&
+ orderRow.productionOrderId
+ ) {
+ nextTick(() => {
+ loadParams(
+ orderRow.productionOrderRoutingOperationId,
+ orderRow.productionOrderId
+ );
+ });
+ }
+
nextTick(() => {
console.log("form.value after assignment:", form.value);
});
@@ -215,13 +467,30 @@
console.error("璁㈠崟瑙f瀽澶辫触:", error);
showToast("璁㈠崟瑙f瀽澶辫触");
goBack();
- return;
}
});
</script>
<style scoped lang="scss">
@import "@/static/scss/form-common.scss";
+
+ .form-section {
+ background: #fff;
+ margin-bottom: 12px;
+ padding: 0 16px;
+ }
+
+ .section-title {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #303133;
+ padding: 24rpx 0 16rpx;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .param-unit {
+ margin-left: 8rpx;
+ color: #909399;
+ font-size: 24rpx;
+ }
</style>
-
-
diff --git a/src/pages/productionManagement/productionReporting/ledger.vue b/src/pages/productionManagement/productionReporting/ledger.vue
new file mode 100644
index 0000000..fb5d1dd
--- /dev/null
+++ b/src/pages/productionManagement/productionReporting/ledger.vue
@@ -0,0 +1,424 @@
+<template>
+ <view class="reporting-ledger">
+ <PageHeader title="鎶ュ伐鍙拌处"
+ @back="goBack" />
+ <!-- 鎼滅储鍖哄煙 - 鍙傝�冮噰璐彴璐︽牱寮� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ュ伐鍗曞彿鎼滅储"
+ v-model="searchForm.keyword"
+ @change="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="list-container"
+ v-if="tableData.length > 0"
+ @scrolltolower="loadMore">
+ <view class="ledger-list">
+ <view v-for="(item, index) in tableData"
+ :key="item.id || index">
+ <view 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.productNo || '-' }}</text>
+ </view>
+ <view class="item-tag">
+ <text class="create-time">{{ formatDate(item.createTime) }}</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">鎶ュ伐浜哄憳</text>
+ <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 || '-'"
+ type="primary"
+ size="mini"
+ plain />
+ </view>
+ </view>
+ <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>
+ <text class="detail-value font-bold">{{ item.productName || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瑙勬牸鍨嬪彿</text>
+ <text class="detail-value">{{ item.productModelName || '-' }}</text>
+ </view>
+ <view class="quantity-section">
+ <view class="qty-item">
+ <text class="qty-label">浜у嚭鏁伴噺</text>
+ <text class="qty-value success">{{ item.quantity || 0 }} {{ item.unit || '' }}</text>
+ </view>
+ <view class="qty-item">
+ <text class="qty-label">鎶ュ簾鏁伴噺</text>
+ <text class="qty-value error">{{ item.scrapQty || 0 }}</text>
+ </view>
+ </view>
+ <view class="item-footer">
+ <view class="action-buttons">
+ <up-button type="primary"
+ size="small"
+ plain
+ text="鏌ョ湅鎶曞叆"
+ @click="handleShowInput(item)"></up-button>
+ <up-button type="info"
+ size="small"
+ plain
+ text="鍙傛暟璇︽儏"
+ @click="handleShowParams(item)"></up-button>
+ <!-- <up-button type="error"
+ size="small"
+ plain
+ text="鍒犻櫎"
+ @click="handleDelete(item)"></up-button> -->
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus"
+ v-if="tableData.length >= page.size" />
+ </scroll-view>
+ <view v-else
+ class="empty-state">
+ <up-empty mode="data"
+ text="鏆傛棤鎶ュ伐鍙拌处鏁版嵁"></up-empty>
+ </view>
+ <!-- 鎶曞叆璇︽儏寮圭獥 -->
+ <up-modal :show="inputVisible"
+ title="鎶曞叆璇︽儏"
+ @confirm="inputVisible = false">
+ <view class="modal-content scroll-view">
+ <view v-if="inputList.length > 0">
+ <view v-for="(input, idx) in inputList"
+ :key="idx"
+ class="detail-item">
+ <view class="detail-row">
+ <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 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 || 0 }} {{ input.unit || '' }}</text>
+ </view>
+ <up-divider></up-divider>
+ </view>
+ </view>
+ <up-empty v-else
+ mode="data"
+ text="鏆傛棤鎶曞叆鏁版嵁" />
+ </view>
+ </up-modal>
+ <!-- 鍙傛暟璇︽儏寮圭獥 -->
+ <up-modal :show="paramsVisible"
+ title="鍙傛暟璇︽儏"
+ @confirm="paramsVisible = false">
+ <view class="modal-content">
+ <view v-if="currentParams.length > 0">
+ <view v-for="(param, idx) in currentParams"
+ :key="idx"
+ class="detail-row">
+ <text class="detail-label">{{ param.paramName }}</text>
+ <text class="detail-value">{{ param.inputValue }} {{ param.unit && param.unit !== '/' ? '(' + param.unit + ')' : '' }}</text>
+ </view>
+ </view>
+ <up-empty v-else
+ mode="data"
+ text="鏆傛棤鍙傛暟鏁版嵁" />
+ </view>
+ </up-modal>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, onMounted } from "vue";
+ import dayjs from "dayjs";
+ import {
+ productionProductMainListPage,
+ productionReportDelete,
+ productionProductInputListPage,
+ } from "@/api/productionManagement/productionProductMain.js";
+ import PageHeader from "@/components/PageHeader.vue";
+ import modal from "@/plugins/modal";
+
+ const tableData = ref([]);
+ const loading = ref(false);
+ const loadStatus = ref("loadmore");
+
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+
+ const searchForm = reactive({
+ keyword: "",
+ });
+
+ // 鎶曞叆璇︽儏鐩稿叧
+ const inputVisible = ref(false);
+ const inputList = ref([]);
+
+ // 鍙傛暟璇︽儏鐩稿叧
+ const paramsVisible = ref(false);
+ const currentParams = ref([]);
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const formatDate = date => {
+ return date ? dayjs(date).format("YYYY-MM-DD HH:mm") : "-";
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
+ getList();
+ };
+
+ const loadMore = () => {
+ if (loadStatus.value === "nomore" || loading.value) return;
+ page.current++;
+ getList();
+ };
+
+ const getList = () => {
+ loading.value = true;
+ loadStatus.value = "loading";
+
+ const params = {
+ current: page.current,
+ size: page.size,
+ workOrderNo: searchForm.keyword,
+ };
+
+ productionProductMainListPage(params)
+ .then(res => {
+ loading.value = false;
+ const records = res.data.records || [];
+ if (page.current === 1) {
+ tableData.value = records;
+ } else {
+ tableData.value = [...tableData.value, ...records];
+ }
+
+ if (records.length < page.size) {
+ loadStatus.value = "nomore";
+ } else {
+ loadStatus.value = "loadmore";
+ }
+ page.total = res.data.total || 0;
+ })
+ .catch(() => {
+ loading.value = false;
+ loadStatus.value = "loadmore";
+ modal.msgError("鍔犺浇澶辫触");
+ });
+ };
+
+ const handleShowInput = item => {
+ modal.loading("鍔犺浇涓�...");
+ productionProductInputListPage({
+ productMainId: item.id,
+ current: 1,
+ size: 100,
+ })
+ .then(res => {
+ modal.closeLoading();
+ inputList.value = res.data.records || [];
+ inputVisible.value = true;
+ })
+ .catch(() => {
+ modal.closeLoading();
+ modal.msgError("鍔犺浇鎶曞叆鏁版嵁澶辫触");
+ });
+ };
+
+ const handleShowParams = item => {
+ currentParams.value = item.productionOperationParamList || [];
+ paramsVisible.value = true;
+ };
+
+ const handleDelete = item => {
+ uni.showModal({
+ title: "鎻愮ず",
+ content: "纭畾瑕佸垹闄よ鎶ュ伐璁板綍鍚楋紵",
+ success: res => {
+ if (res.confirm) {
+ productionReportDelete({ id: item.id }).then(res => {
+ if (res.code === 200) {
+ modal.msgSuccess("鍒犻櫎鎴愬姛");
+ handleQuery();
+ } else {
+ modal.msgError(res.msg || "鍒犻櫎澶辫触");
+ }
+ });
+ }
+ },
+ });
+ };
+
+ onMounted(() => {
+ getList();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/procurement-common.scss";
+
+ .reporting-ledger {
+ min-height: 100vh;
+ background-color: #f8f9fa;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .list-container {
+ flex: 1;
+ height: 0;
+ }
+
+ .ledger-item {
+ .item-header {
+ .item-tag {
+ .create-time {
+ font-size: 24rpx;
+ color: #999;
+ }
+ }
+ }
+
+ .item-details {
+ .quantity-section {
+ display: flex;
+ background-color: #f9f9f9;
+ border-radius: 8rpx;
+ padding: 20rpx;
+ margin: 20rpx 0;
+
+ .qty-item {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ &:first-child {
+ border-right: 1rpx solid #eee;
+ }
+
+ .qty-label {
+ font-size: 24rpx;
+ color: #999;
+ margin-bottom: 8rpx;
+ }
+
+ .qty-value {
+ font-size: 32rpx;
+ font-weight: bold;
+
+ &.success {
+ color: #52c41a;
+ }
+
+ &.error {
+ color: #f56c6c;
+ }
+ }
+ }
+ }
+
+ .item-footer {
+ padding-top: 20rpx;
+ border-top: 1rpx solid #f0f0f0;
+
+ .action-buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 20rpx;
+ }
+ }
+ }
+ }
+
+ .modal-content {
+ width: 100%;
+ padding: 20rpx 0;
+ max-height: 60vh;
+ overflow-y: auto;
+
+ .detail-item {
+ padding-bottom: 20rpx;
+ }
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 16rpx;
+
+ .detail-label {
+ font-size: 26rpx;
+ color: #999;
+ }
+
+ .detail-value {
+ font-size: 26rpx;
+ color: #333;
+
+ &.font-bold {
+ font-weight: bold;
+ }
+
+ &.highlight {
+ color: #3c9cff;
+ }
+ }
+ }
+ }
+
+ .empty-state {
+ padding-top: 200rpx;
+ }
+</style>
diff --git a/src/pages/productionManagement/productionScheduling/index.vue b/src/pages/productionManagement/productionScheduling/index.vue
new file mode 100644
index 0000000..5cc2c0c
--- /dev/null
+++ b/src/pages/productionManagement/productionScheduling/index.vue
@@ -0,0 +1,241 @@
+<template>
+ <view class="production-scheduling">
+ <!-- 閫氱敤椤甸潰澶撮儴 -->
+ <PageHeader title="鐢熶骇鎺掍骇"
+ @back="goBack" />
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ュ伐鍗曠紪鍙�"
+ v-model="searchForm.workOrderNo"
+ @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, index) in tableData"
+ :key="item.id || index"
+ 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.workOrderNo }}</text>
+ </view>
+ <view class="item-right">
+ <up-tag :text="item.workOrderType"
+ size="mini"
+ type="primary"
+ plain></up-tag>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">鐢熶骇璁㈠崟鍙�</text>
+ <text class="detail-value">{{ item.npsNo || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">浜у搧鍚嶇О</text>
+ <text class="detail-value">{{ item.productName || '-' }}</text>
+ </view>
+ <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.operationName || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">闇�姹傛暟閲�</text>
+ <text class="detail-value">{{ item.planQuantity || 0 }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀹屾垚鏁伴噺</text>
+ <text class="detail-value">{{ item.completeQuantity || 0 }}</text>
+ </view>
+ <view class="progress-section">
+ <text class="detail-label">瀹屾垚杩涘害</text>
+ <view class="progress-bar">
+ <up-line-progress :percentage="toProgressPercentage(item.completionStatus)"
+ :activeColor="progressColor(item.completionStatus)"
+ :showText="true"></up-line-progress>
+ </view>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">璁″垝寮�濮�</text>
+ <text class="detail-value">{{ item.planStartTime || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">璁″垝缁撴潫</text>
+ <text class="detail-value">{{ item.planEndTime || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀹為檯寮�濮�</text>
+ <text class="detail-value">{{ item.actualStartTime || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瀹為檯缁撴潫</text>
+ <text class="detail-value">{{ item.actualEndTime || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎸囧畾鎶ュ伐浜�</text>
+ <view class="detail-value tags-box">
+ <template v-if="item.userNames">
+ <up-tag v-for="(name, idx) in item.userNames.split(',')"
+ :key="idx"
+ :text="name"
+ size="mini"
+ type="info"
+ plain
+ class="user-tag"></up-tag>
+ </template>
+ <text v-else>-</text>
+ </view>
+ </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, toRefs, getCurrentInstance } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
+ import PageHeader from "@/components/PageHeader.vue";
+
+ const { proxy } = getCurrentInstance();
+
+ const loading = ref(false);
+ const tableData = ref([]);
+ const loadStatus = ref("loadmore");
+
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+
+ const data = reactive({
+ searchForm: {
+ workOrderNo: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ tableData.value = [];
+ getList();
+ };
+
+ const getList = () => {
+ if (loading.value) return;
+ loading.value = true;
+
+ const params = {
+ ...searchForm.value,
+ ...page,
+ };
+
+ productWorkOrderPage(params)
+ .then(res => {
+ loading.value = false;
+ const records = res.data.records || [];
+ tableData.value =
+ page.current === 1 ? records : [...tableData.value, ...records];
+ page.total = res.data.total;
+
+ if (tableData.value.length >= page.total) {
+ loadStatus.value = "nomore";
+ } else {
+ loadStatus.value = "loadmore";
+ }
+ })
+ .catch(() => {
+ loading.value = false;
+ uni.showToast({ title: "鍔犺浇澶辫触", icon: "error" });
+ });
+ };
+
+ const loadMore = () => {
+ if (loadStatus.value === "nomore" || loading.value) return;
+ page.current++;
+ getList();
+ };
+
+ const toProgressPercentage = val => {
+ const n = Number(val);
+ if (!Number.isFinite(n)) return 0;
+ if (n <= 0) return 0;
+ if (n >= 100) return 100;
+ return Math.round(n);
+ };
+
+ const progressColor = percentage => {
+ const p = toProgressPercentage(percentage);
+ if (p < 30) return "#f56c6c";
+ if (p < 50) return "#e6a23c";
+ if (p < 80) return "#409eff";
+ return "#67c23a";
+ };
+
+ onShow(() => {
+ handleQuery();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/sales-common.scss";
+
+ .production-scheduling {
+ padding-bottom: 20rpx;
+ }
+ .progress-bar {
+ margin-top: 20rpx;
+ margin-bottom: 20rpx;
+ }
+
+ .tags-box {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8rpx;
+ justify-content: flex-end;
+ }
+
+ .user-tag {
+ margin-bottom: 4rpx;
+ }
+</style>
diff --git a/src/pages/productionManagement/productionTraceability/index.vue b/src/pages/productionManagement/productionTraceability/index.vue
new file mode 100644
index 0000000..d187543
--- /dev/null
+++ b/src/pages/productionManagement/productionTraceability/index.vue
@@ -0,0 +1,1032 @@
+<template>
+ <view class="production-traceability">
+ <PageHeader title="鐢熶骇杩芥函"
+ @back="goBack" />
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="search-bar"
+ @click="openNpsNoSelector">
+ <view class="search-input">
+ <text v-if="!selectedNpsNo"
+ class="placeholder">璇烽�夋嫨鐢熶骇璁㈠崟鍙�</text>
+ <text v-else
+ class="value">{{ selectedNpsNoLabel }}</text>
+ </view>
+ <view class="search-button">
+ <up-icon name="arrow-down"
+ size="20"
+ color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <!-- 鍐呭鍖哄煙 -->
+ <view class="content-container"
+ v-if="rowData.productionOrderDto">
+ <!-- 鍩虹淇℃伅 -->
+ <view class="info-card">
+ <view class="card-title">鍩虹淇℃伅</view>
+ <view class="base-info">
+ <view class="info-row">
+ <text class="label">鐢熶骇璁㈠崟鍙凤細</text>
+ <text class="value">{{ rowData.productionOrderDto?.npsNo || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">浜у搧鍚嶇О锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.productName || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">瑙勬牸鍨嬪彿锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.model || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">璁″垝鏁伴噺锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.quantity || 0 }} {{ rowData.productionOrderDto?.unit }}</text>
+ </view>
+ <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-row">
+ <text class="label">瀹㈡埛鍚嶇О锛�</text>
+ <text class="value">{{ rowData.productionOrderDto?.customerName || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">寮�濮嬫棩鏈燂細</text>
+ <text class="value">{{ formatDate(rowData.productionOrderDto?.startTime) }}</text>
+ </view>
+ <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>
+ </view>
+ <!-- 宸ュ崟淇℃伅 -->
+ <view class="work-order-section"
+ v-if="rowData.productionRecords && rowData.productionRecords.length > 0">
+ <view class="section-title">宸ュ崟淇℃伅</view>
+ <view v-for="(item, index) in rowData.productionRecords"
+ :key="index"
+ class="work-order-card">
+ <view class="card-header">
+ <text class="work-order-no">{{ item.workOrder.workOrderNo }}</text>
+ <text class="progress-tag"
+ :style="{ color: progressColor(item.workOrder.completionStatus) }">{{ item.workOrder.completionStatus || 0 }}%</text>
+ </view>
+ <view class="card-content">
+ <view class="content-row">
+ <text class="label">浜у搧/瑙勬牸锛�</text>
+ <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">
+ <up-button type="primary"
+ size="small"
+ plain
+ text="鎶ュ伐璁板綍"
+ @click="handleShowReports(item)"></up-button>
+ <up-button type="success"
+ size="small"
+ plain
+ text="璐ㄦ淇℃伅"
+ @click="handleShowQuality(item)"></up-button>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data-minor">
+ <up-empty mode="data"
+ text="鏆傛棤宸ュ崟淇℃伅"
+ icon-size="40"></up-empty>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <up-empty mode="search"
+ text="璇烽�夋嫨鐢熶骇璁㈠崟鍙锋煡鐪嬭拷婧俊鎭�"></up-empty>
+ </view>
+ <!-- 鐢熶骇璁㈠崟鍙烽�夋嫨寮圭獥 -->
+ <up-popup :show="showNpsNoSelector"
+ mode="bottom"
+ @close="showNpsNoSelector = false"
+ round="10">
+ <view class="selector-popup">
+ <view class="popup-header">
+ <text class="popup-title">閫夋嫨鐢熶骇璁㈠崟鍙�</text>
+ <up-icon name="close"
+ size="20"
+ @click="showNpsNoSelector = false"></up-icon>
+ </view>
+ <view class="search-box">
+ <up-search placeholder="杈撳叆鍏抽敭瀛楁悳绱�"
+ v-model="npsNoQuery"
+ :show-action="false"
+ @change="handleNpsNoSearch"
+ @search="handleNpsNoSearch"
+ :loading="npsNoLoading"></up-search>
+ </view>
+ <scroll-view scroll-y
+ class="options-list">
+ <view v-for="item in npsNoOptions"
+ :key="item.id"
+ class="option-item"
+ @click="onSelectNpsNo(item)">
+ <view class="option-main">
+ <text class="nps-no">{{ item.npsNo }}</text>
+ <text class="product-info">{{ item.productName }} / {{ item.model }}</text>
+ </view>
+ <up-icon v-if="selectedNpsNo === item.id"
+ name="checkbox-mark"
+ color="#3c9cff"
+ size="20"></up-icon>
+ </view>
+ <view v-if="npsNoOptions.length === 0"
+ class="no-options">
+ <text>{{ npsNoLoading ? '鍔犺浇涓�...' : '鏆傛棤閫夐」' }}</text>
+ </view>
+ </scroll-view>
+ </view>
+ </up-popup>
+ <!-- 鎶ュ伐璇︽儏寮圭獥 -->
+ <up-popup :show="reportPopupVisible"
+ mode="bottom"
+ @close="reportPopupVisible = false"
+ round="10">
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="popup-title">鐢熶骇鎶ュ伐璇︽儏</text>
+ <up-icon name="close"
+ size="20"
+ @click="reportPopupVisible = false"></up-icon>
+ </view>
+ <scroll-view scroll-y
+ class="popup-scroll">
+ <view class="detail-info">
+ <view class="info-row"><text class="label">宸ュ崟鍙凤細</text><text class="value">{{ detailData.workOrder.workOrderNo }}</text></view>
+ <view class="info-row"><text class="label">璁″垝/瀹屾垚锛�</text><text class="value">{{ detailData.workOrder.planQuantity }} / {{ detailData.workOrder.completeQuantity }}</text></view>
+ <view class="info-row"><text class="label">瀹為檯鏃堕棿锛�</text><text class="value">{{ formatDate(detailData.workOrder.actualStartTime) }} 鑷� {{ formatDate(detailData.workOrder.actualEndTime) }}</text></view>
+ </view>
+ <view class="list-title">鎶ュ伐鏄庣粏</view>
+ <view v-for="(report, idx) in detailData.reports"
+ :key="idx"
+ 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>
+ <view class="item-actions">
+ <text class="action-link"
+ @click="showParams(report.productionOperationParamList)">鍙傛暟璇︽儏</text>
+ <text class="action-link green"
+ @click="handleShowInput(report.id)">鎶曞叆璇︽儏</text>
+ </view>
+ </view>
+ <view v-if="!detailData.reports || detailData.reports.length === 0"
+ class="no-data-minor">鏆傛棤鎶ュ伐鏄庣粏</view>
+ </scroll-view>
+ </view>
+ </up-popup>
+ <!-- 鎶曞叆璇︽儏寮圭獥 -->
+ <up-popup :show="inputPopupVisible"
+ mode="bottom"
+ @close="inputPopupVisible = false"
+ round="10">
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="popup-title">鎶曞叆淇℃伅璇︽儏</text>
+ <up-icon name="close"
+ size="20"
+ @click="inputPopupVisible = false"></up-icon>
+ </view>
+ <scroll-view scroll-y
+ class="popup-scroll">
+ <view class="input-list-popup">
+ <view v-for="(item, idx) in inputListData"
+ :key="idx"
+ 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>
+ </view>
+ </scroll-view>
+ </view>
+ </up-popup>
+ <!-- 璐ㄦ璇︽儏寮圭獥 -->
+ <up-popup :show="qualityPopupVisible"
+ mode="bottom"
+ @close="qualityPopupVisible = false"
+ round="10">
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="popup-title">璐ㄦ璇︽儏</text>
+ <up-icon name="close"
+ size="20"
+ @click="qualityPopupVisible = false"></up-icon>
+ </view>
+ <scroll-view scroll-y
+ class="popup-scroll">
+ <view v-for="(record, idx) in qualityRecords"
+ :key="idx"
+ 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">{{ formatDate(record.createTime) }}</text></view>
+ <view class="info-item"><text class="label">妫�娴嬬粨鏋�</text><up-tag style="width:100rpx"
+ :text="record.checkResult || '寰呮娴�'"
+ :type="record.checkResult === '鍚堟牸' ? 'success' : 'error'"
+ 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>
+ </view>
+ </view>
+ <view v-if="!qualityRecords || qualityRecords.length === 0"
+ class="no-data-minor">鏆傛棤璐ㄦ璁板綍</view>
+ </scroll-view>
+ </view>
+ </up-popup>
+ <!-- 鍙傛暟璇︽儏寮圭獥 -->
+ <up-modal :show="paramModalVisible"
+ title="鍙傛暟璇︽儏"
+ @confirm="paramModalVisible = false">
+ <view class="modal-content">
+ <view v-for="(param, idx) in currentParams"
+ :key="idx"
+ class="param-row">
+ <text class="label">{{ param.paramName }}锛�</text>
+ <text class="value">{{ param.inputValue }} {{ param.unit && param.unit !== '/' ? param.unit : '' }}</text>
+ </view>
+ <view v-if="!currentParams || currentParams.length === 0"
+ class="no-data-minor">鏆傛棤鍙傛暟鏁版嵁</view>
+ </view>
+ </up-modal>
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, computed } from "vue";
+ import { onLoad } from "@dcloudio/uni-app";
+ import {
+ getOrderDetail,
+ productOrderListPage,
+ } from "@/api/productionManagement/productionOrder";
+ import { productionProductInputListPage } from "@/api/productionManagement/productionProductMain";
+ import PageHeader from "@/components/PageHeader.vue";
+ import { parseTime } from "@/utils/ruoyi";
+
+ // 閫夋嫨鍣ㄧ浉鍏�
+ const showNpsNoSelector = ref(false);
+ const npsNoQuery = ref("");
+ const npsNoOptions = ref([]);
+ const npsNoLoading = ref(false);
+ const selectedNpsNo = ref(null);
+ const selectedNpsNoLabel = ref("");
+
+ const rowData = reactive({
+ productionOrderDto: null,
+ productionRecords: [],
+ });
+
+ // 鎶ュ伐璇︽儏
+ const reportPopupVisible = ref(false);
+ const detailData = ref({ workOrder: {}, reports: [] });
+
+ // 鎶曞叆璇︽儏
+ const inputPopupVisible = ref(false);
+ const inputListData = ref([]);
+ const inputLoading = ref(false);
+
+ // 璐ㄦ璇︽儏
+ const qualityPopupVisible = ref(false);
+ const qualityRecords = ref([]);
+
+ // 鍙傛暟璇︽儏
+ const paramModalVisible = ref(false);
+ const currentParams = ref([]);
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const openNpsNoSelector = () => {
+ showNpsNoSelector.value = true;
+ if (npsNoOptions.value.length === 0) {
+ handleNpsNoSearch();
+ }
+ };
+
+ const handleNpsNoSearch = async () => {
+ npsNoLoading.value = true;
+ try {
+ const res = await productOrderListPage({
+ npsNo: npsNoQuery.value || "",
+ pageNum: 1,
+ pageSize: 50,
+ });
+ npsNoOptions.value = res.data?.records || res.rows || [];
+ } catch (error) {
+ console.error(error);
+ } finally {
+ npsNoLoading.value = false;
+ }
+ };
+
+ const onSelectNpsNo = async item => {
+ selectedNpsNo.value = item.id;
+ selectedNpsNoLabel.value = item.npsNo;
+ showNpsNoSelector.value = false;
+
+ uni.showLoading({ title: "鍔犺浇涓�..." });
+ try {
+ const res = await getOrderDetail(item.npsNo);
+ if (res.code === 200 && res.data) {
+ const { productionOrder, workOrderList } = res.data;
+ rowData.productionOrderDto = productionOrder || item;
+ rowData.productionRecords = workOrderList || [];
+ } else {
+ rowData.productionOrderDto = item;
+ rowData.productionRecords = [];
+ }
+ } catch (error) {
+ console.error(error);
+ rowData.productionOrderDto = item;
+ rowData.productionRecords = [];
+ uni.showToast({ title: "鑾峰彇璇︽儏澶辫触", icon: "none" });
+ } finally {
+ uni.hideLoading();
+ }
+ };
+
+ onLoad(async options => {
+ if (options.npsNo) {
+ uni.showLoading({ title: "鍔犺浇涓�..." });
+ try {
+ const res = await productOrderListPage({
+ npsNo: options.npsNo,
+ pageNum: 1,
+ pageSize: 10,
+ });
+ const records = res.data?.records || res.rows || [];
+ if (records.length > 0) {
+ onSelectNpsNo(records[0]);
+ } else {
+ uni.showToast({ title: "鏈壘鍒扮浉鍏宠鍗�", icon: "none" });
+ }
+ } catch (error) {
+ console.error(error);
+ } finally {
+ uni.hideLoading();
+ }
+ }
+ });
+
+ const getStatusText = status => {
+ const statusMap = { 1: "寰呭紑濮�", 2: "杩涜涓�", 3: "宸插畬鎴�", 5: "宸茬粨鏉�" };
+ return statusMap[status] || "宸插彇娑�";
+ };
+
+ const getStatusType = status => {
+ const typeMap = { 1: "primary", 2: "warning", 3: "success", 5: "error" };
+ return typeMap[status] || "info";
+ };
+
+ const formatDate = (date, pattern = "{y}-{m}-{d}") => {
+ return parseTime(date, pattern) || "-";
+ };
+
+ const formatProgress = val => {
+ const p = parseFloat(val || 0);
+ return p >= 100 ? 100 : p;
+ };
+
+ const progressColor = percentage => {
+ if (percentage < 30) return "#f56c6c";
+ if (percentage < 70) return "#e6a23c";
+ return "#67c23a";
+ };
+
+ const handleShowReports = row => {
+ detailData.value = {
+ workOrder: row.workOrder || {},
+ reports: (row.reportList || []).map(r => ({
+ ...r.reportMain,
+ ...(r.reportOutputList ? r.reportOutputList[0] : {}),
+ id: r.reportMain.id,
+ productionOperationParamList: r.reportParamList || [],
+ })),
+ };
+ reportPopupVisible.value = true;
+ };
+
+ const handleShowInput = async reportId => {
+ inputPopupVisible.value = true;
+ inputLoading.value = true;
+ inputListData.value = [];
+ try {
+ const res = await productionProductInputListPage({
+ productMainId: reportId,
+ pageNum: 1,
+ pageSize: 100,
+ });
+ inputListData.value = res.data?.records || res.rows || [];
+ } catch (error) {
+ console.error(error);
+ uni.showToast({ title: "鑾峰彇鎶曞叆淇℃伅澶辫触", icon: "none" });
+ } finally {
+ inputLoading.value = false;
+ }
+ };
+
+ const handleShowQuality = row => {
+ const inspects = row.inspectList || [];
+ 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 || [],
+ }));
+ qualityPopupVisible.value = true;
+ };
+
+ const showParams = params => {
+ currentParams.value = params || [];
+ paramModalVisible.value = true;
+ };
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/procurement-common.scss";
+
+ .production-traceability {
+ min-height: 100vh;
+ background-color: #f5f7fa;
+ }
+
+ .search-section {
+ background-color: #fff;
+ padding: 20rpx 24rpx;
+ margin-bottom: 20rpx;
+ }
+
+ .search-bar {
+ display: flex;
+ align-items: center;
+ background-color: #f2f2f2;
+ border-radius: 8rpx;
+ padding: 0 20rpx;
+ height: 80rpx;
+
+ .search-input {
+ flex: 1;
+ display: flex;
+ align-items: center;
+
+ .placeholder {
+ font-size: 28rpx;
+ color: #999;
+ }
+
+ .value {
+ font-size: 28rpx;
+ color: #333;
+ font-weight: 500;
+ }
+ }
+
+ .search-button {
+ padding: 0 10rpx;
+ }
+ }
+
+ .selector-popup {
+ background: #fff;
+ padding: 30rpx;
+ max-height: 70vh;
+ display: flex;
+ flex-direction: column;
+
+ .popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24rpx;
+
+ .popup-title {
+ font-size: 32rpx;
+ font-weight: bold;
+ }
+ }
+
+ .search-box {
+ margin-bottom: 20rpx;
+ }
+
+ .options-list {
+ flex: 1;
+ overflow: hidden;
+ }
+
+ .option-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 24rpx 0;
+ border-bottom: 1rpx solid #f0f0f0;
+
+ .option-main {
+ flex: 1;
+ .nps-no {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ display: block;
+ margin-bottom: 4rpx;
+ }
+ .product-info {
+ font-size: 24rpx;
+ color: #999;
+ }
+ }
+ }
+
+ .no-options {
+ text-align: center;
+ padding: 40rpx;
+ color: #999;
+ font-size: 26rpx;
+ }
+ }
+
+ .content-container {
+ padding: 0 24rpx 40rpx;
+ }
+
+ .info-card {
+ background: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ margin-bottom: 24rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+ .card-title {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #333;
+ margin-bottom: 24rpx;
+ padding-left: 16rpx;
+ border-left: 8rpx solid #3c9cff;
+ }
+ }
+
+ .info-grid {
+ display: flex;
+ flex-wrap: wrap;
+
+ .info-item {
+ width: 50%;
+ margin-bottom: 20rpx;
+ display: flex;
+ flex-direction: column;
+
+ &.full-width {
+ width: 100%;
+ }
+
+ .label {
+ font-size: 24rpx;
+ color: #999;
+ margin-bottom: 8rpx;
+ }
+
+ .value {
+ font-size: 28rpx;
+ color: #333;
+ word-break: break-all;
+ }
+ }
+ }
+
+ .progress-container {
+ display: flex;
+ align-items: center;
+ gap: 20rpx;
+
+ up-line-progress {
+ flex: 1;
+ }
+
+ .progress-text {
+ font-size: 24rpx;
+ color: #666;
+ min-width: 60rpx;
+ }
+ }
+
+ .section-title {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #333;
+ margin: 32rpx 0 20rpx;
+ }
+
+ .work-order-card {
+ background: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20rpx;
+ padding-bottom: 16rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+
+ .work-order-no {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #3c9cff;
+ }
+
+ .progress-tag {
+ font-size: 28rpx;
+ font-weight: bold;
+ }
+ }
+
+ .card-content {
+ .content-row {
+ margin-bottom: 12rpx;
+ font-size: 26rpx;
+
+ .label {
+ color: #999;
+ }
+
+ .value {
+ color: #333;
+ }
+ }
+ }
+
+ .card-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 20rpx;
+ margin-top: 20rpx;
+ }
+ }
+
+ .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;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ border-radius: 20rpx 20rpx 0 0;
+
+ .popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 30rpx;
+
+ .popup-title {
+ font-size: 34rpx;
+ font-weight: bold;
+ color: #333;
+ }
+ }
+
+ .popup-scroll {
+ flex: 1;
+ overflow: hidden;
+ }
+ }
+
+ .detail-info {
+ background: #f8f9fa;
+ padding: 24rpx;
+ border-radius: 16rpx;
+ margin-bottom: 30rpx;
+ flex-direction: column;
+
+ .info-row {
+ margin-bottom: 12rpx;
+ font-size: 28rpx;
+ display: flex;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .label {
+ color: #999;
+ min-width: 140rpx;
+ }
+ .value {
+ color: #333;
+ flex: 1;
+ font-weight: 500;
+ }
+ }
+ }
+
+ .list-title {
+ font-size: 30rpx;
+ font-weight: bold;
+ margin-bottom: 20rpx;
+ color: #333;
+ display: flex;
+ align-items: center;
+
+ &::before {
+ content: "";
+ width: 6rpx;
+ height: 28rpx;
+ background: #3c9cff;
+ margin-right: 12rpx;
+ border-radius: 4rpx;
+ }
+ }
+
+ .detail-item {
+ background: #fff;
+ border: 1rpx solid #f0f0f0;
+ border-radius: 12rpx;
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .item-main {
+ flex: 1;
+ .item-row {
+ font-size: 26rpx;
+ margin-bottom: 8rpx;
+ display: flex;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .label {
+ color: #999;
+ min-width: 130rpx;
+ }
+ .value {
+ color: #333;
+ flex: 1;
+ }
+ }
+ }
+
+ .item-actions {
+ display: flex;
+ flex-direction: column;
+ gap: 16rpx;
+ padding-left: 20rpx;
+ border-left: 1rpx solid #f0f0f0;
+
+ .action-link {
+ font-size: 26rpx;
+ color: #3c9cff;
+ white-space: nowrap;
+
+ &.green {
+ color: #52c41a;
+ }
+ }
+ }
+ }
+
+ .quality-record {
+ background: #fff;
+ border: 1rpx solid #f0f0f0;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ margin-bottom: 30rpx;
+
+ .record-title {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #3c9cff;
+ margin-bottom: 24rpx;
+ display: flex;
+ justify-content: space-between;
+ }
+ }
+
+ .params-table {
+ margin-top: 24rpx;
+ border: 1rpx solid #f0f0f0;
+ border-radius: 12rpx;
+ overflow: hidden;
+
+ .table-header {
+ display: flex;
+ background: #f8f9fa;
+ padding: 20rpx 16rpx;
+ font-size: 26rpx;
+ font-weight: bold;
+ color: #666;
+ }
+
+ .table-row {
+ display: flex;
+ padding: 20rpx 16rpx;
+ font-size: 26rpx;
+ border-top: 1rpx solid #f0f0f0;
+ color: #333;
+
+ &:nth-child(even) {
+ background: #fafafa;
+ }
+ }
+
+ .col {
+ flex: 1;
+ text-align: center;
+ word-break: break-all;
+ }
+ }
+
+ .modal-content {
+ padding: 30rpx;
+ .param-row {
+ margin-bottom: 20rpx;
+ font-size: 28rpx;
+ display: flex;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .label {
+ color: #666;
+ min-width: 160rpx;
+ }
+ .value {
+ color: #333;
+ font-weight: 500;
+ flex: 1;
+ }
+ }
+ }
+
+ .input-list-popup {
+ .input-item {
+ background: #fff;
+ border: 1rpx solid #f0f0f0;
+ border-radius: 12rpx;
+ padding: 20rpx;
+ margin-bottom: 20rpx;
+
+ .input-row {
+ display: flex;
+ font-size: 26rpx;
+ margin-bottom: 8rpx;
+ &:last-child {
+ margin-bottom: 0;
+ }
+ .label {
+ color: #999;
+ min-width: 160rpx;
+ }
+ .value {
+ color: #333;
+ flex: 1;
+ }
+ }
+ }
+ }
+
+ .error-text {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+
+ .no-data-minor {
+ text-align: center;
+ padding: 60rpx 40rpx;
+ color: #999;
+ font-size: 28rpx;
+ }
+</style>
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 0eb5bf0..45c3fd6 100644
--- a/src/pages/sales/salesQuotation/detail.vue
+++ b/src/pages/sales/salesQuotation/detail.vue
@@ -1,114 +1,137 @@
<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>
<view class="info-list">
- <view class="info-item"><text class="info-label">鎶ヤ环鍗曞彿</text><text class="info-value">{{ detailData.quotationNo || "-" }}</text></view>
- <view class="info-item"><text class="info-label">瀹㈡埛</text><text class="info-value">{{ detailData.customer || "-" }}</text></view>
- <view class="info-item"><text class="info-label">涓氬姟鍛�</text><text class="info-value">{{ detailData.salesperson || "-" }}</text></view>
- <view class="info-item"><text class="info-label">鎶ヤ环鏃ユ湡</text><text class="info-value">{{ detailData.quotationDate || "-" }}</text></view>
- <view class="info-item"><text class="info-label">鏈夋晥鏈熻嚦</text><text class="info-value">{{ detailData.validDate || "-" }}</text></view>
- <view class="info-item"><text class="info-label">浠樻鏂瑰紡</text><text class="info-value">{{ detailData.paymentMethod || "-" }}</text></view>
- <view class="info-item"><text class="info-label">鎬婚</text><text class="info-value highlight">{{ formatAmount(totalAmount) }}</text></view>
- <view class="info-item"><text class="info-label">澶囨敞</text><text class="info-value">{{ detailData.remark || "-" }}</text></view>
- </view>
- </view>
-
- <view class="section">
- <view class="section-title">浜у搧鏄庣粏</view>
- <view v-if="products.length" class="product-list">
- <view v-for="(item, index) in products" :key="index" class="product-card">
- <view class="product-head">浜у搧 {{ index + 1 }}</view>
- <view class="info-item"><text class="info-label">浜у搧</text><text class="info-value">{{ item.product || item.productName || "-" }}</text></view>
- <view class="info-item"><text class="info-label">瑙勬牸</text><text class="info-value">{{ item.specification || "-" }}</text></view>
- <view class="info-item"><text class="info-label">鍗曚綅</text><text class="info-value">{{ item.unit || "-" }}</text></view>
- <view class="info-item"><text class="info-label">绾稿紶</text><text class="info-value">{{ item.paper || "-" }}</text></view>
- <view class="info-item"><text class="info-label">瀹氶噺</text><text class="info-value">{{ item.paperWeight || "-" }}</text></view>
- <view class="info-item"><text class="info-label">鏁伴噺</text><text class="info-value">{{ Number(item.quantity || 0) }}</text></view>
- <view class="info-item"><text class="info-label">鍗曚环</text><text class="info-value">{{ formatAmount(item.unitPrice) }}</text></view>
- <view class="info-item"><text class="info-label">鍗扮増璐�</text><text class="info-value">{{ formatAmount(item.printingFee) }}</text></view>
- <view class="info-item"><text class="info-label">鍒�鐗堣垂</text><text class="info-value">{{ formatAmount(item.dieCuttingFee) }}</text></view>
- <view class="info-item"><text class="info-label">纾ㄥ叿璐�</text><text class="info-value">{{ formatAmount(item.grindingFee) }}</text></view>
- <view class="info-item"><text class="info-label">閲戦</text><text class="info-value highlight">{{ formatAmount(item.amount) }}</text></view>
+ <view class="info-item">
+ <text class="info-label">鎶ヤ环鍗曞彿</text>
+ <text class="info-value">{{ detailData.quotationNo || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">瀹㈡埛鍚嶇О</text>
+ <text class="info-value">{{ detailData.customer || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">涓氬姟鍛�</text>
+ <text class="info-value">{{ detailData.salesperson || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鎶ヤ环鏃ユ湡</text>
+ <text class="info-value">{{ detailData.quotationDate || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鏈夋晥鏈熻嚦</text>
+ <text class="info-value">{{ detailData.validDate || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">浠樻鏂瑰紡</text>
+ <text class="info-value">{{ detailData.paymentMethod || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">瀹℃壒鐘舵��</text>
+ <text class="info-value">{{ detailData.status || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鎶ヤ环鎬婚</text>
+ <text class="info-value highlight">{{ formatAmount(detailData.totalAmount) }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">澶囨敞</text>
+ <text class="info-value">{{ detailData.remark || "-" }}</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 class="product-head">浜у搧 {{ index + 1 }}</view>
+ <view class="info-item">
+ <text class="info-label">浜у搧鍚嶇О</text>
+ <text class="info-value">{{ item.product || item.productName || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">瑙勬牸鍨嬪彿</text>
+ <text class="info-value">{{ item.specification || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鍗曚綅</text>
+ <text class="info-value">{{ item.unit || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鏁伴噺</text>
+ <text class="info-value">{{ item.quantity || "-" }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鍗曚环</text>
+ <text class="info-value">{{ formatAmount(item.unitPrice) }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">閲戦</text>
+ <text class="info-value highlight">{{ formatAmount(item.amount) }}</text>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="empty-box">
+ <text>鏆傛棤浜у搧鏄庣粏</text>
+ </view>
</view>
</view>
-
- <view class="detail-footer">
- <up-button type="primary" @click="goBack">杩斿洖</up-button>
- </view>
+ <FooterButtons cancelText="杩斿洖"
+ confirmText="缂栬緫"
+ @cancel="goBack"
+ @confirm="goEdit" />
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
+ import FooterButtons from "@/components/FooterButtons.vue";
import PageHeader from "@/components/PageHeader.vue";
- import { getQuotationDetail } from "@/api/salesManagement/salesQuotation";
const quotationId = ref("");
const detailData = ref({});
- const products = computed(() => {
- const rows = detailData.value?.products;
- if (Array.isArray(rows) && rows.length) return rows;
- if (detailData.value?.product || detailData.value?.productName) return [detailData.value];
- return [];
- });
+ const goBack = () => {
+ uni.navigateBack();
+ };
- const totalAmount = computed(() => {
- const backendTotal = Number(detailData.value?.totalAmount || 0);
- if (backendTotal > 0) return backendTotal;
- return Number(
- products.value
- .reduce((sum, item) => {
- const unitPrice = Number(item?.unitPrice || 0);
- const printingFee = Number(item?.printingFee || 0);
- const dieCuttingFee = Number(item?.dieCuttingFee || 0);
- const grindingFee = Number(item?.grindingFee || 0);
- return sum + unitPrice + printingFee + dieCuttingFee + grindingFee;
- }, 0)
- .toFixed(2)
- );
- });
+ const goEdit = () => {
+ if (!quotationId.value) return;
+ uni.navigateTo({
+ url: `/pages/sales/salesQuotation/edit?id=${quotationId.value}`,
+ });
+ };
- const goBack = () => uni.navigateBack();
const formatAmount = amount => `楼${Number(amount || 0).toFixed(2)}`;
const loadDetailFromStorage = () => {
const cachedData = uni.getStorageSync("salesQuotationDetail");
- if (cachedData && typeof cachedData === "object") detailData.value = cachedData;
- };
-
- const loadDetailFromApi = () => {
- if (!quotationId.value) return Promise.resolve();
- uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
- return getQuotationDetail({ id: quotationId.value })
- .then(res => {
- detailData.value = res?.data || detailData.value || {};
- })
- .catch(() => {
- uni.showToast({ title: "鍔犺浇璇︽儏澶辫触", icon: "error" });
- })
- .finally(() => {
- uni.hideLoading();
- });
+ if (cachedData && (cachedData.id === quotationId.value || cachedData.id === Number(quotationId.value))) {
+ detailData.value = cachedData;
+ } else {
+ detailData.value = cachedData || {};
+ console.warn("鏈壘鍒板搴旂殑鎶ヤ环鍗曠紦瀛樻暟鎹�");
+ }
};
onLoad(options => {
- if (options?.id) quotationId.value = options.id;
+ if (options?.id) {
+ quotationId.value = options.id;
+ }
loadDetailFromStorage();
- loadDetailFromApi();
});
onShow(() => {
loadDetailFromStorage();
- loadDetailFromApi();
});
</script>
@@ -198,16 +221,5 @@
font-weight: 600;
color: #22324d;
border-bottom: 1px solid #eef2f7;
- }
-
- .detail-footer {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
- background: #fff;
- box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
- z-index: 10;
}
</style>
diff --git a/src/pages/sales/salesQuotation/edit.vue b/src/pages/sales/salesQuotation/edit.vue
index 80561e5..bfcb930 100644
--- a/src/pages/sales/salesQuotation/edit.vue
+++ b/src/pages/sales/salesQuotation/edit.vue
@@ -1,124 +1,301 @@
<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" label-width="110" input-align="right" error-message-align="right">
- <u-cell-group title="浜у搧淇℃伅" class="form-section">
+ <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>
+ </template>
+ </up-form-item>
+ <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>
+ </template>
+ </up-form-item>
+ <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>
+ </template>
+ </up-form-item>
+ <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>
+ </template>
+ </up-form-item>
+ <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>
+ </u-cell-group>
+ <u-cell-group title="浜у搧淇℃伅"
+ class="form-section">
<view class="section-tools">
- <up-button type="primary" size="small" text="鏂板浜у搧" :disabled="isEditMode" @click="addProduct" />
+ <up-button type="primary"
+ size="small"
+ text="鏂板浜у搧"
+ @click="addProduct" />
</view>
- <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 || index" class="product-card">
+ <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 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)" />
- <template #right><up-icon name="arrow-right" @click="openProductPicker(index)"></up-icon></template>
+ <up-form-item label="浜у搧鍚嶇О">
+ <up-input v-model="product.product"
+ placeholder="璇烽�夋嫨浜у搧"
+ readonly
+ @click="openProductPicker(index)" />
+ <template #right>
+ <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)" />
- <template #right><up-icon name="arrow-right" @click="openModelPicker(index)"></up-icon></template>
+ <up-form-item label="瑙勬牸鍨嬪彿">
+ <up-input v-model="product.ProductModel"
+ placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
+ readonly
+ @click="openModelPicker(index)" />
+ <template #right>
+ <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-form-item>
- <up-form-item label="绾稿紶"><up-input v-model="product.paper" placeholder="璇疯緭鍏ョ焊寮�" clearable /></up-form-item>
- <up-form-item label="瀹氶噺"><up-input v-model="product.paperWeight" placeholder="璇疯緭鍏ュ畾閲�" clearable /></up-form-item>
+ <up-form-item label="鍗曚綅">
+ <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-form-item>
- <up-form-item label="鍗扮増璐�">
- <up-input v-model="product.printingFee" type="number" placeholder="璇疯緭鍏ュ嵃鐗堣垂" clearable @blur="syncTotalAmount" />
- </up-form-item>
- <up-form-item label="鍒�鐗堣垂">
- <up-input v-model="product.dieCuttingFee" type="number" placeholder="璇疯緭鍏ュ垁鐗堣垂" clearable @blur="syncTotalAmount" />
- </up-form-item>
- <up-form-item label="纾ㄥ叿璐�">
- <up-input v-model="product.grindingFee" type="number" placeholder="璇疯緭鍏ョ(鍏疯垂" clearable @blur="syncTotalAmount" />
+ <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">
- <up-form-item label="澶囨敞">
- <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">
<up-form-item label="鎶ヤ环鎬婚">
- <up-input :model-value="formatAmount(totalAmount)" disabled placeholder="鑷姩姹囨��" />
+ <up-input :model-value="formatAmount(totalAmount)"
+ disabled
+ placeholder="鑷姩姹囨��" />
</up-form-item>
- <view class="summary-tip">鎬婚瑙勫垯锛氬崟浠� + 鍗扮増璐� + 鍒�鐗堣垂 + 纾ㄥ叿璐癸紙鎸変骇鍝侀�愯姹傚拰锛�</view>
</u-cell-group>
</up-form>
</view>
-
- <FooterButtons :loading="loading" confirmText="淇濆瓨" @cancel="goBack" @confirm="handleSubmit" />
-
- <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" />
+ <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>
<script setup>
- import { computed, onMounted, ref } from "vue";
+ import { computed, onMounted, onUnmounted, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import FooterButtons from "@/components/FooterButtons.vue";
import PageHeader from "@/components/PageHeader.vue";
+ import { formatDateToYMD } from "@/utils/ruoyi";
import { modelList, productTreeList } from "@/api/basicData/product";
- import { addOrUpdateQuotationProduct, editQuotationProduct } from "@/api/salesManagement/salesQuotationProduct";
+ import { userListNoPageByTenantId } from "@/api/system/user";
+ import {
+ addQuotation,
+ getCustomerList,
+ updateQuotation,
+ } from "@/api/salesManagement/salesQuotation";
const formRef = ref();
const loading = ref(false);
const quotationId = ref("");
+ const showCustomerSheet = ref(false);
+ const showSalespersonSheet = ref(false);
const showProductSheet = ref(false);
const showModelSheet = ref(false);
+ const showQuotationDatePicker = ref(false);
+ const showValidDatePicker = ref(false);
+ const quotationDateValue = ref(Date.now());
+ const validDateValue = ref(Date.now());
const currentProductIndex = ref(-1);
+ const customerList = ref([]);
+ const salespersonList = ref([]);
const productList = ref([]);
const modelActions = ref([]);
let uidSeed = 1;
+
const form = ref({
id: undefined,
+ quotationNo: "",
+ customerId: undefined,
+ customer: "",
+ salesperson: "",
+ quotationDate: "",
+ validDate: "",
+ paymentMethod: "",
+ status: "鑽夌",
remark: "",
products: [],
+ subtotal: 0,
+ freight: 0,
+ otherFee: 0,
+ discountRate: 0,
+ discountAmount: 0,
totalAmount: 0,
});
+ const rules = {
+ customer: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
+ salesperson: [{ required: true, message: "璇烽�夋嫨涓氬姟鍛�", trigger: "change" }],
+ quotationDate: [
+ { required: true, message: "璇烽�夋嫨鎶ヤ环鏃ユ湡", trigger: "change" },
+ ],
+ validDate: [{ required: true, message: "璇烽�夋嫨鏈夋晥鏈�", trigger: "change" }],
+ paymentMethod: [
+ { required: true, message: "璇疯緭鍏ヤ粯娆炬柟寮�", trigger: "blur" },
+ ],
+ };
+
const pageTitle = computed(() => (quotationId.value ? "缂栬緫鎶ヤ环" : "鏂板鎶ヤ环"));
- const isEditMode = computed(() => Boolean(quotationId.value));
- const productActions = computed(() => productList.value.map(item => ({ name: item.label, value: item.value, label: item.label })));
- const totalAmount = computed(() => calcTotalAmountFromProducts(form.value.products));
+ const totalAmount = computed(() =>
+ 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.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++}`,
- id: "",
- salesQuotationId: "",
productId: "",
product: "",
- specificationId: "",
- specification: "",
+ productModelId: "",
+ ProductModel: "",
unit: "",
- paper: "",
- paperWeight: "",
quantity: 1,
unitPrice: 0,
- printingFee: 0,
- dieCuttingFee: 0,
- grindingFee: 0,
amount: 0,
modelOptions: [],
});
@@ -127,68 +304,51 @@
const result = [];
const walk = list => {
(list || []).forEach(item => {
- if (item.children && item.children.length) walk(item.children);
- else result.push({ label: item.label || item.productName || "", value: item.id || item.value });
+ if (item.children && item.children.length) {
+ walk(item.children);
+ } else {
+ result.push({
+ label: item.label || item.productName || "",
+ value: item.id || item.value,
+ });
+ }
});
};
walk(nodes);
return result;
};
- const findProductIdByLabel = label => {
- if (!label) return "";
- const hit = (productList.value || []).find(item => item.label === label);
- return hit?.value || "";
- };
-
const formatAmount = amount => `楼${Number(amount || 0).toFixed(2)}`;
const goBack = () => uni.navigateBack();
- const calcTotalAmountFromProducts = products =>
- Number(
- (products || [])
- .reduce((sum, item) => {
- const unitPrice = Number(item?.unitPrice || 0);
- const printingFee = Number(item?.printingFee || 0);
- const dieCuttingFee = Number(item?.dieCuttingFee || 0);
- const grindingFee = Number(item?.grindingFee || 0);
- return sum + unitPrice + printingFee + dieCuttingFee + grindingFee;
- }, 0)
- .toFixed(2)
+ const calculateAmount = product => {
+ product.amount = Number(
+ (Number(product.quantity || 0) * Number(product.unitPrice || 0)).toFixed(2)
);
-
- const syncTotalAmount = () => {
form.value.totalAmount = totalAmount.value;
};
- const calculateAmount = product => {
- product.amount = Number((Number(product.quantity || 0) * Number(product.unitPrice || 0)).toFixed(2));
- syncTotalAmount();
- };
-
- const addProduct = () => {
- if (isEditMode.value) {
- uni.showToast({ title: "缂栬緫妯″紡涓嬩笉鍏佽鏂板浜у搧", icon: "none" });
- return;
- }
- form.value.products.push(createEmptyProduct());
- };
-
+ const addProduct = () => form.value.products.push(createEmptyProduct());
const removeProduct = index => {
form.value.products.splice(index, 1);
- syncTotalAmount();
+ form.value.totalAmount = totalAmount.value;
};
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 => {
currentProductIndex.value = index;
showProductSheet.value = true;
};
-
const openModelPicker = index => {
currentProductIndex.value = index;
const current = form.value.products[index];
@@ -196,70 +356,122 @@
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" });
+ uni.showToast({ title: "鏆傛棤瑙勬牸鍨嬪彿", icon: "none" });
return;
}
showModelSheet.value = true;
};
+ const onSelectCustomer = action => {
+ 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 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;
fetchModelOptions(action.value, current);
};
-
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;
};
+ const onQuotationDateConfirm = e => {
+ form.value.quotationDate = formatDateToYMD(e.value);
+ showQuotationDatePicker.value = false;
+ };
+ const onValidDateConfirm = e => {
+ form.value.validDate = formatDateToYMD(e.value);
+ showValidDatePicker.value = false;
+ };
- const fetchProductOptions = async () => {
- const productTree = await productTreeList().catch(() => []);
- productList.value = flattenProductTree(Array.isArray(productTree) ? productTree : productTree?.data || []);
+ const fetchBaseOptions = async () => {
+ 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 || []
+ );
+ };
+
+ // 鏍规嵁鍚嶇О鍙嶆煡鑺傜偣 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 productName = item.product || item.productName || "";
+ // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究閫夋嫨鍣ㄥ弽鏄�
+ let resolvedProductId =
+ item.productId ||
+ findNodeIdByLabel(productList.value, productName) ||
+ "";
+
const row = {
uid: `p_${uidSeed++}`,
- id: item.id || "",
- salesQuotationId: item.salesQuotationId || "",
- productId: item.productId || "",
- product: item.product || item.productName || "",
- specificationId: item.specificationId || "",
- specification: item.specification || "",
+ productId: resolvedProductId,
+ product: productName,
+ productModelId: item.productModelId || "",
+ ProductModel: item.ProductModel || item.specification || "",
unit: item.unit || "",
- paper: item.paper || "",
- paperWeight: item.paperWeight || "",
quantity: Number(item.quantity || 1),
unitPrice: Number(item.unitPrice || 0),
- printingFee: Number(item.printingFee || 0),
- dieCuttingFee: Number(item.dieCuttingFee || 0),
- grindingFee: Number(item.grindingFee || 0),
- amount: Number(item.amount || Number(item.quantity || 0) * Number(item.unitPrice || 0)),
+ amount: Number(item.amount || 0),
modelOptions: [],
};
+
if (row.productId) {
await fetchModelOptions(row.productId, row);
- if (!row.specificationId && row.specification) {
- const matchedModel = (row.modelOptions || []).find(model => model.model === row.specification);
- if (matchedModel) {
- row.specificationId = matchedModel.id;
- if (!row.unit) row.unit = matchedModel.unit || "";
+ // 濡傛灉娌℃湁 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;
}
}
}
@@ -269,26 +481,41 @@
form.value.products = normalized;
};
- const loadEditFromStorage = async () => {
+ const loadDetail = async () => {
if (!quotationId.value) return;
- const cached = uni.getStorageSync("salesQuotationEdit");
- if (!cached || typeof cached !== "object") return;
- if (cached.id && String(cached.id) !== String(quotationId.value)) return;
- const data = cached;
- form.value = {
- ...form.value,
- id: data.id || form.value.id,
- remark: data.remark || "",
- };
-
- const rows = Array.isArray(data.products) && data.products.length ? data.products : [data];
- const normalizedRows = rows.map(item => ({
- ...item,
- productId: item.productId || findProductIdByLabel(item.product || item.productName || ""),
- }));
- await normalizeProductRows(normalizedRows);
- syncTotalAmount();
+ // 鐩存帴浠庢湰鍦板瓨鍌ㄨ幏鍙栨暟鎹紝涓嶅啀璋冪敤璇︽儏鎺ュ彛
+ 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 || "鑽夌",
+ 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 || []);
+ form.value.totalAmount = totalAmount.value;
+ } else {
+ console.warn("鏈壘鍒扮紦瀛樼殑鎶ヤ环鍗曡鎯呮暟鎹�");
+ }
};
const validateProducts = () => {
@@ -296,7 +523,14 @@
uni.showToast({ title: "璇疯嚦灏戞坊鍔犱竴涓骇鍝�", icon: "none" });
return false;
}
- const invalid = form.value.products.some(item => !item.productId || !item.specificationId || !item.unit || !Number(item.unitPrice || 0));
+ 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;
@@ -304,56 +538,30 @@
return true;
};
- const buildProductPayload = item => {
- const quantity = Number(item?.quantity || 0);
- const unitPrice = Number(item?.unitPrice || 0);
- const printingFee = Number(item?.printingFee || 0);
- const dieCuttingFee = Number(item?.dieCuttingFee || 0);
- const grindingFee = Number(item?.grindingFee || 0);
- return {
- id: item?.id || undefined,
- salesQuotationId: item?.salesQuotationId || null,
- product: item?.product || "",
- specification: item?.specification || "",
- unit: item?.unit || "",
- paper: item?.paper || "",
- paperWeight: item?.paperWeight || "",
- unitPrice,
- printingFee,
- dieCuttingFee,
- grindingFee,
- quantity,
- amount: Number(item?.amount ?? quantity * unitPrice),
- remark: form.value.remark || "",
- };
- };
-
const handleSubmit = async () => {
- if (!validateProducts()) return;
-
+ const valid = await formRef.value.validate().catch(() => false);
+ if (!valid || !validateProducts()) return;
loading.value = true;
- if (quotationId.value) {
- const editingItem = form.value.products[0] || {};
- const payload = buildProductPayload({
- ...editingItem,
- id: editingItem.id || quotationId.value,
- });
- editQuotationProduct(payload)
- .then(() => {
- uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
- setTimeout(() => uni.navigateBack(), 300);
- })
- .catch(() => {
- uni.showToast({ title: "淇濆瓨澶辫触", icon: "error" });
- })
- .finally(() => {
- loading.value = false;
- });
- return;
- }
- const payloadList = form.value.products.map(item => buildProductPayload(item));
- addOrUpdateQuotationProduct(payloadList)
+ // 鍚屾鏈�鏂扮殑鎬婚
+ form.value.totalAmount = totalAmount.value;
+ form.value.subtotal = totalAmount.value;
+
+ const payload = {
+ ...form.value,
+ products: form.value.products.map(item => ({
+ productId: item.productId,
+ product: item.product,
+ productModelId: item.productModelId,
+ ProductModel: item.ProductModel,
+ quantity: Number(item.quantity || 0),
+ unit: item.unit,
+ unitPrice: Number(item.unitPrice || 0),
+ amount: Number(item.amount || 0),
+ })),
+ };
+ const action = quotationId.value ? updateQuotation : addQuotation;
+ action(payload)
.then(() => {
uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
setTimeout(() => uni.navigateBack(), 300);
@@ -371,21 +579,56 @@
quotationId.value = options.id;
form.value.id = options.id;
} else {
- form.value.products = [];
+ const today = formatDateToYMD(Date.now());
+ form.value.quotationDate = today;
+ form.value.validDate = today;
}
});
onMounted(async () => {
- await fetchProductOptions();
- if (quotationId.value) await loadEditFromStorage();
+ await fetchBaseOptions();
+ if (quotationId.value) {
+ await loadDetail();
+ }
});
+
+ onUnmounted(() => {});
</script>
<style scoped lang="scss">
@import "@/static/scss/form-common.scss";
+ .account-detail {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 100px;
+ }
+
.form-container {
padding: 12px 12px 0;
+ }
+
+ .hero-card {
+ margin-bottom: 12px;
+ padding: 18px 18px 16px;
+ border-radius: 16px;
+ background: linear-gradient(135deg, #eef6ff 0%, #ffffff 100%);
+ box-shadow: 0 6px 18px rgba(41, 121, 255, 0.08);
+ }
+
+ .hero-title {
+ display: block;
+ font-size: 18px;
+ font-weight: 600;
+ color: #1f2d3d;
+ margin-bottom: 6px;
+ }
+
+ .hero-desc {
+ display: block;
+ font-size: 13px;
+ line-height: 1.6;
+ color: #7a8599;
}
.form-section {
@@ -435,13 +678,6 @@
padding: 16px 12px;
color: #999;
font-size: 14px;
- }
-
- .summary-tip {
- padding: 0 24rpx 24rpx;
- color: #909399;
- font-size: 12px;
- line-height: 1.6;
}
:deep(.u-cell-group__title) {
diff --git a/src/pages/sales/salesQuotation/index.vue b/src/pages/sales/salesQuotation/index.vue
index e9f6be3..4ecf910 100644
--- a/src/pages/sales/salesQuotation/index.vue
+++ b/src/pages/sales/salesQuotation/index.vue
@@ -1,90 +1,107 @@
<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="searchForm.product"
- placeholder="璇疯緭鍏ヤ骇鍝佸悕绉版悳绱�"
- clearable
- @change="getList(true)"
- />
+ <up-input class="search-text"
+ v-model="quotationNo"
+ placeholder="璇疯緭鍏ユ姤浠峰崟鍙锋悳绱�"
+ clearable
+ @change="getList" />
</view>
- <view class="filter-button" @click="getList(true)">
- <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 v-if="quotationList.length > 0" class="ledger-list">
- <view v-for="item in quotationList" :key="item.id" class="ledger-item">
+ <view class="tabs-section">
+ <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 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.product || "-" }}</text>
+ <text class="item-id">{{ item.quotationNo || "-" }}</text>
</view>
+ <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>
- <text class="detail-value">{{ item.specification || "-" }}</text>
+ <text class="detail-label">瀹㈡埛鍚嶇О</text>
+ <text class="detail-value">{{ item.customer || "-" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">绾稿紶</text>
- <text class="detail-value">{{ item.paper || "-" }}</text>
+ <text class="detail-label">涓氬姟鍛�</text>
+ <text class="detail-value">{{ item.salesperson || "-" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">瀹氶噺</text>
- <text class="detail-value">{{ item.paperWeight || "-" }}</text>
+ <text class="detail-label">鎶ヤ环鏃ユ湡</text>
+ <text class="detail-value">{{ item.quotationDate || "-" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">鍗曚环</text>
- <text class="detail-value">{{ formatAmount(item.unitPrice) }}</text>
+ <text class="detail-label">鏈夋晥鏈熻嚦</text>
+ <text class="detail-value">{{ item.validDate || "-" }}</text>
</view>
<view class="detail-row">
- <text class="detail-label">鍗扮増璐�</text>
- <text class="detail-value">{{ formatAmount(item.printingFee) }}</text>
+ <text class="detail-label">鎶ヤ环閲戦</text>
+ <text class="detail-value highlight">{{ formatAmount(item.totalAmount) }}</text>
</view>
- <view class="detail-row">
- <text class="detail-label">鍒�鐗堣垂</text>
- <text class="detail-value">{{ formatAmount(item.dieCuttingFee) }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">纾ㄥ叿璐�</text>
- <text class="detail-value">{{ formatAmount(item.grindingFee) }}</text>
- </view>
- <view class="detail-row">
- <text class="detail-label">鏁伴噺</text>
- <text class="detail-value">{{ Number(item.quantity || 0) }}</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" @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>
+ <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">
- <text>鏆傛棤鏁版嵁</text>
+ <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>
@@ -93,73 +110,85 @@
import { reactive, ref } from "vue";
import { onShow } from "@dcloudio/uni-app";
import PageHeader from "@/components/PageHeader.vue";
- import { deleteQuotation } from "@/api/salesManagement/salesQuotation";
- import { quotationProductListPage } from "@/api/salesManagement/salesQuotationProduct";
+ import {
+ deleteQuotation,
+ getQuotationList,
+ } from "@/api/salesManagement/salesQuotation";
- const searchForm = reactive({ product: "" });
+ const quotationNo = ref("");
const quotationList = ref([]);
- const goBack = () => uni.navigateBack();
- const goAdd = () => uni.navigateTo({ url: "/pages/sales/salesQuotation/edit" });
+
+ const tabList = reactive([
+ { name: "鍏ㄩ儴", value: "" },
+ { name: "寰呭鎵�", value: "寰呭鎵�" },
+ { name: "瀹℃牳涓�", value: "瀹℃牳涓�" },
+ { name: "閫氳繃", value: "閫氳繃" },
+ { name: "鎷掔粷", value: "鎷掔粷" },
+ ]);
+ const tabValue = ref(0);
+
+ const page = {
+ current: -1,
+ size: -1,
+ };
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const goAdd = () => {
+ uni.removeStorageSync("salesQuotationDetail");
+ uni.navigateTo({ url: "/pages/sales/salesQuotation/edit" });
+ };
+
const goEdit = item => {
- const source = item?.__raw || item || {};
- uni.setStorageSync("salesQuotationEdit", source);
+ if (!canEdit(item)) return;
+ uni.setStorageSync("salesQuotationDetail", item || {});
uni.navigateTo({ url: `/pages/sales/salesQuotation/edit?id=${item.id}` });
};
+
const goDetail = item => {
uni.setStorageSync("salesQuotationDetail", item || {});
uni.navigateTo({ url: `/pages/sales/salesQuotation/detail?id=${item.id}` });
};
- const formatAmount = amount => `楼${Number(amount || 0).toFixed(2)}`;
+ const canEdit = item => ["寰呭鎵�", "鎷掔粷"].includes(item?.status);
- const calcTotalAmountFromProducts = products =>
- Number(
- (products || [])
- .reduce((sum, product) => {
- const unitPrice = Number(product?.unitPrice || 0);
- const printingFee = Number(product?.printingFee || 0);
- const dieCuttingFee = Number(product?.dieCuttingFee || 0);
- const grindingFee = Number(product?.grindingFee || 0);
- return sum + unitPrice + printingFee + dieCuttingFee + grindingFee;
- }, 0)
- .toFixed(2)
- );
+ const onTabChange = val => {
+ tabValue.value = val.index;
+ getList();
+ };
- const normalizeQuotation = row => {
- const sourceProducts = Array.isArray(row?.products) && row.products.length ? row.products : [row];
- const first = sourceProducts[0] || {};
- return {
- ...row,
- __raw: row,
- customer: row?.customer || row?.customerName || first?.customer || first?.customerName || "",
- salesperson: row?.salesperson || row?.salesman || row?.salesPerson || first?.salesperson || "",
- quotationDate: row?.quotationDate || row?.quoteDate || first?.quotationDate || "",
- validDate: row?.validDate || row?.expireDate || first?.validDate || "",
- paymentMethod: row?.paymentMethod || row?.paymentType || first?.paymentMethod || "",
- product: first.product || first.productName || row?.product || "",
- specification: first.specification || row?.specification || "",
- paper: first.paper || row?.paper || "",
- paperWeight: first.paperWeight || row?.paperWeight || "",
- unitPrice: Number(first.unitPrice || row?.unitPrice || 0),
- printingFee: Number(first.printingFee || row?.printingFee || 0),
- dieCuttingFee: Number(first.dieCuttingFee || row?.dieCuttingFee || 0),
- grindingFee: Number(first.grindingFee || row?.grindingFee || 0),
- quantity: Number(first.quantity || row?.quantity || 0),
- quotationNo: row?.quotationNo || first?.quotationNo || "",
- totalAmount: Number(row?.totalAmount || calcTotalAmountFromProducts(sourceProducts)),
+ const getCurrentStatus = () => {
+ const currentTab = tabList[tabValue.value];
+ return currentTab?.value || "";
+ };
+
+ const formatAmount = amount => {
+ const num = Number(amount || 0);
+ return `楼${num.toFixed(2)}`;
+ };
+
+ const getStatusType = status => {
+ const statusMap = {
+ 寰呭鎵�: "info",
+ 瀹℃牳涓�: "primary",
+ 閫氳繃: "success",
+ 鎷掔粷: "danger",
};
+ return statusMap[status] || "info";
};
const getList = () => {
uni.showLoading({ title: "鍔犺浇涓�...", mask: true });
- quotationProductListPage({
- current: -1,
- size: -1,
- product: String(searchForm.product || "").trim(),
+ getQuotationList({
+ ...page,
+ quotationNo: quotationNo.value,
+ status: getCurrentStatus(),
})
.then(res => {
const records = res?.data?.records || res?.records || [];
- quotationList.value = Array.isArray(records) ? records.map(normalizeQuotation) : [];
+ quotationList.value = Array.isArray(records) ? records : [];
})
.catch(() => {
uni.showToast({ title: "鏌ヨ澶辫触", icon: "error" });
@@ -173,11 +202,11 @@
if (!item?.id) return;
uni.showModal({
title: "鍒犻櫎纭",
- content: "纭鍒犻櫎璇ユ姤浠峰悧锛�",
+ content: "纭鍒犻櫎璇ユ姤浠峰崟鍚楋紵",
success: res => {
if (!res.confirm) return;
uni.showLoading({ title: "澶勭悊涓�...", mask: true });
- deleteQuotation([item.id])
+ deleteQuotation(item.id)
.then(() => {
uni.showToast({ title: "鍒犻櫎鎴愬姛", icon: "success" });
getList();
@@ -200,6 +229,11 @@
<style scoped lang="scss">
@import "@/styles/sales-common.scss";
+ .tabs-section {
+ background: #ffffff;
+ padding: 0 12px 8px 12px;
+ }
+
.item-index {
max-width: 180rpx;
text-align: center;
diff --git a/src/pages/works.vue b/src/pages/works.vue
index 360fc0e..1a908d9 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -15,7 +15,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -37,7 +38,31 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
+ </view>
+ <text class="item-label">{{item.label}}</text>
+ </up-grid-item>
+ </up-grid>
+ </view>
+ </view>
+ <!-- 宸ヨ壓璁捐 -->
+ <view class="common-module design-module"
+ v-if="hasDesignItems">
+ <view class="module-header">
+ <view class="module-title-container">
+ <text class="module-title">宸ヨ壓璁捐</text>
+ </view>
+ </view>
+ <view class="module-content">
+ <up-grid :border="false"
+ col="4">
+ <up-grid-item v-for="(item, index) in designItems"
+ :key="index"
+ @click="handleCommonItemClick(item)">
+ <view class="icon-container">
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -59,7 +84,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -81,7 +107,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -125,7 +152,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -147,7 +175,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -191,7 +220,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -213,7 +243,8 @@
:key="index"
@click="handleCommonItemClick(item)">
<view class="icon-container">
- <image :src="item.icon" class="item-icon"></image>
+ <image :src="item.icon"
+ class="item-icon"></image>
</view>
<text class="item-label">{{item.label}}</text>
</up-grid-item>
@@ -249,16 +280,13 @@
<script setup>
import { ref, onMounted, nextTick, reactive, computed } from "vue";
- import { onShow } from "@dcloudio/uni-app";
import { userLoginFacotryList } from "@/api/login";
import { getProductWorkOrderById } from "@/api/productionManagement/productionReporting";
- import { createVersionUpgradeChecker } from "@/utils/versionUpgrade";
import DownloadProgressMask from "@/components/DownloadProgressMask.vue";
import modal from "@/plugins/modal";
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
- const { triggerVersionCheck } = createVersionUpgradeChecker({ logPrefix: "[version-works]" });
const show = ref(false);
const factoryList = ref([]);
const factoryListTem = ref([]);
@@ -280,12 +308,31 @@
currentStatus.value = statusList[statusIndex];
}, 3000);
};
+ // 宸ヨ壓璁捐鍔熻兘鏁版嵁
+ const designItems = reactive([
+ {
+ icon: "/static/images/icon/jichucanshu.svg",
+ label: "鍩虹鍙傛暟",
+ },
+ {
+ icon: "/static/images/icon/gongxuguanli.svg",
+ label: "宸ュ簭绠$悊",
+ },
+ {
+ icon: "/static/images/icon/bom.svg",
+ label: "BOM",
+ },
+ {
+ icon: "/static/images/icon/gongyiluxian.svg",
+ label: "宸ヨ壓璺嚎",
+ },
+ ]);
// 钀ラ攢绠$悊鍔熻兘鏁版嵁
const marketingItems = reactive([
{
icon: "/static/images/icon/kehudangan.svg",
- label: "瀹㈡埛妗f(绉佹捣)",
+ label: "瀹㈡埛妗f",
},
{
icon: "/static/images/icon/xiaoshoubaojia.svg",
@@ -312,12 +359,12 @@
label: "渚涘簲鍟嗗線鏉�",
},
{
- icon: "/static/images/icon/gongyingshangdangan.svg",
- label: "渚涘簲鍟嗘。妗�",
- },
- {
icon: "/static/images/icon/caigouguanli.svg",
label: "閲囪喘閫�璐�",
+ },
+ {
+ icon: "/static/images/icon/gongyingshangdangan.svg",
+ label: "渚涘簲鍟嗘。妗�",
},
]);
@@ -369,6 +416,18 @@
},
]);
+ // 妗f绠$悊鍔熻兘鏁版嵁
+ const archiveManagementItems = reactive([
+ {
+ icon: "/static/images/icon/jieyuedengji.svg",
+ label: "鍊熼槄鐧昏",
+ },
+ {
+ icon: "/static/images/icon/guihuandengji.svg",
+ label: "褰掕繕鐧昏",
+ },
+ ]);
+
// 鍞悗鏈嶅姟鍔熻兘鏁版嵁
const afterSalesServiceItems = reactive([
{
@@ -384,7 +443,7 @@
// 浠撳偍鐗╂祦鍔熻兘鏁版嵁
const warehouseLogisticsItems = reactive([
{
- icon: "/static/images/icon/xiaoshoutaizhang.svg",
+ icon: "/static/images/icon/kucunguanli.svg",
label: "搴撳瓨绠$悊",
},
]);
@@ -485,30 +544,39 @@
// 鐢熶骇绠℃帶鍔熻兘鏁版嵁
const productionItems = reactive([
- // {
- // icon: "/static/images/icon/shengchandingdan@2x.svg",
- // label: "鐢熶骇璁㈠崟",
- // },
- // {
- // icon: "/static/images/icon/shengchanpaigong@2x.svg",
- // label: "鐢熶骇娲惧伐",
- // },
- // {
- // icon: "/static/images/icon/shengchanpaichan@2x.svg",
- // label: "宸ュ簭鎺掍骇",
- // },
+ {
+ icon: "/static/images/icon/shengchanjihua.svg",
+ label: "涓荤敓浜ц鍒�",
+ },
+ {
+ icon: "/static/images/icon/shengchandingdan.svg",
+ label: "鐢熶骇璁㈠崟",
+ },
+ {
+ icon: "/static/images/icon/shengchanzhuisu.svg",
+ label: "鐢熶骇杩芥函",
+ },
+ {
+ icon: "/static/images/icon/shengchanshikuang.svg",
+ label: "宸ュ簭鐢熶骇瀹炲喌",
+ },
+ {
+ icon: "/static/images/icon/shengchanpaichan.svg",
+ label: "鐢熶骇鎺掍骇",
+ },
+
{
icon: "/static/images/icon/shengchanbaogong.svg",
label: "鐢熶骇鎶ュ伐",
},
{
- icon: "/static/images/icon/shengchanbaogong.svg",
- label: "鐢熶骇宸ュ崟",
+ icon: "/static/images/icon/baogongtaizhang.svg",
+ label: "鎶ュ伐鍙拌处",
},
- // {
- // icon: "/static/images/icon/shengchanhesuan@2x.svg",
- // label: "鐢熶骇鏍哥畻",
- // },
+ {
+ icon: "/static/images/icon/shengchanhesuan.svg",
+ label: "鐢熶骇鏍哥畻",
+ },
]);
// 璁惧绠$悊鍔熻兘鏁版嵁
@@ -539,7 +607,7 @@
const handleCommonItemClick = item => {
// 鏍规嵁涓嶅悓鐨勫姛鑳介」杩涜璺宠浆
switch (item.label) {
- case "瀹㈡埛妗f(绉佹捣)":
+ case "瀹㈡埛妗f":
uni.navigateTo({
url: "/pages/basicData/customerFile/index",
});
@@ -749,9 +817,9 @@
url: "/pages/productionManagement/productionDispatching/index",
});
break;
- case "宸ュ簭鎺掍骇":
+ case "宸ヨ壓璺嚎":
uni.navigateTo({
- url: "/pages/productionManagement/processScheduling/index",
+ url: "/pages/productionManagement/processRoute/index",
});
break;
case "鐢熶骇宸ュ崟":
@@ -759,12 +827,37 @@
url: "/pages/productionManagement/workOrder/index",
});
break;
+ case "涓荤敓浜ц鍒�":
+ uni.navigateTo({
+ url: "/pages/productionManagement/mainProductionPlan/index",
+ });
+ break;
+ case "鐢熶骇鎺掍骇":
+ uni.navigateTo({
+ url: "/pages/productionManagement/productionScheduling/index",
+ });
+ break;
case "鐢熶骇鎶ュ伐":
getcode();
+ break;
+ case "鎶ュ伐鍙拌处":
+ uni.navigateTo({
+ url: "/pages/productionManagement/productionReporting/ledger",
+ });
break;
case "鐢熶骇鏍哥畻":
uni.navigateTo({
url: "/pages/productionManagement/productionAccounting/index",
+ });
+ break;
+ case "鐢熶骇杩芥函":
+ uni.navigateTo({
+ url: "/pages/productionManagement/productionTraceability/index",
+ });
+ break;
+ case "宸ュ簭鐢熶骇瀹炲喌":
+ uni.navigateTo({
+ url: "/pages/productionManagement/processStatistics/index",
});
break;
case "璁惧鍙拌处":
@@ -897,6 +990,31 @@
url: "/pages/customerService/afterSalesHandling/index",
});
break;
+ case "鍊熼槄鐧昏":
+ uni.navigateTo({
+ url: "/pages/fileManagement/borrow/index",
+ });
+ break;
+ case "褰掕繕鐧昏":
+ uni.navigateTo({
+ url: "/pages/fileManagement/return/index",
+ });
+ break;
+ case "鍩虹鍙傛暟":
+ uni.navigateTo({
+ url: "/pages/productionDesign/basicParameters/index",
+ });
+ break;
+ case "宸ュ簭绠$悊":
+ uni.navigateTo({
+ url: "/pages/productionDesign/processManagement/index",
+ });
+ break;
+ case "BOM":
+ uni.navigateTo({
+ url: "/pages/productionDesign/bom/index",
+ });
+ break;
default:
uni.showToast({
title: `鐐瑰嚮浜�${item.label}`,
@@ -928,7 +1046,7 @@
factoryList.value = [];
});
}
- const getcode = () => {
+ const getcode = async () => {
uni.scanCode({
success: async res => {
// 瑙f瀽浜岀淮鐮佸唴瀹�
@@ -953,14 +1071,12 @@
const workData = workRes.data;
console.log("宸ュ崟鏁版嵁:", workData);
- orderRow = JSON.stringify({
- id: workData.id || workOrderId,
- planQuantity: workData.planQuantity - workData.completeQuantity,
- productProcessRouteItemId:
- workData.productProcessRouteItemId ||
- workData.浜у搧宸ヨ壓璺嚎椤笽D ||
- "",
- });
+ if (workData.endOrder === true) {
+ modal.msgError("璇ヨ鍗曞凡缁撴潫锛屾棤娉曟姤宸�");
+ return;
+ }
+
+ orderRow = JSON.stringify(workData);
console.log("鏋勯�犵殑orderRow:", orderRow);
} else {
@@ -1084,10 +1200,13 @@
// 瀹氫箟鑿滃崟閰嶇疆鏄犲皠
const menuMapping = {
- purchase: { target: purchaseItems, specialMapping: { "渚涘簲鍟嗘。妗�": "渚涘簲鍟嗙鐞�" } },
- collaboration: { target: collaborationItems, specialMapping: { "瑙勭珷鍒跺害": "瑙勭珷鍒跺害绠$悊" } },
+ collaboration: {
+ target: collaborationItems,
+ specialMapping: { 瑙勭珷鍒跺害: "瑙勭珷鍒跺害绠$悊" },
+ },
+ purchase: { specialMapping: { 渚涘簲鍟嗘。妗�: "渚涘簲鍟嗙鐞�" } },
};
- console.log(allowedMenuTitles)
+ console.log(allowedMenuTitles);
// 閫氱敤杩囨护鍑芥暟
const filterArray = (targetArray, specialMapping) => {
const filtered = targetArray.filter(item => {
@@ -1102,6 +1221,7 @@
// 杩囨护鍚勪釜妯″潡
filterArray(marketingItems);
+ filterArray(designItems);
filterArray(purchaseItems, menuMapping.purchase.specialMapping);
filterArray(financeManagementItems);
filterArray(collaborationItems, menuMapping.collaboration.specialMapping);
@@ -1111,23 +1231,34 @@
filterArray(qualityItems);
filterArray(productionItems);
filterArray(equipmentItems);
+ filterArray(archiveManagementItems);
+ filterArray(afterSalesServiceItems);
};
// 妫�鏌ユā鍧楁槸鍚︽湁鑿滃崟椤归渶瑕佹樉绀�
const hasMarketingItems = computed(() => marketingItems.length > 0);
+ const hasDesignItems = computed(() => designItems.length > 0);
const hasPurchaseItems = computed(() => purchaseItems.length > 0);
- const hasFinanceManagementItems = computed(() => financeManagementItems.length > 0);
- const hasAfterSalesServiceItems = computed(() => afterSalesServiceItems.length > 0);
+ const hasFinanceManagementItems = computed(
+ () => financeManagementItems.length > 0
+ );
+ const hasAfterSalesServiceItems = computed(
+ () => afterSalesServiceItems.length > 0
+ );
const hasCollaborationItems = computed(() => collaborationItems.length > 0);
const hasSafetyItems = computed(() => safetyItems.length > 0);
const hasQualityItems = computed(() => qualityItems.length > 0);
const hasHumanResourcesItems = computed(() => humanResourcesItems.length > 0);
- const hasWarehouseLogisticsItems = computed(() => warehouseLogisticsItems.length > 0);
+ const hasWarehouseLogisticsItems = computed(
+ () => warehouseLogisticsItems.length > 0
+ );
const hasProductionItems = computed(() => productionItems.length > 0);
const hasEquipmentItems = computed(() => equipmentItems.length > 0);
+ const hasArchiveManagementItems = computed(
+ () => archiveManagementItems.length > 0
+ );
onMounted(() => {
- triggerVersionCheck("onMounted");
// 姣忔杩涘叆棣栭〉閮藉己鍒跺埛鏂扮敤鎴蜂俊鎭拰璺敱鏉冮檺锛屼笉鍋氭湰鍦扮紦瀛樺垽鏂�
userStore.getInfo().then(() => {
userStore
@@ -1142,10 +1273,6 @@
getUserLoginFacotryList();
// 鍚姩閫氱煡鐘舵�佸畾鏃跺櫒
startStatusTimer();
- });
-
- onShow(() => {
- triggerVersionCheck("onShow");
});
</script>
diff --git a/src/static/images/icon/baogongtaizhang.svg b/src/static/images/icon/baogongtaizhang.svg
new file mode 100644
index 0000000..d8a822a
--- /dev/null
+++ b/src/static/images/icon/baogongtaizhang.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34784"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34784)"><path d="M22.914785281249998,14.7584486125C19.94918328125,14.5709972125,17.63954128125,12.1115937125,17.63853528125,9.140073812499999C17.63853528125,7.8441987125,18.08040928125,6.6541986125,18.81540928125,5.7021988625C16.85716058125,5.1570737325,16.802909881250002,5.6943240125,15.13253498125,6.5203237125C13.82265998125,6.7941990125,13.91541008125,5.0013237625,12.260785081249999,5.8281989124999996C13.102535281249999,7.6236989125,13.792035081249999,8.1040738125,13.94516038125,8.736698412500001C13.753535281249999,8.7576985125,13.60566088125,8.8758237125,13.60566088125,9.0201988125C13.60566088125,9.1636982125,13.753535281249999,9.282698912499999,13.94516138125,9.3028235125C13.93991188125,9.3308234125,13.93641088125,9.3570738125,13.92853638125,9.3894486125C13.799910981250001,9.8951988125,12.595911481249999,10.705448112500001,11.571286181249999,11.464948612499999L11.571286181249999,18.7930733125C11.571286181249999,19.3863233125,11.16791058125,19.865823312499998,10.670036281249999,19.865823312499998L7.51128649725,19.865823312499998C7.19803667125,22.8863223125,8.09316146125,25.6539483125,11.87228678125,25.7878223125C15.48691178125,25.9138223125,15.94628718125,25.9138223125,15.94628718125,25.9138223125C15.94628718125,25.9138223125,16.40566158125,25.9138223125,20.021161281250002,25.7878223125C26.02016028125,25.5751973125,24.75928728125,18.7221973125,22.914785281249998,14.7584486125ZM18.59141028125,17.703697312499997L18.59141028125,19.0109473125L16.719785681250002,19.0109473125L16.719785681250002,19.546446312500002L18.57128528125,19.546446312500002L18.57828428125,20.8598213125L16.71978378125,20.8545703125L16.71978378125,22.4269463125L15.25503448125,22.4199463125L15.25503448125,20.8545723125L13.40003438125,20.8545723125L13.40003438125,19.547323312499998L15.25503448125,19.547323312499998L15.25503448125,19.0118233125L13.40003438125,19.0118233125L13.40003438125,17.704573312500003L15.04503438125,17.697574312500002L12.55653478125,14.2518244125L14.20153378125,14.2518244125L16.04865928125,16.6685753125L17.693658281250002,14.2308264125L19.33778628125,14.2308264125L16.934161181249998,17.6975763125L18.59141028125,17.703697312499997Z" fill="#CFCFCF" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M22.914785281249998,14.7584486125C19.94918328125,14.5709972125,17.63954128125,12.1115937125,17.63853528125,9.140073812499999C17.63853528125,7.8441987125,18.08040928125,6.6541986125,18.81540928125,5.7021988625C16.85716058125,5.1570737325,16.802909881250002,5.6943240125,15.13253498125,6.5203237125C13.82265998125,6.7941990125,13.91541008125,5.0013237625,12.260785081249999,5.8281989124999996C13.102535281249999,7.6236989125,13.792035081249999,8.1040738125,13.94516038125,8.736698412500001C13.753535281249999,8.7576985125,13.60566088125,8.8758237125,13.60566088125,9.0201988125C13.60566088125,9.1636982125,13.753535281249999,9.282698912499999,13.94516138125,9.3028235125C13.93991188125,9.3308234125,13.93641088125,9.3570738125,13.92853638125,9.3894486125C13.799910981250001,9.8951988125,12.595911481249999,10.705448112500001,11.571286181249999,11.464948612499999L11.571286181249999,18.7930733125C11.571286181249999,19.3863233125,11.16791058125,19.865823312499998,10.670036281249999,19.865823312499998L7.51128649725,19.865823312499998C7.19803667125,22.8863223125,8.09316146125,25.6539483125,11.87228678125,25.7878223125C15.48691178125,25.9138223125,15.94628718125,25.9138223125,15.94628718125,25.9138223125C15.94628718125,25.9138223125,16.40566158125,25.9138223125,20.021161281250002,25.7878223125C26.02016028125,25.5751973125,24.75928728125,18.7221973125,22.914785281249998,14.7584486125ZM18.59141028125,17.703697312499997L18.59141028125,19.0109473125L16.719785681250002,19.0109473125L16.719785681250002,19.546446312500002L18.57128528125,19.546446312500002L18.57828428125,20.8598213125L16.71978378125,20.8545703125L16.71978378125,22.4269463125L15.25503448125,22.4199463125L15.25503448125,20.8545723125L13.40003438125,20.8545723125L13.40003438125,19.547323312499998L15.25503448125,19.547323312499998L15.25503448125,19.0118233125L13.40003438125,19.0118233125L13.40003438125,17.704573312500003L15.04503438125,17.697574312500002L12.55653478125,14.2518244125L14.20153378125,14.2518244125L16.04865928125,16.6685753125L17.693658281250002,14.2308264125L19.33778628125,14.2308264125L16.934161181249998,17.6975763125L18.59141028125,17.703697312499997Z" fill="#FF9533" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M10.38227634375,2.37548828125L0.92615223375,2.37548828125C0.42740228375,2.37548828125,0.02490234375,2.85586330125,0.02490234375,3.4464882812499997L0.02490234375,18.43261328125C0.02490234375,19.02323728125,0.42740231375,19.50186328125,0.92615223375,19.50186328125L10.38315134375,19.50186328125C10.88102634375,19.50186328125,11.28440134375,19.02323728125,11.28440134375,18.43261328125L11.28440134375,3.4464882812499997C11.28352534375,2.85586330125,10.88102534375,2.37548828125,10.38227634375,2.37548828125ZM3.62815214375,17.89798828125C3.62815214375,18.19286328125,3.42690204375,18.43261328125,3.17840214375,18.43261328125L1.82565224375,18.43261328125C1.57802724375,18.43261328125,1.37590234375,18.191988281249998,1.37590234375,17.89798828125L1.37590234375,16.29148728125C1.37590234375,15.99573828125,1.57715224375,15.75511128125,1.82565224375,15.75511128125L3.17665244375,15.75511128125C3.42602734375,15.75511128125,3.62815214375,15.99573728125,3.62815214375,16.29148728125L3.62815214375,17.89798828125ZM3.62815214375,14.15123828125C3.62815214375,14.44698828125,3.42690204375,14.68673828125,3.17840214375,14.68673828125L1.82565224375,14.68673828125C1.57802724375,14.68673828125,1.37590234375,14.44698828125,1.37590234375,14.15123828125L1.37590234375,12.54561328125C1.37590234375,12.25073818125,1.57715224375,12.01186368125,1.82565224375,12.01186368125L3.17665244375,12.01186368125C3.42602734375,12.01186368125,3.62815214375,12.25073818125,3.62815214375,12.54561328125L3.62815214375,14.15123828125ZM3.62815214375,10.40448858125C3.62815214375,10.70023918125,3.42690204375,10.94086358125,3.17840214375,10.94086358125L1.82565224375,10.94086358125C1.57802724375,10.94086358125,1.37590234375,10.70111368125,1.37590234375,10.40448858125L1.37590234375,8.79886388125C1.37590234375,8.503988781250001,1.57715224375,8.26423888125,1.82565224375,8.26423888125L3.17665244375,8.26423888125C3.42602734375,8.26423888125,3.62815214375,8.503988781250001,3.62815214375,8.79886388125L3.62815214375,10.40448858125ZM6.32927704375,18.43261528125L4.97827724375,18.43261528125C4.72890234375,18.43261528125,4.52852724375,18.19199028125,4.52852724375,17.89798928125L4.52852724375,16.29148928125C4.52852724375,15.99574028125,4.73065234375,15.75511328125,4.97827724375,15.75511328125L6.32927704375,15.75511328125C6.57865194375,15.75511328125,6.77902694375,15.99573828125,6.77902694375,16.29148928125L6.77902694375,17.89798928125L6.77990194375,17.89798928125C6.78077694375,18.19286428125,6.57865194375,18.43261528125,6.32927704375,18.43261528125ZM6.32927704375,14.68674028125L4.97827724375,14.68674028125C4.72890234375,14.68674028125,4.52852724375,14.44698928125,4.52852724375,14.15124028125L4.52852724375,12.54561528125C4.52852724375,12.25074008125,4.73065234375,12.01186558125,4.97827724375,12.01186558125L6.32927704375,12.01186558125C6.57865194375,12.01186558125,6.77902694375,12.25074008125,6.77902694375,12.54561528125L6.77902694375,14.15124028125L6.77990194375,14.15124028125C6.78077694375,14.44698928125,6.57865194375,14.68674028125,6.32927704375,14.68674028125ZM6.32927704375,10.93999008125L4.97827724375,10.93999008125C4.72890234375,10.93999008125,4.52852724375,10.70024018125,4.52852724375,10.40361498125L4.52852724375,8.79798988125C4.52852724375,8.50311518125,4.73065234375,8.26336478125,4.97827724375,8.26336478125L6.32927704375,8.26336478125C6.57865194375,8.26336478125,6.77902694375,8.50311518125,6.77902694375,8.79798988125L6.77902694375,10.40361498125L6.77990194375,10.40361498125C6.78077694375,10.70024018125,6.57865194375,10.93999008125,6.32927704375,10.93999008125ZM9.93252654375,17.89798928125C9.93252654375,18.19286428125,9.73127744375,18.43261528125,9.48277664375,18.43261528125L8.13090134375,18.43261528125C7.88152694375,18.43261528125,7.68027644375,18.19199028125,7.68027644375,17.89798928125L7.68027644375,16.29148928125C7.68027644375,15.99574028125,7.88152594375,15.75511328125,8.13090134375,15.75511328125L9.48190114375,15.75511328125C9.72952654375,15.75511328125,9.93165114375,15.99573828125,9.93165114375,16.29148928125L9.93165114375,17.89798928125L9.93252654375,17.89798928125ZM9.93252654375,14.15124028125C9.93252654375,14.44698928125,9.73127744375,14.68674028125,9.48277664375,14.68674028125L8.13090134375,14.68674028125C7.88152694375,14.68674028125,7.68027644375,14.44698928125,7.68027644375,14.15124028125L7.68027644375,12.54561528125C7.68027644375,12.25074008125,7.88152594375,12.01186558125,8.13090134375,12.01186558125L9.48190114375,12.01186558125C9.72952654375,12.01186558125,9.93165114375,12.25074008125,9.93165114375,12.54561528125L9.93165114375,14.15124028125L9.93252654375,14.15124028125ZM9.93252654375,10.40449048125C9.93252654375,10.70024108125,9.73127744375,10.94086548125,9.48277664375,10.94086548125L8.13090134375,10.94086548125C7.88152694375,10.94086548125,7.68027644375,10.70111558125,7.68027644375,10.40449048125L7.68027644375,8.79886528125C7.68027644375,8.50399068125,7.88152594375,8.26424028125,8.13090134375,8.26424028125L9.48190114375,8.26424028125C9.72952654375,8.26424028125,9.93165114375,8.50399068125,9.93165114375,8.79886528125L9.93165114375,10.40449048125L9.93252654375,10.40449048125ZM9.93252654375,6.65686558125L1.37590234375,6.65686558125L1.37590234375,3.98286338125L9.93165204375,3.98286338125L9.93165204375,6.65686318125L9.93252654375,6.65686558125ZM23.27452634375,4.50086328125C20.71282234375,4.50086328125,18.63615234375,6.57753468125,18.63615234375,9.13923788125C18.63615234375,11.70094208125,20.71282234375,13.77761328125,23.27452634375,13.77761328125C25.83623134375,13.77761328125,27.91290234375,11.70094208125,27.91290234375,9.13923788125C27.91290234375,6.57753468125,25.83623134375,4.50086328125,23.27452634375,4.50086328125ZM26.45340134375,8.56961348125C26.45340134375,8.606363281250001,26.41489934375,8.64398858125,26.37815134375,8.64398858125L25.85314934375,8.64398858125C25.81552534375,8.64398858125,25.77702534375,8.68161388125,25.74027634375,8.719239281250001L25.28965234375,9.20573898125L25.28965234375,9.317738981249999L25.66502734375,10.06761458125C25.70177634375,10.10436438125,25.74027634375,10.14198968125,25.77702734375,10.14198968125L26.00190134375,10.14198968125C26.04236234375,10.14420228125,26.07453734375,10.176757381249999,26.07627634375,10.21723988125L26.07627634375,10.81748958125C26.07627634375,10.85423948125,26.03952634375,10.89186478125,26.00190134375,10.89186478125L25.40252734375,10.89186478125C25.36577834375,10.89186478125,25.28965234375,10.85423948125,25.28965234375,10.81748958125L24.80140334375,9.842739581250001C24.80140334375,9.805114281249999,24.76465234375,9.805114281249999,24.72702834375,9.842739581250001L23.86515234375,10.81748958125C23.86515234375,10.85423948125,23.78990134375,10.89186478125,23.75227734375,10.89186478125L23.15290234375,10.89186478125C23.11527834375,10.89186478125,23.07852934375,10.85423948125,23.07852934375,10.81748958125L23.07852934375,10.21723988125C23.07852934375,10.17961458125,23.11527834375,10.14198968125,23.15290234375,10.14198968125L23.67790434375,10.14198968125C23.71640434375,10.14198968125,23.75227734375,10.10436438125,23.78990334375,10.10436438125L24.46452934375,9.317738981249999L24.46452934375,9.20573898125L24.24052834375,8.719239281250001C24.20202834375,8.68248938125,24.16527734375,8.64398858125,24.12677934375,8.64398858125L23.52565334375,8.64398858125C23.48890534375,8.64398858125,23.45215434375,8.606363281250001,23.45215434375,8.56961348125C23.45215434375,8.606363281250001,23.41452934375,8.64398858125,23.37690334375,8.64398858125L22.51590534375,8.64398858125L21.95152834375,10.89273838125C21.87890434375,11.26723858125,21.76602934375,11.60411358125,21.57703034375,11.79223828125C21.42828034375,11.97948838125,21.12903034375,12.01711368125,20.82802934375,12.01711368125L20.45352934375,12.01711368125L20.45352934375,11.26723768125L20.82802934375,11.26723768125C20.82802934375,11.26723768125,21.12990334375,11.34161278125,21.24190334375,10.89273738125L21.80365334375,8.64398768125L20.90502934375,8.64398768125C20.86652734375,8.64398768125,20.82890334375,8.606362781249999,20.82890334375,8.56961298125L20.82890334375,7.97023778125C20.82890334375,7.93261238125,20.86652734375,7.89498708125,20.90502934375,7.89498708125L21.99177934375,7.89498708125L22.17902934375,7.14598708125C22.29102934375,6.77061228125,22.47915434375,6.54573728125,22.66552934375,6.39611248125C23.37777934375,5.79586268125,24.20290334375,6.05836198125,24.57827934375,6.13361268125L24.57827934375,6.77061228125C24.20290334375,6.69711258125,23.64115534375,6.50811198125,23.22815534375,6.80823758125C23.11615534375,6.88348768125,22.92890534375,7.07161238125,22.89128134375,7.18361238125L22.70490634375,7.89498708125L23.37865634375,7.89498708125C23.41628034375,7.89498708125,23.45390734375,7.93261238125,23.45390734375,7.97023778125C23.45390734375,7.93261238125,23.49065634375,7.89498708125,23.52740634375,7.89498708125L24.50303034375,7.89498708125C24.54153034375,7.89498708125,24.57828134375,7.93261238125,24.61590534375,7.97023778125L24.95278134375,8.64486218125C24.95278134375,8.68248748125,24.99040634375,8.68248748125,25.02803034375,8.64486218125L25.66590734375,7.97023778125C25.66590734375,7.93261238125,25.74115734375,7.89498708125,25.77790634375,7.89498708125L26.37903234375,7.89498708125C26.41578134375,7.89498708125,26.45428234375,7.93261238125,26.45428234375,7.97023778125L26.45428234375,8.56961298125L26.45340134375,8.56961348125Z" fill="#FFDB42" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/bom.svg b/src/static/images/icon/bom.svg
new file mode 100644
index 0000000..42a7eca
--- /dev/null
+++ b/src/static/images/icon/bom.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_137_41249"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_137_41249)"><path d="M25,21.1513879C25,21.3787005,25.067284852,21.6009274,25.19337524,21.7900629L27.1094005,24.6641006C27.6657977,25.4986968,28.6024919,26,29.6055508,26L37,26C38.656855,26,40,24.656854199999998,40,23C40,21.343146,38.656855,20,37,20L26.1513879,20C25.51549393,20,25,20.51549393,25,21.1513879Z" fill="#FFDB42" fill-opacity="1" transform="matrix(-1,0,0,1,50,0)"/><rect x="2" y="2" width="25" height="21" rx="6" fill="#FF7B00" fill-opacity="1" style="opacity:0.800000011920929;"/><g><path d="M4.57750022,9.7000003L7.2685001,9.7000003Q8.5195003,9.7000003,9.145,10.037500399999999Q9.7705002,10.3750005,9.7705002,11.1760001Q9.7705002,12.4090004,8.285499999999999,12.6520004L8.285499999999999,12.6700001Q10.085499800000001,12.8950005,10.085499800000001,14.2360001Q10.085499800000001,15.217,9.3474998,15.6084995Q8.6094999,16,7.2235,16L4.57750022,16L4.57750022,15.8290005L5.1625001,15.8290005L5.1625001,9.8710003L4.57750022,9.8710003L4.57750022,9.7000003ZM6.9175,12.814000100000001L6.9175,15.8290005L7.3405001,15.8290005Q7.8355,15.8290005,8.0334997,15.4870005Q8.2315001,15.1450005,8.2315001,14.4790001L8.2315001,14.001999900000001Q8.2315001,13.408000000000001,7.9885000999999995,13.1110001Q7.7455001,12.814000100000001,7.124499999999999,12.814000100000001L6.9175,12.814000100000001ZM6.9175,9.8710003L6.9175,12.6430001L7.1515,12.6430001Q7.6195002,12.6430001,7.8040001,12.3594999Q7.9885000999999995,12.0760002,7.9885000999999995,11.4910002L7.9885000999999995,11.0680003Q7.9885000999999995,10.4020004,7.8265002,10.1365004Q7.6645,9.8710003,7.2055001,9.8710003L6.9175,9.8710003ZM14.8285,13.2189999L14.8285,12.5530005Q14.8285,10.9060001,14.6665,10.474000499999999Q14.4865,9.9790001,14.1535,9.8620005Q13.9644995,9.7900004,13.6945,9.7900004Q13.4244995,9.7900004,13.2309999,9.8620005Q13.0375004,9.9340005,12.9115,10.1140003Q12.7854996,10.2940001,12.7089996,10.4920001Q12.6324997,10.690000099999999,12.5874996,11.0500002Q12.5334997,11.572,12.5334997,12.589000200000001L12.5334997,13.2370005Q12.5334997,14.335,12.6189995,14.7985001Q12.7045002,15.2620001,12.8304996,15.46Q13.1094999,15.901,13.6945,15.901Q14.4055,15.901,14.617,15.3024998Q14.8285,14.704,14.8285,13.2189999ZM13.6765003,16.0900002Q12.1735001,16.0900002,11.4309998,15.2755003Q10.6884999,14.461,10.6884999,12.9175Q10.6884999,11.3740001,11.4849997,10.4875002Q12.2814999,9.601000299999999,13.7395,9.601000299999999Q15.1975,9.601000299999999,15.94,10.4425001Q16.682499999999997,11.2840004,16.682499999999997,12.8320003Q16.682499999999997,14.3800001,15.931,15.2349997Q15.1795,16.0900002,13.6765003,16.0900002ZM21.9835,9.7000003L24.386499,9.7000003L24.386499,9.8710003L23.8915,9.8710003L23.8915,15.8290005L24.386499,15.8290005L24.386499,16L21.6415,16L21.6415,15.8290005L22.136499,15.8290005L22.136499,10.2040005L22.1005,10.2040005L20.7505,16L19.625500000000002,16L18.0415,10.177L18.005499999999998,10.177L18.005499999999998,15.8290005L18.500500000000002,15.8290005L18.500500000000002,16L17.276501,16L17.276501,15.8290005L17.771501,15.8290005L17.771501,9.8710003L17.276501,9.8710003L17.276501,9.7000003L19.7065,9.7000003L20.9035,14.335L21.9835,9.7000003Z" fill="#FFFFFF" fill-opacity="1"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/gongxuguanli.svg b/src/static/images/icon/gongxuguanli.svg
new file mode 100644
index 0000000..f7bdcb4
--- /dev/null
+++ b/src/static/images/icon/gongxuguanli.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_137_41304"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_137_41307"><rect x="8" y="7" width="12" height="12" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_137_41304)"><path d="M22,22.0000005L22,22.9999995C22,25.2091389,23.7908611,27,26,27L34,27C36.209139,27,38,25.2091389,38,22.9999995L38,22.0000005C38,20.3431458,36.656855,19,35,19L25.0000005,19C23.3431458,19,22,20.3431458,22,22.0000005Z" fill="#FDC3C1" fill-opacity="1" transform="matrix(-1,0,0,1,44,0)"/><rect x="2" y="2" width="24" height="22" rx="6" fill="#FF2B00" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_137_41307)"><path d="M12.203554171875,9.8023956C12.203554171875,8.6526947,11.269422071874999,7.7185629,10.119721371875,7.7185629C8.970020531875,7.7185629,8.035888671875,8.6526947,8.035888671875,9.8023956C8.035888671875,10.3772457,8.251457571875,10.8802395,8.610738931875,11.239521C8.538882671875,11.6706586,8.467026411875,12.101797099999999,8.467026411875,12.5329351C8.467026411875,14.5449114,9.544870571875,16.2694607,11.053852571875,17.203594000000002L11.484991071875001,15.4790421C10.622715271875,14.760479,10.119721371875,13.682635300000001,10.119721371875,12.5329351C10.119721371875,12.317365599999999,10.119721371875,12.101797099999999,10.191577471875,11.886228599999999C11.269422071874999,11.886228599999999,12.203554671875,10.952096000000001,12.203554171875,9.8023956ZM13.999961371875,8.6526947C14.718523971875001,8.6526947,15.365230071875,8.8682635,15.940080671875,9.1556888C15.868224171875,9.3712578,15.796367671875,9.6586823,15.796367671875,9.874251600000001C15.796367671875,11.023952000000001,16.730499271875,11.9580836,17.880200371875,11.9580836C19.029902671875,11.9580836,19.964033671875,11.023952000000001,19.964033671875,9.874251600000001C19.964033671875,8.7245512,19.029902671875,7.7904191,17.880200371875,7.7904191C17.592776271875,7.7904191,17.377205871875,7.86227548,17.089782671875,7.93413174C16.227506671875,7.35928145,15.149661071875,7,13.999961371875,7C13.209542271875,7,12.419122671875,7.14371257,11.772416071875,7.502994L12.706547771875,8.8682635C13.137685771874999,8.724551,13.568823771875,8.6526947,13.999961371875,8.6526947ZM17.880200371875,12.6766467C17.808345771875,14.1137724,17.017926171875,15.335331,15.796367671875,15.910181C15.437087571875,15.2634745,14.718522571874999,14.760479,13.928104871875,14.760479C12.778404271875,14.760479,11.844271871875,15.6946106,11.844271871875,16.8443117C11.844271871875,17.994014,12.778404271875,18.928145,13.928104871875,18.928145C14.790379971875,18.928145,15.508942571875,18.425150000000002,15.868224171875,17.634729999999998C18.023912471875,16.8443117,19.532894671875,14.8323355,19.532894671875,12.4610786L19.532894671875,12.101797099999999C19.461038671875002,12.1736536,17.880200371875,12.6766467,17.880200371875,12.6766467Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/gongyiluxian.svg b/src/static/images/icon/gongyiluxian.svg
new file mode 100644
index 0000000..5b03c56
--- /dev/null
+++ b/src/static/images/icon/gongyiluxian.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_137_41224"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_137_41229"><rect x="8" y="6" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_137_41224)"><g transform="matrix(0,1.0000003576278687,-1.0000003576278687,0,29.00000089406967,-24.00000947713852)"><path d="M33.103223299999996,27.5C31.7891606,27.5,30.83216852,28.7456321,31.17075348,30.0153255L31.7081735,32.0306506C32.1751106,33.7816644,33.76091,35,35.5731134,35L41.426888,35C43.239091,35,44.824889,33.7816644,45.291826,32.0306506L45.829247,30.0153253C46.167831,28.7456321,45.210839,27.5,43.896776,27.5L33.103223299999996,27.5Z" fill="#69FFB4" fill-opacity="1" transform="matrix(1,0,0,-1,0,55)"/><path d="M26.5,8.500001000000001L26.5,17.999999000000003C26.5,21.313709,29.186291699999998,24,32.5000005,24L44.5,24C47.813709,24,50.5,21.313709,50.5,18L50.5,8.500001000000001C50.5,5.1862917,47.813709,2.5,44.5,2.5L32.500001,2.5C29.186291699999998,2.5,26.5,5.1862917,26.5,8.500001000000001Z" fill="#01BF34" fill-opacity="1" style="opacity:0.800000011920929;"/></g><g clip-path="url(#master_svg1_137_41229)"><path d="M20.366401,15.224C20.163159,15.224,19.9984,15.0592403,19.9984,14.8559999C19.9984,14.6527596,20.163159,14.4879999,20.366401,14.4879999C21.5888,14.4879999,23.014400000000002,13.776,23.014400000000002,11.7712002C23.014400000000002,9.7664003,21.4848,9.0544002,20.1744,9.0544002L13.0032001,9.0544002C12.7999592,9.0544002,12.635200000000001,8.889641,12.635200000000001,8.6864004C12.635200000000001,8.4831595,12.7999592,8.3184004,13.0032001,8.3184001L20.176001,8.3184001C21.139200000000002,8.3184001,22.020801,8.6272001,22.659201,9.1872001C23.372801000000003,9.8127999,23.750401,10.7055998,23.750401,11.7712002C23.750401,12.870400400000001,23.3744,13.7807999,22.664001,14.4032001C22.0592,14.9312,21.2432,15.224,20.366401,15.224ZM12.740799899999999,20.4032C11.7791998,20.4032,10.8992,20.096,10.2607999,19.536C9.54719996,18.9104,9.16959992,18.016,9.16959992,16.9488001C9.16959992,15.8495998,9.54559994,14.9392004,10.255999899999999,14.3168001C10.8607998,13.7872,11.6767998,13.4960003,12.5535998,13.4960003C12.7568407,13.4960003,12.9215999,13.6607594,12.9215999,13.8640003C12.9215999,14.0672407,12.7568407,14.2320004,12.5535998,14.2320004C11.3311999,14.2320004,9.90559983,14.9440002,9.90559983,16.9488001C9.90559983,17.796799999999998,10.1967999,18.500799999999998,10.7455997,18.9824C11.249599700000001,19.424,11.9583998,19.6672,12.7423997,19.6672L20.172800000000002,19.6672C20.37604,19.6672,20.540799,19.831960000000002,20.540799,20.0352C20.540799,20.238441,20.37604,20.4032,20.172800000000002,20.4032L12.740799899999999,20.4032ZM11.1775999,11.355199800000001C9.97599995,11.355199800000001,9,10.3792,9,9.1775999C9,7.97759986,9.97599995,7,11.1775999,7C11.7584,7,12.3055999,7.22720003,12.7167997,7.63840008C13.127999800000001,8.0496001,13.355199800000001,8.5968001,13.355199800000001,9.1775999C13.3536,10.3792,12.377599700000001,11.355199800000001,11.1775999,11.355199800000001ZM11.1775999,7.73599982C10.3823999,7.73599982,9.73599994,8.3823998,9.73599994,9.1775999C9.73599994,9.9728,10.3823999,10.6191998,11.1775999,10.6191998C11.9727998,10.6191998,12.6191998,9.9727998,12.6191998,9.1775999C12.6191998,8.7919998,12.4687998,8.4303999,12.1967998,8.1584001C11.9269657,7.8873536600000005,11.5600605,7.73529232,11.1775999,7.73599982ZM21.744,21.3536C22.744,21.3536,23.5536,20.544,23.5536,19.544C23.5536,18.544,22.744,17.7344,21.744,17.7344C20.744,17.7344,19.934401,18.544,19.934401,19.544C19.934401,20.023999,20.124800999999998,20.484799000000002,20.464001,20.823999C20.803201,21.161599000000002,21.264001,21.3536,21.744,21.3536ZM18.9136009,14.352000199999999C18.9136009,14.1008005,19.068801,13.8752003,19.304001,13.7872L19.425601,13.740799899999999L19.392001,13.6143999C19.321601,13.3392,19.212801,13.076799900000001,19.068801,12.8319998L18.998400699999998,12.7199998L18.8800011,12.7728C18.6508932,12.8759217,18.3818769,12.826487499999999,18.204396199999998,12.6486521C18.0269165,12.4708161,17.978021599999998,12.2017016,18.0816011,11.9727998L18.1344013,11.8543997L18.0224009,11.785599699999999C17.7782354,11.641330199999999,17.514800100000002,11.532507899999999,17.2400007,11.4623995L17.1136007,11.430399399999999L17.0672007,11.5519996C16.9783568,11.7868605,16.7535033,11.9422832,16.5023999,11.9423995C16.2511997,11.9423995,16.0271997,11.7871995,15.9375997,11.5519996L15.8911996,11.430399399999999L15.7727995,11.4655995C15.497599600000001,11.5359993,15.2351995,11.6447997,14.990399400000001,11.7887993L14.8783994,11.859199499999999L14.9311996,11.9775996C15.0144258,12.1639524,14.9969354,12.379872800000001,14.8847995,12.550399800000001C14.722160800000001,12.7994199,14.4025073,12.8930764,14.1311994,12.7711997L14.012799300000001,12.7183995L13.9423995,12.8303995C13.796959900000001,13.0741673,13.6875596,13.337698,13.6175995,13.612799599999999L13.5855994,13.7391996L13.7071996,13.785599699999999C13.9423995,13.8751998,14.097599500000001,14.0991993,14.097599500000001,14.3504C14.097599500000001,14.601599700000001,13.9423995,14.8255997,13.7071996,14.915200200000001L13.5855994,14.9616003L13.6175995,15.0880003C13.6879992,15.3632002,13.796799700000001,15.6255999,13.9407992,15.8704004L14.0111995,15.9824009L14.129599599999999,15.9296007C14.358708400000001,15.826478,14.6277251,15.8759127,14.8052053,16.0537481C14.9826856,16.2315845,15.0315804,16.500700000000002,14.9279995,16.7296009L14.8751993,16.848000499999998L14.9871993,16.9184008C15.2319994,17.060800999999998,15.495999300000001,17.168001,15.7695994,17.236801L15.895999400000001,17.268801L15.9423995,17.147201000000003C16.0312433,16.9123402,16.2560968,16.756917,16.507199800000002,16.7568007C16.7583995,16.7568007,16.9823995,16.9120007,17.0720005,17.147201000000003L17.1184006,17.268801L17.244800599999998,17.236801C17.520000500000002,17.166401,17.7824001,17.057601,18.0272007,16.9136009L18.139201200000002,16.8448009L18.086401000000002,16.7264013C18.0016012,16.5392017,18.017601,16.3232012,18.129600500000002,16.1504011C18.2416,15.9776011,18.432000199999997,15.873601,18.6368008,15.8752012C18.7216005,15.8752012,18.8064003,15.8928013,18.884800900000002,15.9280014L19.003201,15.9808016L19.073601,15.8688011C19.216001,15.6256008,19.321601,15.3648014,19.390401,15.0928011L19.4224,14.966401099999999L19.300800000000002,14.920001C19.066637999999998,14.8290253,18.9127045,14.6032133,18.9136009,14.352000199999999ZM18.184001000000002,15.3168001C18.1280012,15.3008003,18.0720005,15.2911997,18.0128012,15.2911997C17.841601400000002,15.2911997,17.678401,15.3647995,17.564801199999998,15.4911995C17.400196100000002,15.6712322,17.3392162,15.9230013,17.4032011,16.1583996C17.2944012,16.2207994,17.1792011,16.2719994,17.0608015,16.3119993C16.9594064,16.099060100000003,16.7446485,15.9633579,16.508801,15.9631996C16.2736011,15.9631996,16.057601,16.099199300000002,15.956800900000001,16.3119993C15.836801099999999,16.2735996,15.7232008,16.2223997,15.6144009,16.1583996C15.6752009,15.9231997,15.6144009,15.6735992,15.4528008,15.4927998C15.3391409,15.3667078,15.1777534,15.2941418,15.008000899999999,15.2927999C14.9504008,15.2927999,14.8928008,15.3008003,14.836801099999999,15.3184004C14.779201,15.2000008,14.732801,15.0768003,14.6976008,14.9488001C14.8990641,14.8178301,15.020664700000001,14.593893099999999,15.0208011,14.353600499999999C15.0208011,14.1136007,14.8992009,13.8896008,14.6976008,13.7584004C14.732801,13.6304007,14.779201,13.5072002,14.836801099999999,13.3888006C14.8928013,13.4048004,14.948801,13.4144006,15.008000899999999,13.4144006C15.1776009,13.4128008,15.339201,13.342400600000001,15.4528008,13.216000600000001C15.6174059,13.0359683,15.6783862,12.7841992,15.6144009,12.548800499999999C15.7232008,12.4864006,15.838400799999999,12.4352007,15.956800900000001,12.395200299999999C16.0581956,12.6081395,16.2729535,12.7438416,16.508801,12.744000400000001C16.7440009,12.744000400000001,16.9600005,12.6080003,17.0608015,12.395200299999999C17.1808014,12.4336004,17.2944012,12.486400100000001,17.4032011,12.548800499999999C17.3424015,12.7840004,17.4032011,13.0352006,17.566401499999998,13.216000600000001C17.6800013,13.342400600000001,17.8432016,13.4160004,18.0144014,13.4160004C18.0720015,13.4160004,18.1296015,13.4080005,18.1856012,13.3904004C18.243201300000003,13.5088005,18.2896013,13.632000399999999,18.3248014,13.7600002C18.1237259,13.8911381,18.0026922,14.1151409,18.0032015,14.355199800000001C18.0032015,14.5951996,18.124801599999998,14.819199600000001,18.3248014,14.9503994C18.288001100000002,15.0767994,18.241601,15.1999998,18.184001000000002,15.3168001ZM16.507199800000002,13.452799800000001C16.0112,13.452799800000001,15.6079998,13.8559999,15.6079998,14.352000199999999C15.6079998,14.848000500000001,16.0112,15.2512007,16.507199800000002,15.2512007C17.0032005,15.2512007,17.4064007,14.848000500000001,17.4064007,14.352000199999999C17.4064007,13.8559999,17.0032005,13.4544001,16.507199800000002,13.452799800000001Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="" fill="#FFFFFF" fill-opacity="1"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/guihuandengji.svg b/src/static/images/icon/guihuandengji.svg
new file mode 100644
index 0000000..5993654
--- /dev/null
+++ b/src/static/images/icon/guihuandengji.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_137_41279"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_137_41281"><rect x="8" y="6" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_137_41279)"><path d="M1,18C1,20.209139,2.7908611,22,5.000001,22L6.9999995,22C8.6568542,22,10,20.656855,10,19L10,9.0000005C10,7.3431458,8.6568542,6,6.9999995,6L5.0000005,6C2.7908611,6,1,7.7908611,1,10L1,18Z" fill="#FC99E5" fill-opacity="1"/><rect x="5" y="2" width="22" height="24" rx="6" fill="#815DFA" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_137_41281)"><path d="M16.679638871875,20.4578125L16.679638871875,7.9312499800000005L17.743700971875,7.24531245C17.842138271875,7.174999952,17.964013071875,7.1328125,18.093701371875,7.1328125L22.421826171875,7.1328125C22.960889171875,7.151562452,23.392138171875,7.59375,23.392138171875,8.1375L23.392138171875,19.0671875C23.364014171875,19.5640625,22.976514171875,19.9640625,22.485888171875,20.0109375L18.234326371875,20.0109375L16.584326771875,20.9640625C16.434327171875,21.0359375,16.271826771875,21.0671875,16.109326371875,21.0609375C16.428076771875,21.0437505,16.679638871875,20.7812505,16.679638871875,20.4578125ZM15.396826271875,20.4578125L15.396826271875,7.9312499800000005L14.332763671875,7.24531245C14.234326371875,7.174999952,14.112451071875,7.1328125,13.982763771875,7.1328125L9.654638651875,7.1328125C9.115576151875,7.151562452,8.684326171875,7.59375,8.684326171875,8.1375L8.684326171875,19.0671875C8.712451159875,19.5640625,9.099951151875,19.9640625,9.590576171875,20.0109375L13.843701371875,20.0109375L15.493701471875,20.9640625C15.643701571874999,21.0359375,15.806201471875,21.0671875,15.968701371875,21.0609375C15.649951471875,21.0437505,15.396826271875,20.7812505,15.396826271875,20.4578125Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M14.1343751,10.83447265625C14.1343751,10.48134755625,13.8484378,10.19384765625,13.4937501,10.19384765625L12.1171875,10.19384765625C11.7640624,10.19384765625,11.4765625,10.47978496625,11.4765625,10.83447265625C11.4765625,11.18759775625,11.76250005,11.47509765625,12.1171875,11.47509765625L13.4937501,11.47509765625C13.8468752,11.47353505625,14.1343751,11.18759775625,14.1343751,10.83447265625ZM14.1343751,13.69384765625C14.1343751,13.34072255625,13.8484378,13.05322265625,13.4937501,13.05322265625L11.8046875,13.05322265625C11.4515624,13.05322265625,11.1640625,13.33915995625,11.1640625,13.69384765625C11.1640625,14.04697275625,11.45000005,14.334473156249999,11.8046875,14.334473156249999L13.4937501,14.334473156249999C13.8468752,14.332911056250001,14.1343751,14.04697275625,14.1343751,13.69384765625ZM14.1343751,16.55322315625C14.1343751,16.20009855625,13.8484378,15.912598156249999,13.4937501,15.912598156249999L12.2734375,15.912598156249999C11.9203124,15.912598156249999,11.6328125,16.19853545625,11.6328125,16.55322315625C11.6328125,16.90634775625,11.91875005,17.19384815625,12.2734375,17.19384815625L13.4937501,17.19384815625C13.8468752,17.19384815625,14.1343751,16.90634775625,14.1343751,16.55322315625Z" fill="#815DFA" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/jichucanshu.svg b/src/static/images/icon/jichucanshu.svg
new file mode 100644
index 0000000..b184c59
--- /dev/null
+++ b/src/static/images/icon/jichucanshu.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_137_41236"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_137_41241"><rect x="7" y="6" width="15" height="14" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_137_41236)"><path d="M25,21.1513879C25,21.3787005,25.067284852,21.6009274,25.19337524,21.7900629L27.1094005,24.6641006C27.6657977,25.4986968,28.6024919,26,29.6055508,26L37,26C38.656855,26,40,24.656854199999998,40,23C40,21.343146,38.656855,20,37,20L26.1513879,20C25.51549393,20,25,20.51549393,25,21.1513879Z" fill="#FFDB42" fill-opacity="1" transform="matrix(-1,0,0,1,50,0)"/><rect x="2" y="2" width="25" height="21" rx="6" fill="#FF7B00" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_137_41241)"><path d="M9.678466799999999,19L19.324707,19C20.797660999999998,18.995276,21.991028,17.829696,21.997802999999998,16.38916L21.997847999999998,8.619499900000001C21.995915,7.1755933,20.801091,6.004747971,19.324704,6L9.6784396,6C8.1999841,6.0019013807,7.0019443023,7.173578,7,8.619499900000001L7,16.38916C7.0067913504,17.83171,8.2034429,18.998117,9.678466799999999,19ZM8.2226562,16.38916L8.2227658,8.619499900000001C8.223738,7.8336385,8.874896,7.1968093,9.6784396,7.1958585L19.324704,7.1958585C20.128249,7.1968096,20.779405,7.8336387,20.780375,8.619499900000001L20.780273,16.38916C20.779304,17.17502,20.128252,17.811790000000002,19.324707,17.812744000000002L9.678466799999999,17.812744000000002C8.8749235,17.811790000000002,8.223628399999999,17.17502,8.2226562,16.38916Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/><path d="M10.4777521,10.089073248C10.3469837,10.30475119,10.1729468,10.49624744,9.96464378,10.65365517C9.70487261,10.86177546,9.41337633,11.0346493,9.099201024,11.1669112L9,11.2089053L9,12.0736644L9.23431939,11.9990091C9.47174489,11.9133329,9.7006349,11.8092618,9.91846389,11.6879447C10.0381892,11.6241767,10.1476524,11.5588531,10.2468534,11.4950846L10.2468534,14.9121275L11.2063658,14.9121275L11.2063658,10.0019750439L10.5256423,10.0019750439L10.4777521,10.089073248ZM13.7873006,14.0209274C13.886501299999999,13.9260538,14.129372100000001,13.736305,14.4885483,13.4563463C14.8311844,13.2016056,15.1556678,12.9273148,15.4600329,12.6351361C15.6386623,12.4559331,15.784215,12.2516172,15.8910446,12.0301166C15.9808555,11.839932300000001,16.0273976,11.6351838,16.0278735,11.4282067C16.0362639,11.0429803,15.8632469,10.67284936,15.552392000000001,10.41102597C15.2064652,10.12802772,14.7514644,9.980976105,14.286725,10.0019761715C13.8312612,9.982462278,13.384000799999999,10.11711118,13.031321,10.37991947C12.697223900000001,10.66255075,12.4998209,11.0540031,12.4822946,11.468645L12.4669011,11.6241771L13.4572001,11.7174965L13.4572001,11.5464114C13.4430242,11.3382672,13.5226898,11.1338509,13.6778364,10.980274080000001C13.8367767,10.84198374,14.051708699999999,10.76988751,14.271332300000001,10.78119278C14.4805198,10.77137673,14.6852269,10.83866006,14.8391714,10.96783167C14.9768214,11.0840187,15.0533643,11.2481257,15.0495462,11.4188752C15.033392899999999,11.6317267,14.944266299999999,11.8346176,14.7947035,11.9990091C14.4746122,12.3486953,14.112628,12.664825,13.7154651,12.9415345C13.4162827,13.1567047,13.138164,13.395041899999999,12.8842304,13.653872C12.6985469,13.8477199,12.551005400000001,14.0692272,12.4480877,14.3086624C12.3868821,14.4523063,12.3583713,14.6057506,12.3642807,14.7597065L12.3642807,14.9152384L16.0347157,14.9152384L16.0347157,14.0598116L13.7359896,14.0598116L13.7873006,14.0209274ZM19.71712,12.5698113C19.601044,12.4313142,19.451921,12.3185742,19.280981,12.2400832C19.386072,12.1649587,19.476803,12.0745716,19.549506,11.9725679C19.683591,11.7880906,19.754429000000002,11.5715657,19.75304,11.3504392C19.752536,11.1138016,19.680339,10.88186604,19.544376,10.6800952C19.400192,10.46579963,19.191989,10.29319018,18.9440393,10.18239217C18.6848888,10.063231848,18.3980541,10.0024236778,18.107672700000002,10.0050853989C17.7032843,9.9939849926,17.3084183,10.1179171,16.9976482,10.35347748C16.687448,10.60396022,16.4845166,10.94603568,16.4246755,11.3193326L16.3938913,11.4748646L17.353403999999998,11.6303967L17.3807688,11.4748646C17.3993073,11.2775718,17.4895763,11.0915124,17.6373234,10.9460547C17.7680531,10.83463174,17.942295100000003,10.77528393,18.121356,10.78119051C18.2964592,10.77241272,18.4680042,10.82852536,18.5968361,10.93672252C18.7133751,11.0420121,18.775506,11.1872133,18.7678719,11.3364403C18.7851925,11.5141391,18.6938963,11.6861514,18.5301323,11.7843734C18.345854799999998,11.8907928,18.130917500000002,11.944935300000001,17.9126902,11.9399053L17.8323059,11.9399053L17.6612692,11.9227965L17.5500956,12.7968874L17.7963877,12.7377851C17.9239302,12.704607,18.0554733,12.6858013,18.1880589,12.6817935C18.406900399999998,12.6736355,18.6197557,12.7479084,18.7764254,12.8870962C18.9297466,13.0269716,19.012158,13.2185521,19.003902,13.415905500000001C19.009782,13.6304023,18.9167614,13.8373623,18.7473497,13.986708199999999C18.586937,14.1388984,18.363360399999998,14.2224822,18.131618500000002,14.2168956C17.9424763,14.223569399999999,17.758078599999997,14.1620855,17.6185102,14.0458102C17.4482222,13.878574799999999,17.3399401,13.6669579,17.3089342,13.440789L17.2764359,13.299254900000001L16.3357382,13.4127934L16.3528419,13.568325999999999C16.384346999999998,13.9641922,16.582900000000002,14.3336644,16.906997699999998,14.599504C17.241446500000002,14.8689198,17.6764793,15.0119162,18.123066899999998,14.9992218C18.618236500000002,15.0108728,19.098183,14.8430104,19.457145,14.5326242C19.807885,14.2425933,20.005249,13.8298612,19.999333999999998,13.3987994C20.009191,13.101628999999999,19.909892,12.8099413,19.71712,12.5698113Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/jieyuedengji.svg b/src/static/images/icon/jieyuedengji.svg
new file mode 100644
index 0000000..a8c248d
--- /dev/null
+++ b/src/static/images/icon/jieyuedengji.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_137_41267"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_137_41269"><rect x="9" y="7" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_137_41267)"><path d="M1,18C1,20.209139,2.7908611,22,5.000001,22L6.9999995,22C8.6568542,22,10,20.656855,10,19L10,9.0000005C10,7.3431458,8.6568542,6,6.9999995,6L5.0000005,6C2.7908611,6,1,7.7908611,1,10L1,18Z" fill="#7BF1FF" fill-opacity="1"/><rect x="5" y="2" width="22" height="24" rx="6" fill="#0066FF" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_137_41269)"><path d="M13.5,8L11.5,8C10.66999996,8,10,8.66999996,10,9.5L10,17.5C10,18.33,10.66999996,19,11.5,19L13.5,19C14.3299999,19,15,19.67,15,20.5C15,20.78,15.2199998,21,15.5,21C15.7800002,21,16,20.78,16,20.5L16,10.5C16,9.1199999,14.8800001,8,13.5,8ZM21.5,8L19.5,8C18.1199999,8,17,9.1199999,17,10.5L17,20.5C17,20.78,17.2200003,21,17.5,21C17.7799997,21,18,20.78,18,20.5C18,19.67,18.6700001,19,19.5,19L21.5,19C22.33,19,23,18.33,23,17.5L23,9.5C23,8.66999996,22.33,8,21.5,8Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/kucunguanli.svg b/src/static/images/icon/kucunguanli.svg
new file mode 100644
index 0000000..19f79e0
--- /dev/null
+++ b/src/static/images/icon/kucunguanli.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28.000038623820046" height="28.000038623820046" viewBox="0 0 28.000038623820046 28.000038623820046"><defs><clipPath id="master_svg0_137_41296"><rect x="0" y="28.000038623820046" width="28.000028610229492" height="28.000028610229492" rx="0"/></clipPath><clipPath id="master_svg1_137_41298"><rect x="25" y="35.000038623820046" width="15" height="15.000000953674316" rx="0"/></clipPath></defs><g transform="matrix(0,-1.0000003576278687,1.0000003576278687,0,-28.00004863741418,28.000038623820046)" clip-path="url(#master_svg0_137_41296)"><path d="M1,46.000038623820046C1,48.20917762382005,2.7908611,50.000038623820046,5.000001,50.000038623820046L6.9999995,50.000038623820046C8.6568542,50.000038623820046,10,48.65689362382005,10,47.000038623820046L10,37.000039123820045C10,35.34318442382005,8.6568542,34.000038623820046,6.9999995,34.000038623820046L5.0000005,34.000038623820046C2.7908611,34.000038623820046,1,35.79089972382005,1,38.000038623820046L1,46.000038623820046Z" fill="#7BF1FF" fill-opacity="1"/><rect x="5" y="30.000038623820046" width="22" height="24" rx="6" fill="#0066FF" fill-opacity="1" style="opacity:0.800000011920929;"/><g transform="matrix(0,0.9999996423721313,-0.9999996423721313,0,60.00002610683083,10.000047564516763)" clip-path="url(#master_svg1_137_41298)"><path d="M27.0000062114314,42.054564923820045C27.0000062114314,41.35603812382005,27.45221668,40.45351172382004,28.0130587,40.04109122382005C28.0130587,40.04109122382005,32.0526376,37.000038623820046,32.684216,37.000038623820046C33.315794,37.000038623820046,37.361689,40.04551222382005,37.361689,40.04551222382005C37.917479,40.45603852382005,38.368425,41.35414362382004,38.368425,42.05393362382004L38.368425,47.73498362382004C38.369824,48.43226062382004,37.805701,48.998645623820046,37.108425,49.000038623820046L28.260006,49.000038623820046C27.56244022,48.999341623820044,26.9979092889,48.43254862382005,27.0000062114314,47.73498362382004L27.0000062114314,42.054564923820045ZM29.5263219,41.42677502382005L29.5263219,42.678564023820044C29.5263219,43.036036523820044,29.8117952,43.31582642382005,30.1642168,43.31582642382005L31.4147429,43.31582642382005C31.7728481,43.31582642382005,32.0526376,43.03035302382005,32.0526376,42.67793322382005L32.0526376,41.427405823820045C32.0526376,41.06930062382005,31.767164700000002,40.789510923820046,31.4147429,40.789510923820046L30.1642172,40.789510923820046C29.8061118,40.789510923820046,29.5263224,41.07498402382004,29.5263224,41.427405823820045L29.5263219,41.42677502382005ZM34.128005,40.71688012382005L33.2437949,41.60172222382005C32.9911637,41.854353423820044,32.9943199,42.254774523820046,33.2437949,42.503616823820046L34.128005,43.387826423820044C34.3806367,43.64045762382005,34.7810588,43.63730092382005,35.0298996,43.387826423820044L35.9141111,42.503615423820044C36.166742299999996,42.25098422382005,36.1635847,41.85056402382005,35.9141111,41.60172172382005L35.0298996,40.717511623820045C34.7772684,40.464880223820046,34.3768468,40.468038123820044,34.128005,40.717511623820045L34.128005,40.71688012382005ZM29.5263219,45.21688032382005L29.5263219,46.468669423820046C29.5263219,46.826143723820046,29.8117952,47.105932623820046,30.1642168,47.105932623820046L31.4147429,47.105932623820046C31.7728481,47.105932623820046,32.0526376,46.82045892382005,32.0526376,46.46803902382005L32.0526376,45.21751262382004C32.0526376,44.859406923820046,31.767164700000002,44.57961702382005,31.4147429,44.57961702382005L30.1642172,44.57961702382005C29.8061118,44.57961702382005,29.5263224,44.86509232382004,29.5263224,45.21751262382004L29.5263219,45.21688032382005ZM33.3157954,45.21688032382005L33.3157954,46.468669423820046C33.3157954,46.826143723820046,33.6012692,47.105932623820046,33.9536896,47.105932623820046L35.204215,47.105932623820046C35.5623198,47.105932623820046,35.8421097,46.82045892382005,35.8421097,46.46803902382005L35.8421097,45.21751262382004C35.8421097,44.859406923820046,35.5566368,44.57961702382005,35.204215,44.57961702382005L33.9536896,44.57961702382005C33.595585299999996,44.57961702382005,33.3157954,44.86509232382004,33.3157954,45.21751262382004L33.3157954,45.21688032382005Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/shengchandingdan.svg b/src/static/images/icon/shengchandingdan.svg
new file mode 100644
index 0000000..3109429
--- /dev/null
+++ b/src/static/images/icon/shengchandingdan.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34441"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34750"><rect x="7" y="5" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34441)"><g transform="matrix(1,3.7247229744963306e-9,-3.7247229744963306e-9,1,7.449445948992661e-9,-7.449445948992661e-9)"><path d="M8.6032233,27C7.2891606,27,6.33216852,28.2456321,6.67075348,29.5153255L7.2081735,31.5306506C7.6751106,33.2816644,9.260909999999999,34.5,11.0731134,34.5L16.926887999999998,34.5C18.739091000000002,34.5,20.324889,33.2816644,20.791826,31.5306506L21.329247000000002,29.5153253C21.667831,28.2456321,20.710839,27,19.396776,27L8.6032233,27Z" fill="#69FFB4" fill-opacity="1" transform="matrix(1,0,0,-1,0,54)"/><path d="M2,8.000001000000001L2,17.499999000000003C2,20.813709,4.6862917,23.5,8.000000499999999,23.5L20,23.5C23.313709,23.5,26,20.813709,26,17.5L26,8.000001000000001C26,4.6862917,23.313709,2,20,2L8.000001000000001,2C4.6862917,2,2,4.6862917,2,8.000001000000001Z" fill="#01BF34" fill-opacity="1" style="opacity:0.800000011920929;"/></g><g clip-path="url(#master_svg1_141_34750)"><path d="M10.5241249,13.861916515625001C10.4281249,13.975916815625,10.383124800000001,14.121916815625,10.3871248,14.299916315625001C10.3911247,14.477916715625,10.4491248,14.623916615625,10.5581248,14.737916015625C10.6671247,14.851916315625,10.8131247,14.908916515625,10.9961247,14.908916515625L12.3081245,14.908916515625C12.2621245,15.045916515625,12.235124599999999,15.177916515625,12.226124800000001,15.304916415625C12.2171249,15.432916615625,12.2171249,15.564916615625,12.226124800000001,15.700916315625C12.226124800000001,16.229916615625,12.2881246,16.728916015625,12.4111247,17.197916015624997C12.5341249,17.667916015625,12.705124900000001,18.102916015625,12.9241247,18.503916015625002L10.203125,18.503916015625002C9.7111249,18.485916015625,9.30112505,18.312916015625,8.97312498,17.983915015625C8.64512491,17.654915015625,8.472125053,17.245915015625002,8.453125,16.753916015625002L8.453125,8.263916015625C8.471125007,7.771915915625,8.64412498,7.359915975625,8.97312498,7.026916025625C9.30112505,6.693916085625,9.7111249,6.522916078525,10.203125,6.513916015625L17.8181248,6.513916015625C18.3101244,6.522915959325,18.722125,6.693916085625,19.055125,7.026916025625C19.388125000000002,7.359915975625,19.559125,7.771915915625,19.568126,8.263916015625L19.568126,10.533916015625C19.304126,10.405916215625,19.010126,10.314916115625,18.686126,10.260915715625C18.3621254,10.205915915624999,18.0461254,10.183915615625,17.736125899999998,10.192915915625L17.640126199999997,10.192915915625C17.5941267,10.101915815625,17.5331259,10.033916015625,17.455125799999998,9.987916015625C17.3771257,9.941916015625,17.293126100000002,9.919916115625,17.2021255,9.919916115625L10.818125,9.919916115625C10.6811249,9.919916115625,10.5601251,9.965916115625,10.456125,10.056916215625C10.351125,10.147916315625,10.299125,10.279916315625,10.299125,10.452916115625C10.299125,10.625916015625,10.351125,10.760916215624999,10.456125,10.855916015624999C10.561125,10.951916215625001,10.6821251,11.021915915625,10.818125,11.067915915625L14.851124800000001,11.067915915625C14.322125,11.368916015625,13.864124799999999,11.737916015625,13.477124700000001,12.174915815624999C13.0891247,12.612916015625,12.7871246,13.117916115625,12.5681248,13.692915915625L10.996125,13.692915915625C10.7771249,13.690916015625,10.6201251,13.747916215625,10.5241249,13.861916515625001ZM20.327126,13.061916315625C21.024124999999998,13.726916315625001,21.387126000000002,14.574916815625,21.414125,15.604916615625C21.387124999999997,16.643917015625,21.024124999999998,17.509916015625002,20.327126,18.202917015624998C19.630126,18.895918015625,18.766126,19.255917015625002,17.736125,19.282917015625C16.697125399999997,19.255917015625002,15.8311253,18.886917015625002,15.1381254,18.175917015625C14.4451256,17.464916015625,14.085125399999999,16.607917015625,14.0581255,15.605917015625C14.085125399999999,14.621916815625,14.4451256,13.784916915625,15.1381254,13.096917115625C15.8311253,12.408916915625,16.697125399999997,12.050917115625001,17.736125,12.023917215625C18.766125000000002,12.050917115625001,19.630125,12.396917315625,20.327126,13.061916315625ZM20.020125,16.671916015625C20.120126,16.571916015625,20.170125,16.452916115625,20.170125,16.316916515625C20.170125,16.180916815625,20.120125,16.059916515624998,20.020125,15.954916015625C19.920125,15.849916415625,19.801126,15.792916315625,19.665126,15.783916515625L17.8191261,15.783916515625L17.8191261,13.854915615625C17.8191261,13.717915515625,17.7661257,13.599915515625,17.6621265,13.499916115625C17.557127,13.399915715625,17.436126700000003,13.349916415625,17.3001261,13.349916415625C17.1641254,13.349916415625,17.0421257,13.399916615625,16.9381266,13.499916115625C16.833126999999998,13.599916415625,16.7761269,13.718915915625,16.767127000000002,13.854915615625L16.767127000000002,16.315916015625C16.7761269,16.452916115625,16.833126999999998,16.570916015625002,16.9381266,16.670916015625C17.043126100000002,16.770916015624998,17.1641264,16.825915015625,17.3001261,16.834915015625L19.665126,16.834915015625C19.801126,16.826915015624998,19.919126,16.771915015624998,20.020125,16.671916015625Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/shengchanhesuan.svg b/src/static/images/icon/shengchanhesuan.svg
new file mode 100644
index 0000000..ca37231
--- /dev/null
+++ b/src/static/images/icon/shengchanhesuan.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34449"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34776"><rect x="6" y="5" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34449)"><path d="M8.6032233,27C7.2891606,27,6.33216852,28.2456321,6.67075348,29.5153255L7.2081735,31.5306506C7.6751106,33.2816644,9.260909999999999,34.5,11.0731134,34.5L16.926887999999998,34.5C18.739091000000002,34.5,20.324889,33.2816644,20.791826,31.5306506L21.329247000000002,29.5153253C21.667831,28.2456321,20.710839,27,19.396776,27L8.6032233,27Z" fill="#FC99E5" fill-opacity="1" transform="matrix(1,0,0,-1,0,54)"/><path d="M2,8.000001000000001L2,17.499999000000003C2,20.813709,4.6862917,23.5,8.000000499999999,23.5L20,23.5C23.313709,23.5,26,20.813709,26,17.5L26,8.000001000000001C26,4.6862917,23.313709,2,20,2L8.000001000000001,2C4.6862917,2,2,4.6862917,2,8.000001000000001Z" fill="#6234F9" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_34776)"><path d="M19.536021859374998,6.0341796875L15.876568759375,6.0341796875C15.067975059375,6.0341796875,14.412474659375,6.6896797375,14.412474659375,7.4982734875L14.412474659375,11.157726287500001C14.412474659375,11.9663200875,15.067975059375,12.621820487499999,15.876568759375,12.621820487499999L19.536021859374998,12.621820487499999C20.344615859375,12.621820487499999,21.000115859375,11.9663200875,21.000115859375,11.157726287500001L21.000115859375,7.4982890875C21.000131859375,6.6896796275,20.344632859375,6.03417956829071,19.536021859374998,6.0341796875ZM19.634476859375,9.7672736875L15.778131459375,9.7672736875C15.535570159375,9.7672736875,15.338896759375,9.5706016875,15.338896759375,9.3280391875C15.338896759375,9.0854770875,15.535568259375,8.888804887500001,15.778131459375,8.888804887500001L19.634475859375,8.888804887500001C19.877036859375,8.888804887500001,20.073709859375,9.0854770875,20.073709859375,9.3280391875C20.073693859375,9.5706016875,19.877037859375,9.7672736875,19.634476859375,9.7672736875ZM19.525537859375,13.4172105875L15.873585659375,13.4172105875C15.066647559375,13.4172105875,14.412490859375,14.0713681875,14.412490859375,14.8783044875L14.412490859375,18.5303366875C14.412490859375,19.3372726875,15.066647559375,19.9914306875,15.873585659375,19.9914306875L19.525553859375,19.9914306875C20.332491859375,19.9914306875,20.986648859375002,19.3372726875,20.986648859375002,18.5303366875L20.986648859375002,14.8783197875C20.986648859375002,14.0713672875,20.332491859375,13.4172105875,19.525537859375,13.4172105875ZM17.698413859375002,14.9017886875L17.699912859375,14.9017886875C17.941976859375,14.9017886875,18.138240859375,15.0980548875,18.138240859375,15.3401164875C18.138240859375,15.5821790875,17.941976859375,15.7784452875,17.699912859375,15.7784452875C17.457850859375,15.7784452875,17.260866859375,15.5821790875,17.260866859375,15.3401164875C17.260866859375,15.0980548875,17.456350859375,14.9017886875,17.698413859375002,14.9017886875ZM17.699912859375,18.526601687499998C17.457850859375,18.526601687499998,17.260866859375,18.3303366875,17.260866859375,18.0882736875C17.260866859375,17.846211687500002,17.456350859375,17.6499456875,17.698413859375002,17.6499456875L17.699912859375,17.6499456875C17.941976859375,17.6499456875,18.138240859375,17.846211687500002,18.138240859375,18.0882736875C18.138240859375,18.3303366875,17.941976859375,18.526601687499998,17.699912859375,18.526601687499998ZM19.623787859375,17.1426626875L15.775351559375,17.1426626875C15.533288959375,17.1426626875,15.337023759375,16.9463986875,15.337023759375,16.7043356875C15.337023759375,16.4622726875,15.533288959375,16.266008687499998,15.775351559375,16.266008687499998L19.623787859375,16.266008687499998C19.865851859375,16.266008687499998,20.062116859375,16.4622726875,20.062116859375,16.7043356875C20.062116859375,16.9463986875,19.865851859375,17.1426626875,19.623787859375,17.1426626875ZM12.146208759375,6.0341796875L8.492740259375001,6.0341796875C7.685443399375,6.0341796875,7.031005859375,6.6886171675,7.031005859375,7.4959140875L7.031005859375,11.1494612875C7.031005859375,11.9567579875,7.685443339375,12.6111950875,8.492740259375001,12.6111950875L12.146209259375,12.6111950875C12.953506459375,12.6111950875,13.607944059375,11.9567579875,13.607944059375,11.1494612875L13.607944059375,7.4959140875C13.607944059375,6.6886172275,12.953506459375,6.0341796875,12.146208759375,6.0341796875ZM12.244490159375001,9.7612421875L10.757990359375,9.7612421875L10.757990359375,11.2477421875C10.757990359375,11.4899139875,10.561646659375,11.6862578875,10.319474959375,11.6862578875C10.077303159374999,11.6862578875,9.880959259375,11.4899139875,9.880959259375,11.2477421875L9.880959259375,9.7612421875L8.394459259375001,9.7612421875C8.152287459375,9.7612421875,7.955943529375,9.5648979875,7.955943529375,9.3227262875C7.955943529375,9.0805546875,8.152287259375,8.884210787499999,8.394459259375001,8.884210787499999L9.880959259375,8.884210787499999L9.880959259375,7.3977107875C9.880959259375,7.1555389875,10.077303159374999,6.9591950775,10.319474959375,6.9591950775C10.561646659375,6.9591950775,10.757990359375,7.1555387875,10.757990359375,7.3977107875L10.757990359375,8.884210787499999L12.244490159375001,8.884210787499999C12.486661959374999,8.884210787499999,12.683006259375,9.0805546875,12.683006259375,9.3227262875C12.683022059375,9.5648979875,12.486661959374999,9.7612421875,12.244490159375001,9.7612421875ZM12.154397059375,13.4631013875L8.521380759374999,13.4631013875C7.718615169375,13.4631013875,7.067833904375,14.1138667875,7.067833904375,14.9166488875L7.067833904375,18.549820687500002C7.067833904375,19.3525866875,7.718615169375,20.003366687499998,8.521380759374999,20.003366687499998L12.154397059375,20.003366687499998C12.957162859375,20.003366687499998,13.607944059375,19.3525856875,13.607944059375,18.549820687500002L13.607944059375,14.9166488875C13.607944059375,14.1138820875,12.957162859375,13.4631013875,12.154397059375,13.4631013875ZM11.999740159375001,17.7785396875C12.170084459375001,17.9488036875,12.170084459375001,18.2248996875,11.999740159375001,18.3951646875C11.914646659375,18.4803366875,11.803005659375,18.5229136875,11.691427659375,18.5229136875C11.579850159374999,18.5229136875,11.468209259375,18.4803366875,11.383115259375,18.3951646875L10.337849859375,17.3498996875L9.292584159375,18.3951646875C9.207490659375,18.4803366875,9.095849559375,18.5229136875,8.984271759375,18.5229136875C8.872693759375,18.5229136875,8.761053059375,18.4803366875,8.675959359375,18.3951646875C8.505615459375,18.2248996875,8.505615459375,17.9488036875,8.675959359375,17.7785396875L9.721224759375,16.733273687500002L8.675959359375,15.6880092875C8.505615459375,15.5177430875,8.505615459375,15.2416486875,8.675959359375,15.0713843875C8.846162559375,14.9010400875,9.122381259375,14.9010400875,9.292584659375,15.0713843875L10.337850359375,16.1166486875L11.383115759375,15.0713843875C11.553318959375,14.9010400875,11.829538359375,14.9010400875,11.999741059375001,15.0713843875C12.170084959375,15.2416486875,12.170084959375,15.5177430875,11.999741059375001,15.6880092875L10.954475159375,16.733273687500002L11.999740159375001,17.7785396875Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/shengchanjihua.svg b/src/static/images/icon/shengchanjihua.svg
new file mode 100644
index 0000000..3d73a8c
--- /dev/null
+++ b/src/static/images/icon/shengchanjihua.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34460"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34770"><rect x="6.5" y="4" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34460)"><path d="M25,21.1513879C25,21.3787005,25.067284852,21.6009274,25.19337524,21.7900629L27.1094005,24.6641006C27.6657977,25.4986968,28.6024919,26,29.6055508,26L37,26C38.656855,26,40,24.656854199999998,40,23C40,21.343146,38.656855,20,37,20L26.1513879,20C25.51549393,20,25,20.51549393,25,21.1513879Z" fill="#FFDB42" fill-opacity="1" transform="matrix(-1,0,0,1,50,0)"/><rect x="2" y="2" width="25" height="21" rx="6" fill="#FF7B00" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_34770)"><path d="M19.6875,17.8359375L9.3125,17.8359375C8.59623432,17.8359375,8.015625,17.255328499999997,8.015625,16.5390625L8.015625,9.40625L20.984375,9.40625L20.984375,16.5390625C20.984375,17.255328499999997,20.403765999999997,17.8359375,19.6875,17.8359375ZM16.4453125,11.3515625C16.4453125,10.9934998,16.154999699999998,10.703125,15.796875,10.703125C15.7324848,10.703125,13.2675157,10.703125,13.203125,10.703125C12.8450623,10.703125,12.5546875,10.9934998,12.5546875,11.3515625C12.5546875,11.7096877,12.8450623,12,13.203125,12C13.2675157,12,13.4754062,12,13.5273438,12L14.9863281,12L13.203125,15.5664063C13.203125,15.7454996,13.348312400000001,15.890625,13.5273438,15.890625L14.1757812,15.890625C14.354875100000001,15.890625,14.5,15.7454996,14.5,15.5664063L16.4453125,11.6757813C16.4453125,11.6238437,16.4453125,11.4159532,16.4453125,11.3515625ZM8.015625,8.109375C8.015625,7.3931093,8.59623432,6.8125,9.3125,6.8125L10.6747971,6.8125C10.8093438,6.4365938,11.1598282,6.1640625,11.5820312,6.1640625C12.0042343,6.1640625,12.3547029,6.4365938,12.4892654,6.8125L16.5107498,6.8125C16.6452971,6.4365938,16.9957819,6.1640625,17.417984,6.1640625C17.8401871,6.1640625,18.190656,6.4365938,18.325219,6.8125L19.6875,6.8125C20.403765999999997,6.8125,20.984375,7.3931093,20.984375,8.109375L20.984375,8.7578125L8.015625,8.7578125L8.015625,8.109375ZM17.09375,8.109375L17.7421875,8.109375L17.7421875,6.8125L17.09375,6.8125L17.09375,8.109375ZM11.2578125,8.109375L11.90625,8.109375L11.90625,6.8125L11.2578125,6.8125L11.2578125,8.109375Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/shengchanpaichan.svg b/src/static/images/icon/shengchanpaichan.svg
new file mode 100644
index 0000000..06ee178
--- /dev/null
+++ b/src/static/images/icon/shengchanpaichan.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_34407"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_34756"><rect x="9" y="7" width="14" height="14" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_34407)"><path d="M1,10L1,18C1,20.209139,2.7908611,22,5.000001,22L6.9999995,22C8.6568542,22,10,20.656855,10,19L10,9.0000005C10,7.3431458,8.6568542,6,6.9999995,6L5.0000005,6C2.7908611,6,1,7.7908611,1,10Z" fill="#7BF1FF" fill-opacity="1"/><rect x="5" y="2" width="22" height="24" rx="6" fill="#0066FF" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_34756)"><path d="M20.666666,7L11.3333335,7C10.0422223,7,9,8.0422223,9,9.3333335L9,18.666667C9,19.957777999999998,10.0422223,21,11.3333335,21L20.666667,21L19.888889,21L19.904445000000003,20.984445C20.106667,20.984445,20.308888,20.906667,20.464444999999998,20.758889L22.782222,18.441112C22.945556,18.277779000000002,23.015556,18.052222,23,17.834443999999998L23,9.3333335C23,8.0422223,21.957777999999998,7,20.666666,7ZM12.8888891,10.1111112L19.111111,10.1111112C19.538888999999998,10.1111112,19.888889,10.461111299999999,19.888889,10.8888891C19.888889,11.3166666,19.538888999999998,11.666667,19.111111,11.666667L12.8888891,11.666667C12.461111299999999,11.666667,12.1111114,11.3166666,12.1111114,10.8888891C12.1111114,10.4611115,12.4611115,10.1111114,12.8888891,10.1111112ZM12.1111112,14C12.1111112,13.572222199999999,12.461111299999999,13.2222223,12.8888891,13.2222223L19.111111,13.2222223C19.538888999999998,13.2222223,19.888889,13.572222199999999,19.888889,14C19.888889,14.427777800000001,19.538888999999998,14.777778099999999,19.111111,14.777778099999999L12.8888891,14.777778099999999C12.461111299999999,14.777778099999999,12.1111114,14.427778199999999,12.1111112,14ZM20.666666,17.888889L19.888888,17.888889L19.888888,18.666667C19.888888,19.094445,19.538888999999998,19.444446,19.11111,19.444446C18.6833315,19.444446,18.3333321,19.094445999999998,18.3333321,18.666667L18.3333321,17.111112C18.3333321,16.6833344,18.6833315,16.333334,19.11111,16.333334L20.666666,16.333334C21.094443,16.333334,21.444444,16.683333400000002,21.444444,17.111112C21.444444,17.538890000000002,21.094444,17.88889,20.666666,17.888889Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/shengchanshikuang.svg b/src/static/images/icon/shengchanshikuang.svg
new file mode 100644
index 0000000..b97e4f1
--- /dev/null
+++ b/src/static/images/icon/shengchanshikuang.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_42939"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_42954"><rect x="6" y="5" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_42939)"><path d="M22,22.0000005L22,22.9999995C22,25.2091389,23.7908611,27,26,27L34,27C36.209139,27,38,25.2091389,38,22.9999995L38,22.0000005C38,20.3431458,36.656855,19,35,19L25.0000005,19C23.3431458,19,22,20.3431458,22,22.0000005Z" fill="#FDC3C1" fill-opacity="1" transform="matrix(-1,0,0,1,44,0)"/><rect x="2" y="2" width="24" height="22" rx="6" fill="#FF2B00" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_42954)"><path d="M19.5976818125,7.975256428125C19.429908812500003,7.822927328125,19.2089708125,7.739575828125,18.980501812500002,7.739575828125C18.9596658125,7.739575828125,18.9388328125,7.739575828125,18.9180168125,7.741015028125C18.9054158125,7.742438228125,18.759929812499998,7.7511042281249996,18.5325648125,7.7511042281249996C18.1557318125,7.7511042281249996,17.4200373125,7.725233628125,16.7231331125,7.560001828125C15.823278412499999,7.345890628125,14.7847490125,6.354385998125,14.4826178125,6.158966188125C14.3303046125,6.059849266125,14.1550002125,6.010986328125,13.9793429125,6.010986328125C13.8044057125,6.010986328125,13.6290998125,6.059849266125,13.4771390125,6.157543068125C13.4405084125,6.181974528125,12.3530998125,7.3070687281249995,11.2894523125,7.560001828125C10.5929158125,7.725233928125,9.8431994125,7.7511042281249996,9.4667181125,7.7511042281249996C9.2393200125,7.7511042281249996,9.0942183125,7.742438228125,9.0798602125,7.741015028125C9.0600977525,7.739575828125,9.0403350025,7.739575828125,9.0202204625,7.739575828125C8.7913834425,7.739575828125,8.5697414925,7.822911628125,8.4012634725,7.975256428125C8.2184271925,8.140488628125,8.1142578125,8.371819928125,8.1142578125,8.614663628125001L8.1142578125,10.803070528125C8.1142578125,18.892904328125,13.5874648125,19.934664328125002,13.8194995125,19.976283328125C13.8726792125,19.984965328125,13.926195612499999,19.989251328125,13.9797263125,19.989251328125C14.0328917125,19.989251328125,14.0871253125,19.984965328125,14.1395697125,19.976283328125C14.3719735125,19.934649328124998,19.8853928125,18.892889328125,19.8853928125,10.803071028125L19.8853928125,8.614663628125001C19.8853928125,8.371819928125,19.7808728125,8.140504328125001,19.5976818125,7.975256428125ZM17.5744924125,11.104817828125L13.779988812500001,14.776156428125C13.756276612499999,14.814913728125,13.7271771125,14.853767428125,13.6919684125,14.886800728125C13.580604512499999,14.994567828125,13.4333286125,15.046293228125,13.287458412500001,15.043415028125C13.1416373125,15.046293228125,12.9946804125,14.994567828125,12.8833489125,14.886800728125C12.8481564125,14.853767428125,12.8187046125,14.814913728125,12.7949929125,14.776156428125L10.7671649125,12.813325428125001C10.5512478125,12.604953728125,10.5512478125,12.267311128125,10.7671649125,12.057500328125C10.9830506125,11.849193528125,11.3332775125,11.849193528125,11.549195812499999,12.057500328125L13.287475112500001,13.740152828125L16.7928295125,10.349056728125C17.0087471125,10.140685528125001,17.3586063125,10.140685528125001,17.5745096125,10.349056728125C17.7903948125,10.557348228125,17.7903948125,10.896494828125,17.5744924125,11.104817828125Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/static/images/icon/shengchanzhuisu.svg b/src/static/images/icon/shengchanzhuisu.svg
new file mode 100644
index 0000000..8e70971
--- /dev/null
+++ b/src/static/images/icon/shengchanzhuisu.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="28" height="28" viewBox="0 0 28 28"><defs><clipPath id="master_svg0_141_42929"><rect x="0" y="0" width="28" height="28" rx="0"/></clipPath><clipPath id="master_svg1_141_42949"><rect x="6" y="8" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_141_42929)"><path d="M6,13.0000005L6,15.590147C6,16.842423,7.1377500000000005,17.7866721,8.368577,17.555892L14,16.5L19.631422999999998,17.555892C20.86225,17.7866721,22,16.842423,22,15.590147L22,13.0000005C22,11.3431458,20.656855,10,19,10L9.0000005,10C7.3431458,10,6,11.3431458,6,13.0000005Z" fill="#FC99E5" fill-opacity="1" transform="matrix(1,0,0,-1,0,20)"/><rect x="2" y="5" width="24" height="22" rx="6" fill="#6234F9" fill-opacity="1" style="opacity:0.800000011920929;"/><g clip-path="url(#master_svg1_141_42949)"><path d="M19.743700671875,9.612548828125L8.276513691875,9.612548828125C7.901513691875,9.612548828125,7.598388671875,9.915673848125,7.598388671875,10.290673968125L7.598388671875,21.709423828125C7.598388671875,22.082861828125,7.901513691875,22.385986828125,8.276513691875,22.385986828125L19.204639671875,22.385986828125L18.021826671875,21.204736828125C17.992138671875,21.175048828125,17.967139671875,21.143798828125,17.943701671875,21.109424828125C17.193701771875,21.618798828125,16.309327171874997,21.892236828125,15.381201771875,21.892236828125C14.159326571874999,21.892236828125,13.010889071874999,21.415673828125,12.146826771875,20.551611828125C11.282764471875,19.687549828125,10.806201971875,18.539112128124998,10.806201971875,17.317236928125C10.806201971875,16.095361728125,11.282764471875,14.946924228124999,12.146826771875,14.082861928125C13.010889071874999,13.218799828125,14.159326571874999,12.742237328125,15.381201771875,12.742237328125C16.603076971874998,12.742237328125,17.751514671875,13.218799828125,18.617138671875,14.082861928125C19.481200671875,14.946924228124999,19.957763671875,16.095361728125,19.957763671875,17.317236928125C19.957763671875,18.417237328124997,19.571825671875,19.456299828124997,18.865575671875,20.282861828125C18.879638671875,20.293799828125,18.892138671875,20.306298828125,18.906200671875,20.318798828125L20.410887671875003,21.823486828125C20.417137671875,21.785985828125,20.421825671875,21.746923828125,20.421825671875,21.707860828125L20.421825671875,10.289111378125C20.420263671875,9.915673848125,20.117137671875,9.612548828125,19.743700671875,9.612548828125ZM12.195263871875,12.225048828125C12.082763671875,12.337548928124999,11.926513671875,12.407861428124999,11.753076571874999,12.407861428124999L9.846826571874999,12.407861428124999C9.501513871875,12.407861428124999,9.221826471875,12.128174028125,9.221826471875,11.782861428124999C9.221826471875,11.610986428124999,9.292138971875,11.454736428124999,9.404638871875001,11.340673928125C9.517138871875,11.226611328125,9.673388971875,11.157861428124999,9.846826571874999,11.157861428124999L11.754639171874999,11.157861428124999C12.099951771875,11.157861428124999,12.379639171874999,11.437548828125,12.379639171874999,11.782861428124999C12.378076571874999,11.956299028125,12.309326671874999,12.112549028125,12.195263871875,12.225048828125ZM17.787451671875,15.867236128125L18.546826671875,15.867236128125C17.995263671875,14.670361528125,16.784326571875,13.837549228124999,15.382763871875,13.837549228124999C13.464014071874999,13.837549228124999,11.901514071874999,15.400049228124999,11.901514071874999,17.318799028125C11.901514071874999,19.239111928125,13.464014071874999,20.800048828125,15.382763871875,20.800048828125C16.682764071875,20.800048828125,17.818701671875,20.082860828125,18.417138671875,19.025049228125L17.787451671875,19.025049228125C17.442139671874997,19.025049228125,17.162451771875,18.745361328125,17.162451771875,18.400049228125L17.162451771875,16.492236128125C17.162451771875,16.146924028125,17.442139671874997,15.867236128125,17.787451671875,15.867236128125ZM15.045264271875,18.398486128125C15.045264271875,18.743798228125,14.765576371875,19.023486128125,14.420264271875,19.023486128125C14.248389271875,19.023486128125,14.092139271875,18.953173628125,13.978076971875,18.840673428125C13.865576771875,18.728173228125,13.795264271875,18.571923228125,13.795264271875,18.398486128125L13.795264271875,16.492236128125C13.795264271875,16.146924028125,14.074952171875001,15.867236128125,14.420264271875,15.867236128125C14.592139271875,15.867236128125,14.748389271875,15.937548628125,14.862451571874999,16.050048828125C14.974951771875,16.162549028125,15.045264271875,16.318799028125,15.045264271875,16.492236128125L15.045264271875,18.398486128125ZM16.787451771875,18.398486128125C16.787451771875,18.743798228125,16.507763871875,19.023486128125,16.162451771875,19.023486128125C15.990576771875,19.023486128125,15.834326771875,18.953173628125,15.720264471875,18.840673428125C15.606202171875,18.728173228125,15.537451771875,18.571923228125,15.537451771875,18.398486128125L15.537451771875,16.492236128125C15.537451771875,16.146924028125,15.817139671875,15.867236128125,16.162451771875,15.867236128125C16.334326771875,15.867236128125,16.490576771875,15.937548628125,16.604639071875,16.050048828125C16.717139271875,16.162549028125,16.787451771875,16.318799028125,16.787451771875,16.492236128125L16.787451771875,18.398486128125Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/src/utils/versionUpgrade.js b/src/utils/versionUpgrade.js
index 680b794..b7eff6c 100644
--- a/src/utils/versionUpgrade.js
+++ b/src/utils/versionUpgrade.js
@@ -1,6 +1,7 @@
import config from "@/config";
import { getAllVersion } from "@/api/viewIndex";
import bus from "@/plugins/bus";
+let hasTriggeredVersionCheckInSession = false;
function compareVersion(v1, v2) {
const s1 = String(v1 || "").replace(/[^\d.]/g, "").split(".").map((n) => Number(n) || 0);
@@ -264,12 +265,17 @@
let lastVersionCheckAt = 0;
const triggerVersionCheck = async (from = "unknown") => {
+ if (hasTriggeredVersionCheckInSession) {
+ console.log(`${logPrefix} 璺宠繃鐗堟湰妫�鏌ワ紝鏈浼氳瘽宸叉娴嬭繃锛屾潵婧�=${from}`);
+ return;
+ }
const now = Date.now();
if (now - lastVersionCheckAt < throttleMs) {
console.log(`${logPrefix} 璺宠繃閲嶅妫�鏌ワ紝鏉ユ簮=${from}`);
return;
}
lastVersionCheckAt = now;
+ hasTriggeredVersionCheckInSession = true;
console.log(`${logPrefix} 瑙﹀彂鐗堟湰妫�鏌ワ紝鏉ユ簮=${from}`);
const currentVersion = await getCurrentVersion(logPrefix);
// await checkAppVersionUpgrade(logPrefix, currentVersion);
--
Gitblit v1.9.3