From 6e1b96ef02d45d7be6f3c0117a212ba963098886 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期四, 16 四月 2026 16:26:10 +0800
Subject: [PATCH] Merge branch 'dev_河南_鹤壁天沐玻璃厂' of http://114.132.189.42:9002/r/product-inventory-management into dev_河南_鹤壁天沐玻璃厂
---
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue | 783 +-
src/views/procurementManagement/procurementLedger/index.vue | 270
src/api/collaborativeApproval/approvalProcess.js | 24
multiple/config.json | 4
src/views/basicData/product/index.vue | 1041 ++--
src/views/collaborativeApproval/approvalProcess/index.vue | 945 ++-
src/views/salesManagement/salesLedger/index.vue | 7716 +++++++++++++++++----------------
src/views/salesManagement/salesQuotation/index.vue | 2086 ++++----
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue | 764 +-
9 files changed, 7,327 insertions(+), 6,306 deletions(-)
diff --git a/multiple/config.json b/multiple/config.json
index e74556e..ec9c19d 100644
--- a/multiple/config.json
+++ b/multiple/config.json
@@ -10,8 +10,8 @@
"HBTM": {
"env": {
"VITE_APP_TITLE": "楣ゅ澶╂矏淇℃伅绠$悊绯荤粺",
- "VITE_BASE_API": "http://36.133.46.107:9030",
- "VITE_JAVA_API": "http://36.133.46.107:9031"
+ "VITE_BASE_API": "http://1.15.17.182:9028",
+ "VITE_JAVA_API": "http://1.15.17.182:9029"
},
"screen": "screen/login-background.png",
"logo": "logo/Logo.png",
diff --git a/src/api/collaborativeApproval/approvalProcess.js b/src/api/collaborativeApproval/approvalProcess.js
index 415bed8..8432626 100644
--- a/src/api/collaborativeApproval/approvalProcess.js
+++ b/src/api/collaborativeApproval/approvalProcess.js
@@ -60,4 +60,28 @@
url: '/approveNode/details/' + query,
method: 'get',
})
+}
+// 缁存姢瀹℃壒浜烘柊澧�-鏇存柊
+export function addApproveUser(query) {
+ return request({
+ url: '/approveUser/add',
+ method: 'post',
+ data: query,
+ })
+}
+// 鍒犻櫎瀹℃壒浜�
+export function deleteApproveUser(query) {
+ return request({
+ url: '/approveUser/del',
+ method: 'delete',
+ data: query,
+ })
+}
+// 鏌ヨ瀹℃壒浜�
+export function approveUserList(query) {
+ return request({
+ url: '/approveUser/getList',
+ method: 'get',
+ params: query,
+ })
}
\ No newline at end of file
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index e8b0d0b..a74a294 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -2,38 +2,31 @@
<div class="app-container product-view">
<div class="left">
<div>
- <el-input
- v-model="search"
- style="width: 210px"
- placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�"
- @input="searchFilter"
- @change="searchFilter"
- @clear="searchFilter"
- clearable
- prefix-icon="Search"
- />
- <el-button
- type="primary"
- @click="openProDia('addOne')"
- style="margin-left: 10px"
- >鏂板浜у搧澶х被</el-button
- >
+ <el-input v-model="search"
+ style="width: 210px"
+ placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�"
+ @input="searchFilter"
+ @change="searchFilter"
+ @clear="searchFilter"
+ clearable
+ prefix-icon="Search" />
+ <el-button type="primary"
+ @click="openProDia('addOne')"
+ style="margin-left: 10px">鏂板浜у搧澶х被</el-button>
</div>
<div ref="containerRef">
- <el-tree
- ref="tree"
- v-loading="treeLoad"
- :data="list"
- @node-click="handleNodeClick"
- :expand-on-click-node="false"
- :default-expanded-keys="expandedKeys"
- :filter-node-method="filterNode"
- :props="{ children: 'children', label: 'label' }"
- highlight-current
- node-key="id"
- class="product-tree-scroll"
- style="height: calc(100vh - 190px); overflow-y: auto"
- >
+ <el-tree ref="tree"
+ v-loading="treeLoad"
+ :data="list"
+ @node-click="handleNodeClick"
+ :expand-on-click-node="false"
+ :default-expanded-keys="expandedKeys"
+ :filter-node-method="filterNode"
+ :props="{ children: 'children', label: 'label' }"
+ highlight-current
+ node-key="id"
+ class="product-tree-scroll"
+ style="height: calc(100vh - 190px); overflow-y: auto">
<template #default="{ node, data }">
<div class="custom-tree-node">
<span class="tree-node-content">
@@ -44,23 +37,22 @@
<span class="tree-node-label">{{ data.label }}</span>
</span>
<div>
- <el-button
- type="primary"
- link
- @click="openProDia('edit', data)"
- >
+ <el-button type="primary"
+ link
+ @click="openProDia('edit', data)">
缂栬緫
</el-button>
- <el-button type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3">
+ <el-button type="primary"
+ link
+ @click="openProDia('add', data)"
+ :disabled="node.level >= 3">
娣诲姞浜у搧
</el-button>
- <el-button
- v-if="!node.childNodes.length"
- style="margin-left: 4px"
- type="danger"
- link
- @click="remove(node, data)"
- >
+ <el-button v-if="!node.childNodes.length"
+ style="margin-left: 4px"
+ type="danger"
+ link
+ @click="remove(node, data)">
鍒犻櫎
</el-button>
</div>
@@ -70,117 +62,134 @@
</div>
</div>
<div class="right">
- <div style="margin-bottom: 10px" v-if="isShowButton">
- <el-button type="primary" @click="openModelDia('add')">
+ <div style="margin-bottom: 10px"
+ v-if="isShowButton">
+ <el-button type="primary"
+ @click="openModelDia('add')">
鏂板瑙勬牸鍨嬪彿
</el-button>
- <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" />
- <el-button
- type="danger"
- @click="handleDelete"
- style="margin-left: 10px"
- plain
- >
+ <ImportExcel :product-id="currentId"
+ @uploadSuccess="getModelList" />
+ <el-button type="danger"
+ @click="handleDelete"
+ style="margin-left: 10px"
+ plain>
鍒犻櫎
</el-button>
</div>
- <PIMTable
- rowKey="id"
- :column="tableColumn"
- :tableData="tableData"
- :page="page"
- :isSelection="true"
- @selection-change="handleSelectionChange"
- :tableLoading="tableLoading"
- @pagination="pagination"
- ></PIMTable>
+ <PIMTable rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"></PIMTable>
</div>
- <el-dialog v-model="productDia" title="浜у搧" width="400px" @keydown.enter.prevent>
- <el-form
- :model="form"
- label-width="140px"
- label-position="top"
- :rules="rules"
- ref="formRef"
- >
+ <el-dialog v-model="productDia"
+ title="浜у搧"
+ width="400px"
+ @keydown.enter.prevent>
+ <el-form :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="浜у搧鍚嶇О锛�" prop="productName">
- <el-input
- v-model="form.productName"
- placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
- maxlength="20"
- show-word-limit
- clearable
- @keydown.enter.prevent
- />
+ <el-form-item label="浜у搧鍚嶇О锛�"
+ prop="productName">
+ <el-input v-model="form.productName"
+ placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+ maxlength="20"
+ show-word-limit
+ clearable
+ @keydown.enter.prevent />
</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="closeProDia">鍙栨秷</el-button>
</div>
</template>
</el-dialog>
- <el-dialog
- v-model="modelDia"
- title="瑙勬牸鍨嬪彿"
- width="400px"
- @close="closeModelDia"
- @keydown.enter.prevent
- >
- <el-form
- :model="modelForm"
- label-width="140px"
- label-position="top"
- :rules="modelRules"
- ref="modelFormRef"
- >
+ <el-dialog v-model="modelDia"
+ title="瑙勬牸鍨嬪彿"
+ width="400px"
+ @close="closeModelDia"
+ @keydown.enter.prevent>
+ <el-form :model="modelForm"
+ label-width="140px"
+ label-position="top"
+ :rules="modelRules"
+ ref="modelFormRef">
<el-row>
<el-col :span="24">
- <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="model">
- <el-input
- v-model="modelForm.model"
- placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
- clearable
- @keydown.enter.prevent
- />
+ <el-form-item label="瑙勬牸鍨嬪彿锛�"
+ prop="model">
+ <el-input v-model="modelForm.model"
+ placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+ clearable
+ @keydown.enter.prevent />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
- <el-form-item label="鍘氬害锛�" prop="thickness">
- <el-input
- v-model="modelForm.thickness"
- placeholder="璇疯緭鍏ュ帤搴�"
- clearable
- @keydown.enter.prevent
- @blur="modelForm.thickness = formatThicknessTo15(modelForm.thickness)"
- />
+ <el-form-item label="鍘氬害锛�"
+ prop="thickness">
+ <el-input v-model="modelForm.thickness"
+ placeholder="璇疯緭鍏ュ帤搴�"
+ clearable
+ @keydown.enter.prevent
+ @blur="modelForm.thickness = formatThicknessTo15(modelForm.thickness)" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
- <el-form-item label="鍗曚綅锛�" prop="unit">
- <el-input
- v-model="modelForm.unit"
- placeholder="璇疯緭鍏ュ崟浣�"
- clearable
- @keydown.enter.prevent
- />
+ <el-form-item label="鍗曚綅锛�"
+ prop="unit">
+ <el-input v-model="modelForm.unit"
+ placeholder="璇疯緭鍏ュ崟浣�"
+ clearable
+ @keydown.enter.prevent />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
- <el-button type="primary" @click="submitModelForm">纭</el-button>
+ <el-button type="primary"
+ @click="submitModelForm">纭</el-button>
<el-button @click="closeModelDia">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ <!-- 浜岀淮鐮佸璇濇 -->
+ <el-dialog v-model="qrCodeDialog"
+ title="浜у搧浜岀淮鐮�"
+ width="400px">
+ <div class="qrcode-container">
+ <img v-if="qrCodeUrl"
+ :src="qrCodeUrl"
+ class="qrcode-image" />
+ <div v-else
+ class="loading">鐢熸垚涓�...</div>
+ </div>
+ <div style="text-align: center;">
+ {{ qrCodeName }}
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="qrCodeDialog = false">鍏抽棴</el-button>
+ <el-button type="primary"
+ @click="saveQrCodeAsImage"
+ :disabled="!qrCodeUrl">淇濆瓨涓哄浘鐗�</el-button>
</div>
</template>
</el-dialog>
@@ -188,379 +197,465 @@
</template>
<script setup>
-import { ref } from "vue";
-import { ElMessageBox } from "element-plus";
-import {
- addOrEditProduct,
- addOrEditProductModel,
- delProduct,
- delProductModel,
- modelListPage,
- productTreeList,
-} from "@/api/basicData/product.js";
-import ImportExcel from "./ImportExcel/index.vue";
+ import { ref, getCurrentInstance, toRefs, reactive } from "vue";
+ import { ElMessageBox } from "element-plus";
+ import QRCode from "qrcode";
+ import { saveAs } from "file-saver";
+ import {
+ addOrEditProduct,
+ addOrEditProductModel,
+ delProduct,
+ delProductModel,
+ modelListPage,
+ productTreeList,
+ } from "@/api/basicData/product.js";
+ import ImportExcel from "./ImportExcel/index.vue";
-const { proxy } = getCurrentInstance();
-const tree = ref(null);
-const containerRef = ref(null);
+ const { proxy } = getCurrentInstance();
+ const tree = ref(null);
+ const containerRef = ref(null);
-const productDia = ref(false);
-const modelDia = ref(false);
-const modelOperationType = ref("");
-const search = ref("");
-const currentId = ref("");
-const currentParentId = ref("");
-const operationType = ref("");
-const treeLoad = ref(false);
-const list = ref([]);
-const expandedKeys = ref([]);
-const tableColumn = ref([
- {
- label: "瑙勬牸鍨嬪彿",
- prop: "model",
- },
- {
- label: "鍘氬害",
- prop: "thickness",
- // 鍒楄〃灞曠ず鏃剁粺涓�淇濈暀 15 浣嶅皬鏁�
- formatData: (val) => formatThicknessTo15(val),
- },
- {
- label: "鍗曚綅",
- prop: "unit",
- },
- {
- dataType: "action",
- label: "鎿嶄綔",
- align: "center",
- operation: [
- {
- name: "缂栬緫",
- type: "text",
- clickFun: (row) => {
- openModelDia("edit", row);
+ const productDia = ref(false);
+ const modelDia = ref(false);
+ const qrCodeDialog = ref(false);
+ const qrCodeUrl = ref("");
+ const currentProductId = ref("");
+ const modelOperationType = ref("");
+ const search = ref("");
+ const currentId = ref("");
+ const currentParentId = ref("");
+ const operationType = ref("");
+ const treeLoad = ref(false);
+ const list = ref([]);
+ const expandedKeys = ref([]);
+ const tableColumn = ref([
+ {
+ label: "瑙勬牸鍨嬪彿",
+ prop: "model",
+ },
+ {
+ label: "鍘氬害",
+ prop: "thickness",
+ // 鍒楄〃灞曠ず鏃剁粺涓�淇濈暀 15 浣嶅皬鏁�
+ formatData: val => formatThicknessTo15(val),
+ },
+ {
+ label: "鍗曚綅",
+ prop: "unit",
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: row => {
+ openModelDia("edit", row);
+ },
},
- },
- ],
- },
-]);
-const tableData = ref([]);
-const tableLoading = ref(false);
-const isShowButton = ref(false);
-const selectedRows = ref([]);
-const page = reactive({
- current: 1,
- size: 10,
- total: 0,
-});
-const data = reactive({
- form: {
- productName: "",
- },
- rules: {
- productName: [
- { required: true, message: "璇疯緭鍏�", trigger: "blur" },
- { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
- ],
- },
- modelForm: {
- model: "",
- thickness: "",
- },
- modelRules: {
- model: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- thickness: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- },
-});
-const { form, rules, modelForm, modelRules } = toRefs(data);
-
-// 鎶婂帤搴︽牸寮忓寲鎴愬浐瀹� 15 浣嶅皬鏁帮紙鐢ㄤ簬灞曠ず/鎻愪氦锛�
-const formatThicknessTo15 = (val) => {
- if (val === null || val === undefined) return "";
- const s = String(val).trim();
- if (s === "") return "";
- const n = Number(s);
- if (Number.isNaN(n)) return s;
- return n.toFixed(15);
-};
-// 鏌ヨ浜у搧鏍�
-const getProductTreeList = () => {
- treeLoad.value = true;
- productTreeList()
- .then((res) => {
- list.value = res;
- list.value.forEach((a) => {
- expandedKeys.value.push(a.label);
- });
- treeLoad.value = false;
- })
- .catch((err) => {
- treeLoad.value = false;
- });
-};
-// 杩囨护浜у搧鏍�
-const searchFilter = () => {
- proxy.$refs.tree.filter(search.value);
-};
-// 鎵撳紑浜у搧寮规
-const openProDia = (type, data) => {
- operationType.value = type;
- productDia.value = true;
- form.value.productName = "";
- if (type === "edit") {
- form.value.productName = data.productName;
- }
-};
-// 鎵撳紑瑙勬牸鍨嬪彿寮规
-const openModelDia = (type, data) => {
- modelOperationType.value = type;
- modelDia.value = true;
- modelForm.value.model = "";
- modelForm.value.id = "";
- modelForm.value.thickness = "";
- if (type === "edit") {
- modelForm.value = { ...data };
- }
-};
-// 鎻愪氦浜у搧鍚嶇О淇敼
-const submitForm = () => {
- proxy.$refs.formRef.validate((valid) => {
- if (valid) {
- if (operationType.value === "add") {
- form.value.parentId = currentId.value;
- form.value.id = "";
- } else if (operationType.value === "addOne") {
- form.value.id = "";
- form.value.parentId = "";
- } else {
- form.value.id = currentId.value;
- form.value.parentId = "";
- }
- addOrEditProduct(form.value).then((res) => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeProDia();
- getProductTreeList();
- });
- }
+ {
+ name: "鐢熸垚浜岀淮鐮�",
+ type: "text",
+ clickFun: row => {
+ generateQrcode(row);
+ },
+ },
+ ],
+ },
+ ]);
+ const tableData = ref([]);
+ const tableLoading = ref(false);
+ const isShowButton = ref(false);
+ const selectedRows = ref([]);
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
});
-};
-// 鍏抽棴浜у搧寮规
-const closeProDia = () => {
- proxy.$refs.formRef.resetFields();
- productDia.value = false;
-};
+ const data = reactive({
+ form: {
+ productName: "",
+ },
+ rules: {
+ productName: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+ ],
+ },
+ modelForm: {
+ model: "",
+ thickness: "",
+ },
+ modelRules: {
+ model: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ thickness: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ },
+ });
+ const { form, rules, modelForm, modelRules } = toRefs(data);
-// 鍒犻櫎浜у搧
-const remove = (node, data) => {
- let ids = [];
- ids.push(data.id);
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- tableLoading.value = true;
- delProduct(ids)
- .then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ // 鎶婂帤搴︽牸寮忓寲鎴愬浐瀹� 15 浣嶅皬鏁帮紙鐢ㄤ簬灞曠ず/鎻愪氦锛�
+ const formatThicknessTo15 = val => {
+ if (val === null || val === undefined) return "";
+ const s = String(val).trim();
+ if (s === "") return "";
+ const n = Number(s);
+ if (Number.isNaN(n)) return s;
+ return n.toFixed(15);
+ };
+ // 鏌ヨ浜у搧鏍�
+ const getProductTreeList = () => {
+ treeLoad.value = true;
+ productTreeList()
+ .then(res => {
+ list.value = res;
+ list.value.forEach(a => {
+ expandedKeys.value.push(a.label);
+ });
+ treeLoad.value = false;
+ })
+ .catch(err => {
+ treeLoad.value = false;
+ });
+ };
+ // 杩囨护浜у搧鏍�
+ const searchFilter = () => {
+ proxy.$refs.tree.filter(search.value);
+ };
+ // 鎵撳紑浜у搧寮规
+ const openProDia = (type, data) => {
+ operationType.value = type;
+ productDia.value = true;
+ form.value.productName = "";
+ if (type === "edit") {
+ form.value.productName = data.productName;
+ }
+ };
+ // 鎵撳紑瑙勬牸鍨嬪彿寮规
+ const openModelDia = (type, data) => {
+ modelOperationType.value = type;
+ modelDia.value = true;
+ modelForm.value.model = "";
+ modelForm.value.id = "";
+ modelForm.value.thickness = "";
+ if (type === "edit") {
+ modelForm.value = { ...data };
+ }
+ };
+ // 鎻愪氦浜у搧鍚嶇О淇敼
+ const submitForm = () => {
+ proxy.$refs.formRef.validate(valid => {
+ if (valid) {
+ if (operationType.value === "add") {
+ form.value.parentId = currentId.value;
+ form.value.id = "";
+ } else if (operationType.value === "addOne") {
+ form.value.id = "";
+ form.value.parentId = "";
+ } else {
+ form.value.id = currentId.value;
+ form.value.parentId = "";
+ }
+ addOrEditProduct(form.value).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeProDia();
getProductTreeList();
- })
- .finally(() => {
- tableLoading.value = false;
});
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
+ }
});
-};
-// 閫夋嫨浜у搧
-const handleNodeClick = (val, node, el) => {
- // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐�
- isShowButton.value = !(val.children && val.children.length > 0);
- // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫
- currentId.value = val.id;
- currentParentId.value = val.parentId;
- getModelList();
-};
+ };
+ // 鍏抽棴浜у搧寮规
+ const closeProDia = () => {
+ proxy.$refs.formRef.resetFields();
+ productDia.value = false;
+ };
-// 鎻愪氦瑙勬牸鍨嬪彿淇敼
-const submitModelForm = () => {
- proxy.$refs.modelFormRef.validate((valid) => {
- if (valid) {
- modelForm.value.productId = currentId.value;
- modelForm.value.thickness = formatThicknessTo15(modelForm.value.thickness);
- addOrEditProductModel(modelForm.value).then((res) => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeModelDia();
- getModelList();
+ // 鍒犻櫎浜у搧
+ const remove = (node, data) => {
+ let ids = [];
+ ids.push(data.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ tableLoading.value = true;
+ delProduct(ids)
+ .then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getProductTreeList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
});
- }
- });
-};
-// 鍏抽棴鍨嬪彿寮规
-const closeModelDia = () => {
- proxy.$refs.modelFormRef.resetFields();
- modelDia.value = false;
-};
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
-};
+ };
+ const fatherName = ref("");
+ // 閫夋嫨浜у搧
+ const handleNodeClick = (val, node, el) => {
+ // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐�
+ isShowButton.value = !(val.children && val.children.length > 0);
+ // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫
+ currentId.value = val.id;
+ currentParentId.value = val.parentId;
+ fatherName.value = val.label;
+ getModelList();
+ };
-// 鏌ヨ瑙勬牸鍨嬪彿
-const pagination = (obj) => {
- page.current = obj.page;
- page.size = obj.limit;
- getModelList();
-};
-const getModelList = () => {
- tableLoading.value = true;
- modelListPage({
- id: currentId.value,
- current: page.current,
- size: page.size,
- }).then((res) => {
- console.log("res", res);
- tableData.value = res.records;
- page.total = res.total;
- tableLoading.value = false;
- });
-};
-// 鍒犻櫎瑙勬牸鍨嬪彿
-const handleDelete = () => {
- let ids = [];
- if (selectedRows.value.length > 0) {
- ids = selectedRows.value.map((item) => item.id);
- } else {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- tableLoading.value = true;
- delProductModel(ids)
- .then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ // 鎻愪氦瑙勬牸鍨嬪彿淇敼
+ const submitModelForm = () => {
+ proxy.$refs.modelFormRef.validate(valid => {
+ if (valid) {
+ modelForm.value.productId = currentId.value;
+ modelForm.value.thickness = formatThicknessTo15(
+ modelForm.value.thickness
+ );
+ addOrEditProductModel(modelForm.value).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeModelDia();
getModelList();
- })
- .finally(() => {
- tableLoading.value = false;
});
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
+ }
});
-};
-// 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
-const filterNode = (value, data, node) => {
- if (!value) {
- //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
- return true;
- }
- // 鍏抽敭瀛椾笌鑺傜偣 label 鍧囨寜灏忓啓姣旇緝锛岃嫳鏂囨ā绯婂尮閰嶄笉鍖哄垎澶у皬鍐欙紙濡� LOW / low 鍙尮閰� Low-E锛�
- const val = String(value).trim().toLowerCase();
- if (!val) return true;
- return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
-};
-// 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
-const chooseNode = (value, data, node) => {
- const labelLower = String(data.label ?? "").toLowerCase();
- if (labelLower.indexOf(value) !== -1) {
- return true;
- }
- const level = node.level;
- // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
- if (level === 1) {
- return false;
- }
- // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
- let parentData = node.parent;
- // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
- let index = 0;
- while (index < level - 1) {
- // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紱涓� filterNode 涓�鑷达紝鏍囩涓庡叧閿瓧鍧囨寜灏忓啓姣旇緝锛岃嫳鏂囨ā绯婂尮閰嶄笉鍖哄垎澶у皬鍐�
- const parentLabelLower = String(parentData.data.label ?? "").toLowerCase();
- if (parentLabelLower.indexOf(value) !== -1) {
+ };
+ // 鍏抽棴鍨嬪彿寮规
+ const closeModelDia = () => {
+ proxy.$refs.modelFormRef.resetFields();
+ modelDia.value = false;
+ };
+ // 琛ㄦ牸閫夋嫨鏁版嵁
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ // 鏌ヨ瑙勬牸鍨嬪彿
+ const pagination = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getModelList();
+ };
+ const getModelList = () => {
+ tableLoading.value = true;
+ modelListPage({
+ id: currentId.value,
+ current: page.current,
+ size: page.size,
+ }).then(res => {
+ console.log("res", res);
+ tableData.value = res.records;
+ page.total = res.total;
+ tableLoading.value = false;
+ });
+ };
+ // 鍒犻櫎瑙勬牸鍨嬪彿
+ const handleDelete = () => {
+ let ids = [];
+ if (selectedRows.value.length > 0) {
+ ids = selectedRows.value.map(item => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ tableLoading.value = true;
+ delProductModel(ids)
+ .then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getModelList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+ // 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
+ const filterNode = (value, data, node) => {
+ if (!value) {
+ //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
return true;
}
- // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
- parentData = parentData.parent;
- index++;
- }
- // 娌″尮閰嶅埌杩斿洖false
- return false;
-};
-getProductTreeList();
+ // 鍏抽敭瀛椾笌鑺傜偣 label 鍧囨寜灏忓啓姣旇緝锛岃嫳鏂囨ā绯婂尮閰嶄笉鍖哄垎澶у皬鍐欙紙濡� LOW / low 鍙尮閰� Low-E锛�
+ const val = String(value).trim().toLowerCase();
+ if (!val) return true;
+ return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
+ };
+ // 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
+ const chooseNode = (value, data, node) => {
+ const labelLower = String(data.label ?? "").toLowerCase();
+ if (labelLower.indexOf(value) !== -1) {
+ return true;
+ }
+ const level = node.level;
+ // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
+ if (level === 1) {
+ return false;
+ }
+ // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
+ let parentData = node.parent;
+ // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
+ let index = 0;
+ while (index < level - 1) {
+ // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紱涓� filterNode 涓�鑷达紝鏍囩涓庡叧閿瓧鍧囨寜灏忓啓姣旇緝锛岃嫳鏂囨ā绯婂尮閰嶄笉鍖哄垎澶у皬鍐�
+ const parentLabelLower = String(parentData.data.label ?? "").toLowerCase();
+ if (parentLabelLower.indexOf(value) !== -1) {
+ return true;
+ }
+ // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
+ parentData = parentData.parent;
+ index++;
+ }
+ // 娌″尮閰嶅埌杩斿洖false
+ return false;
+ };
+ const qrCodeName = ref("");
+
+ // 鐢熸垚浜岀淮鐮�
+ const generateQrcode = async row => {
+ try {
+ currentProductId.value = row.id;
+ qrCodeName.value = fatherName.value + "-" + row.model;
+ // 浣跨敤row.id鐢熸垚浜岀淮鐮�
+ const qrCodeData = row.id.toString();
+ // 鐢熸垚浜岀淮鐮乁RL
+ qrCodeUrl.value = await QRCode.toDataURL(qrCodeData, {
+ width: 300,
+ margin: 1,
+ });
+ // 鎵撳紑浜岀淮鐮佸璇濇
+ qrCodeDialog.value = true;
+ } catch (error) {
+ console.error("鐢熸垚浜岀淮鐮佸け璐�:", error);
+ proxy.$modal.msgError("鐢熸垚浜岀淮鐮佸け璐�");
+ }
+ };
+
+ // 淇濆瓨浜岀淮鐮佷负鍥剧墖
+ const saveQrCodeAsImage = () => {
+ if (!qrCodeUrl.value) return;
+
+ try {
+ // 浠嶥ata URL鍒涘缓Blob
+ const blob = dataURLToBlob(qrCodeUrl.value);
+ // 浣跨敤file-saver淇濆瓨鍥剧墖
+ saveAs(blob, `${qrCodeName.value}.png`);
+ proxy.$modal.msgSuccess("淇濆瓨鎴愬姛");
+ } catch (error) {
+ console.error("淇濆瓨鍥剧墖澶辫触:", error);
+ proxy.$modal.msgError("淇濆瓨鍥剧墖澶辫触");
+ }
+ };
+
+ // 灏咲ata URL杞崲涓築lob
+ const dataURLToBlob = dataURL => {
+ const arr = dataURL.split(",");
+ const mime = arr[0].match(/:(.*?);/)[1];
+ const bstr = atob(arr[1]);
+ let n = bstr.length;
+ const u8arr = new Uint8Array(n);
+ while (n--) {
+ u8arr[n] = bstr.charCodeAt(n);
+ }
+ return new Blob([u8arr], { type: mime });
+ };
+
+ getProductTreeList();
</script>
<style scoped>
-.product-view {
- display: flex;
-}
-.left {
- width: 450px;
- min-width: 450px;
- padding: 16px;
- background: #ffffff;
-}
-.right {
- flex: 1;
- min-width: 0;
- padding: 16px;
- margin-left: 20px;
- background: #ffffff;
-}
-.custom-tree-node {
- flex: 1;
- min-width: 0;
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-size: 14px;
- padding-right: 8px;
-}
-.tree-node-content {
- flex: 1;
- min-width: 0;
- display: flex;
- align-items: center;
- height: 100%;
- overflow: hidden;
-}
-.tree-node-content .orange-icon {
- flex-shrink: 0;
-}
-.tree-node-label {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-.orange-icon {
- color: orange;
- font-size: 18px;
- margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
-}
-.product-tree-scroll {
- scrollbar-width: thin;
- scrollbar-color: #c0c4cc #f5f7fa;
-}
-.product-tree-scroll::-webkit-scrollbar {
- width: 8px;
-}
-.product-tree-scroll::-webkit-scrollbar-track {
- background: #f5f7fa;
- border-radius: 4px;
-}
-.product-tree-scroll::-webkit-scrollbar-thumb {
- background: #c0c4cc;
- border-radius: 4px;
-}
-.product-tree-scroll::-webkit-scrollbar-thumb:hover {
- background: #909399;
-}
+ .product-view {
+ display: flex;
+ }
+ .left {
+ width: 450px;
+ min-width: 450px;
+ padding: 16px;
+ background: #ffffff;
+ }
+ .right {
+ flex: 1;
+ min-width: 0;
+ padding: 16px;
+ margin-left: 20px;
+ background: #ffffff;
+ }
+ .custom-tree-node {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 14px;
+ padding-right: 8px;
+ }
+ .tree-node-content {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ height: 100%;
+ overflow: hidden;
+ }
+ .tree-node-content .orange-icon {
+ flex-shrink: 0;
+ }
+ .tree-node-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .orange-icon {
+ color: orange;
+ font-size: 18px;
+ margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
+ }
+ .product-tree-scroll {
+ scrollbar-width: thin;
+ scrollbar-color: #c0c4cc #f5f7fa;
+ }
+ .product-tree-scroll::-webkit-scrollbar {
+ width: 8px;
+ }
+ .product-tree-scroll::-webkit-scrollbar-track {
+ background: #f5f7fa;
+ border-radius: 4px;
+ }
+ .product-tree-scroll::-webkit-scrollbar-thumb {
+ background: #c0c4cc;
+ border-radius: 4px;
+ }
+ .product-tree-scroll::-webkit-scrollbar-thumb:hover {
+ background: #909399;
+ }
+
+ /* 浜岀淮鐮佹牱寮� */
+ .qrcode-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+ min-height: 300px;
+ }
+
+ .qrcode-image {
+ max-width: 100%;
+ height: auto;
+ }
+
+ .loading {
+ font-size: 16px;
+ color: #606266;
+ }
</style>
diff --git a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
index 20a4ee6..173a375 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -1,118 +1,135 @@
<template>
<div>
- <el-dialog
- v-model="dialogFormVisible"
- :title="operationType === 'approval' ? '瀹℃壒' : '璇︽儏'"
- width="700px"
- @close="closeDia"
- >
- <el-form :model="form" label-width="140px" label-position="top" ref="formRef">
- <el-row>
- <el-col :span="24">
- <el-form-item label="娴佺▼缂栧彿锛�" prop="approveId">
- <el-input v-model="form.approveId" placeholder="鑷姩缂栧彿" clearable disabled/>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row>
- <el-col :span="24">
- <el-form-item label="鐢宠閮ㄩ棬锛�">
- <el-select
- disabled
- v-model="form.approveDeptId"
- placeholder="閫夋嫨閮ㄩ棬"
- >
- <el-option
- v-for="user in productOptions"
- :key="user.deptId"
- :label="user.deptName"
- :value="user.deptId"
- />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row v-if="!isQuotationApproval && !isPurchaseApproval">
- <el-col :span="24">
- <el-form-item :label="props.approveType == 5 ? '閲囪喘鍚堝悓鍙凤細' : '瀹℃壒浜嬬敱锛�'" prop="approveReason">
- <el-input v-model="form.approveReason" placeholder="璇疯緭鍏�" clearable type="textarea" disabled/>
- </el-form-item>
- </el-col>
- </el-row>
- <!-- 瀹℃壒浜洪�夋嫨锛堝姩鎬佽妭鐐癸級 -->
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="鐢宠浜猴細" prop="approveUser">
- <el-select
- v-model="form.approveUser"
- placeholder="閫夋嫨浜哄憳"
- disabled
- >
- <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="approveTime">
- <el-date-picker
- v-model="form.approveTime"
- type="date"
- placeholder="璇烽�夋嫨鏃ユ湡"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- clearable
- style="width: 100%"
- disabled
- />
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
-
+ <el-dialog v-model="dialogFormVisible"
+ :title="operationType === 'approval' ? '瀹℃壒' : '璇︽儏'"
+ width="700px"
+ @close="closeDia">
+ <el-form :model="form"
+ label-width="140px"
+ label-position="top"
+ ref="formRef">
+ <el-row>
+ <el-col :span="24">
+ <el-form-item label="娴佺▼缂栧彿锛�"
+ prop="approveId">
+ <el-input v-model="form.approveId"
+ placeholder="鑷姩缂栧彿"
+ clearable
+ disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="24">
+ <el-form-item label="鐢宠閮ㄩ棬锛�">
+ <el-select disabled
+ v-model="form.approveDeptId"
+ placeholder="閫夋嫨閮ㄩ棬">
+ <el-option v-for="user in productOptions"
+ :key="user.deptId"
+ :label="user.deptName"
+ :value="user.deptId" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row v-if="!isQuotationApproval && !isPurchaseApproval">
+ <el-col :span="24">
+ <el-form-item :label="props.approveType == 5 ? '閲囪喘鍚堝悓鍙凤細' : '瀹℃壒浜嬬敱锛�'"
+ prop="approveReason">
+ <el-input v-model="form.approveReason"
+ placeholder="璇疯緭鍏�"
+ clearable
+ type="textarea"
+ disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 瀹℃壒浜洪�夋嫨锛堝姩鎬佽妭鐐癸級 -->
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鐢宠浜猴細"
+ prop="approveUser">
+ <el-input v-model="form.approveUserName"
+ clearable
+ disabled />
+ <!-- <el-select v-model="form.approveUser"
+ placeholder="閫夋嫨浜哄憳"
+ disabled>
+ <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="approveTime">
+ <el-date-picker v-model="form.approveTime"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%"
+ disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
<!-- 鎶ヤ环瀹℃壒锛氬睍绀烘姤浠疯鎯咃紙澶嶇敤閿�鍞姤浠�"鏌ョ湅璇︽儏瀵硅瘽妗�"鍐呭缁撴瀯锛� -->
- <div v-if="isQuotationApproval" style="margin: 10px 0 18px;">
+ <div v-if="isQuotationApproval"
+ style="margin: 10px 0 18px;">
<el-divider content-position="left">鎶ヤ环璇︽儏</el-divider>
- <el-skeleton :loading="quotationLoading" animated>
+ <el-skeleton :loading="quotationLoading"
+ animated>
<template #template>
- <el-skeleton-item variant="h3" style="width: 30%" />
- <el-skeleton-item variant="text" style="width: 100%" />
- <el-skeleton-item variant="text" style="width: 100%" />
+ <el-skeleton-item variant="h3"
+ style="width: 30%" />
+ <el-skeleton-item variant="text"
+ style="width: 100%" />
+ <el-skeleton-item variant="text"
+ style="width: 100%" />
</template>
<template #default>
- <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo" description="鏈煡璇㈠埌瀵瑰簲鎶ヤ环璇︽儏" />
+ <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo"
+ description="鏈煡璇㈠埌瀵瑰簲鎶ヤ环璇︽儏" />
<template v-else>
- <el-descriptions :column="2" border>
+ <el-descriptions :column="2"
+ border>
<el-descriptions-item label="鎶ヤ环鍗曞彿">{{ currentQuotation.quotationNo }}</el-descriptions-item>
<el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentQuotation.customer }}</el-descriptions-item>
<el-descriptions-item label="涓氬姟鍛�">{{ currentQuotation.salesperson }}</el-descriptions-item>
<el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
<el-descriptions-item label="鏈夋晥鏈熻嚦">{{ currentQuotation.validDate }}</el-descriptions-item>
<el-descriptions-item label="浠樻鏂瑰紡">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
- <el-descriptions-item label="鎶ヤ环鎬婚" :span="2">
+ <el-descriptions-item label="鎶ヤ环鎬婚"
+ :span="2">
<span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
楼{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }}
</span>
</el-descriptions-item>
</el-descriptions>
-
<div style="margin-top: 20px;">
<h4>浜у搧鏄庣粏</h4>
- <el-table :data="currentQuotation.products || []" border style="width: 100%">
- <el-table-column prop="product" label="浜у搧鍚嶇О" />
- <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" />
- <el-table-column prop="unit" label="鍗曚綅" />
- <el-table-column prop="unitPrice" label="鍗曚环">
+ <el-table :data="currentQuotation.products || []"
+ border
+ style="width: 100%">
+ <el-table-column prop="product"
+ label="浜у搧鍚嶇О" />
+ <el-table-column prop="specification"
+ label="瑙勬牸鍨嬪彿" />
+ <el-table-column prop="unit"
+ label="鍗曚綅" />
+ <el-table-column prop="unitPrice"
+ label="鍗曚环">
<template #default="scope">楼{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template>
</el-table-column>
</el-table>
</div>
-
- <div v-if="currentQuotation.remark" style="margin-top: 20px;">
+ <div v-if="currentQuotation.remark"
+ style="margin-top: 20px;">
<h4>澶囨敞</h4>
<p>{{ currentQuotation.remark }}</p>
</div>
@@ -120,20 +137,26 @@
</template>
</el-skeleton>
</div>
-
<!-- 閲囪喘瀹℃壒锛氬睍绀洪噰璐鎯� -->
- <div v-if="isPurchaseApproval" style="margin: 10px 0 18px;">
+ <div v-if="isPurchaseApproval"
+ style="margin: 10px 0 18px;">
<el-divider content-position="left">閲囪喘璇︽儏</el-divider>
- <el-skeleton :loading="purchaseLoading" animated>
+ <el-skeleton :loading="purchaseLoading"
+ animated>
<template #template>
- <el-skeleton-item variant="h3" style="width: 30%" />
- <el-skeleton-item variant="text" style="width: 100%" />
- <el-skeleton-item variant="text" style="width: 100%" />
+ <el-skeleton-item variant="h3"
+ style="width: 30%" />
+ <el-skeleton-item variant="text"
+ style="width: 100%" />
+ <el-skeleton-item variant="text"
+ style="width: 100%" />
</template>
<template #default>
- <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber" description="鏈煡璇㈠埌瀵瑰簲閲囪喘璇︽儏" />
+ <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber"
+ description="鏈煡璇㈠埌瀵瑰簲閲囪喘璇︽儏" />
<template v-else>
- <el-descriptions :column="2" border>
+ <el-descriptions :column="2"
+ border>
<el-descriptions-item label="閲囪喘鍚堝悓鍙�">{{ currentPurchase.purchaseContractNumber }}</el-descriptions-item>
<el-descriptions-item label="渚涘簲鍟嗗悕绉�">{{ currentPurchase.supplierName }}</el-descriptions-item>
<el-descriptions-item label="椤圭洰鍚嶇О">{{ currentPurchase.projectName }}</el-descriptions-item>
@@ -141,24 +164,32 @@
<el-descriptions-item label="绛捐鏃ユ湡">{{ currentPurchase.executionDate }}</el-descriptions-item>
<el-descriptions-item label="褰曞叆鏃ユ湡">{{ currentPurchase.entryDate }}</el-descriptions-item>
<el-descriptions-item label="浠樻鏂瑰紡">{{ currentPurchase.paymentMethod }}</el-descriptions-item>
- <el-descriptions-item label="鍚堝悓閲戦" :span="2">
+ <el-descriptions-item label="鍚堝悓閲戦"
+ :span="2">
<span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
楼{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }}
</span>
</el-descriptions-item>
</el-descriptions>
-
<div style="margin-top: 20px;">
<h4>浜у搧鏄庣粏</h4>
- <el-table :data="currentPurchase.productData || []" border style="width: 100%">
- <el-table-column prop="productCategory" label="浜у搧鍚嶇О" />
- <el-table-column prop="specificationModel" label="瑙勬牸鍨嬪彿" />
- <el-table-column prop="unit" label="鍗曚綅" />
- <el-table-column prop="quantity" label="鏁伴噺" />
- <el-table-column prop="taxInclusiveUnitPrice" label="鍚◣鍗曚环">
+ <el-table :data="currentPurchase.productData || []"
+ border
+ style="width: 100%">
+ <el-table-column prop="productCategory"
+ label="浜у搧鍚嶇О" />
+ <el-table-column prop="specificationModel"
+ label="瑙勬牸鍨嬪彿" />
+ <el-table-column prop="unit"
+ label="鍗曚綅" />
+ <el-table-column prop="quantity"
+ label="鏁伴噺" />
+ <el-table-column prop="taxInclusiveUnitPrice"
+ label="鍚◣鍗曚环">
<template #default="scope">楼{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template>
</el-table-column>
- <el-table-column prop="taxInclusiveTotalPrice" label="鍚◣鎬讳环">
+ <el-table-column prop="taxInclusiveTotalPrice"
+ label="鍚◣鎬讳环">
<template #default="scope">楼{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template>
</el-table-column>
</el-table>
@@ -167,52 +198,78 @@
</template>
</el-skeleton>
</div>
-
- <el-form :model="{ activities }" ref="formRef" label-position="top">
- <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
- <el-step
- v-for="(activity, index) in activities"
- :key="index"
- finish-status="success"
- :title="getNodeTitle(index, activities.length)"
- :description="activity.approveNodeUser"
- :icon="getNodeIcon(activity, index)"
- >
- <template #icon>
- <el-icon v-if="activity.approveNodeStatus === 2" color="red" :size="22"><WarningFilled /></el-icon>
- <el-icon v-else-if="activity.isShen" color="#1890ff" :size="22"><Edit /></el-icon>
- <el-icon v-else-if="activity.approveNodeStatus === 1" color="#67C23A" :size="26"><Check /></el-icon>
- <el-icon v-else color="#C0C4CC" :size="22"><MoreFilled /></el-icon>
- </template>
+ <el-form :model="{ activities }"
+ ref="formRef"
+ label-position="top">
+ <el-steps :active="getActiveStep()"
+ finish-status="success"
+ process-status="process"
+ align-center
+ direction="vertical">
+ <el-step v-for="(activity, index) in activities"
+ :key="index"
+ finish-status="success"
+ :title="getNodeTitle(index, activities.length)"
+ :description="activity.approveNodeUser"
+ :icon="getNodeIcon(activity, index)">
+ <template #icon>
+ <el-icon v-if="activity.approveNodeStatus === 2"
+ color="red"
+ :size="22">
+ <WarningFilled />
+ </el-icon>
+ <el-icon v-else-if="activity.isShen"
+ color="#1890ff"
+ :size="22">
+ <Edit />
+ </el-icon>
+ <el-icon v-else-if="activity.approveNodeStatus === 1"
+ color="#67C23A"
+ :size="26">
+ <Check />
+ </el-icon>
+ <el-icon v-else
+ color="#C0C4CC"
+ :size="22">
+ <MoreFilled />
+ </el-icon>
+ </template>
<template #title>
<span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span>
</template>
<template #description>
<div class="node-user">
<div class="avatar-wrapper">
- <img :src="userStore.avatar" class="user-avatar" alt=""/>
+ <img :src="userStore.avatar"
+ class="user-avatar"
+ alt="" />
</div>
<span style="color: #000000">{{ activity.approveNodeUser }}-{{activity.isApproval}}</span>
</div>
- <div v-if="!activity.isShen" class="node-reason">
+ <div v-if="!activity.isShen"
+ class="node-reason">
<span>瀹℃壒鎰忚锛�</span>{{ activity.approveNodeReason }}
</div>
<div v-else-if="activity.isShen">
- <el-form-item
- :prop="'activities.' + index + '.approveNodeReason'"
- :rules="[{ required: true, message: '瀹℃壒鎰忚涓嶈兘涓虹┖', trigger: 'blur' }]"
- >
- <el-input v-model="activity.approveNodeReason" clearable type="textarea" :disabled="operationType === 'view'"></el-input>
+ <el-form-item :prop="'activities.' + index + '.approveNodeReason'"
+ :rules="[{ required: true, message: '瀹℃壒鎰忚涓嶈兘涓虹┖', trigger: 'blur' }]">
+ <el-input v-model="activity.approveNodeReason"
+ clearable
+ type="textarea"
+ :disabled="operationType === 'view'"></el-input>
</el-form-item>
</div>
</template>
</el-step>
</el-steps>
</el-form>
- <template #footer v-if="operationType === 'approval'">
+ <template #footer
+ v-if="operationType === 'approval'">
<div class="dialog-footer">
- <el-button type="primary" @click="submitForm(2)">涓嶉�氳繃</el-button>
- <el-button type="primary" @click="submitForm(1)">閫氳繃</el-button>
+ <el-button type="primary"
+ @click="submitForm(2)">涓嶉�氳繃</el-button>
+ <el-button type="primary"
+ @click="submitForm(1)">閫氳繃</el-button>
<el-button @click="closeDia">鍙栨秷</el-button>
</div>
</template>
@@ -221,228 +278,248 @@
</template>
<script setup>
-import { computed, getCurrentInstance, nextTick, reactive, ref, toRefs } from "vue";
-import {
- approveProcessDetails,
- getDept,
- updateApproveNode
-} from "@/api/collaborativeApproval/approvalProcess.js";
-import useUserStore from "@/store/modules/user.js";
-import {userListNoPageByTenantId} from "@/api/system/user.js";
-import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
-import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
-import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
-const emit = defineEmits(['close'])
-const { proxy } = getCurrentInstance()
+ import {
+ computed,
+ getCurrentInstance,
+ nextTick,
+ reactive,
+ ref,
+ toRefs,
+ } from "vue";
+ import {
+ approveProcessDetails,
+ getDept,
+ updateApproveNode,
+ } from "@/api/collaborativeApproval/approvalProcess.js";
+ import useUserStore from "@/store/modules/user.js";
+ import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
+ import {
+ WarningFilled,
+ Edit,
+ Check,
+ MoreFilled,
+ } from "@element-plus/icons-vue";
+ import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
+ import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
+ const emit = defineEmits(["close"]);
+ const { proxy } = getCurrentInstance();
-const props = defineProps({
- approveType: {
- type: [Number, String],
- default: 0
- }
-})
-
-const dialogFormVisible = ref(false);
-const operationType = ref('')
-const activities = ref([])
-const formRef = ref(null);
-const userStore = useUserStore()
-const productOptions = ref([]);
-const userList = ref([])
-const quotationLoading = ref(false)
-const currentQuotation = ref({})
-const purchaseLoading = ref(false)
-const currentPurchase = ref({})
-const isQuotationApproval = computed(() => Number(props.approveType) === 6)
-const isPurchaseApproval = computed(() => Number(props.approveType) === 5)
-
-const data = reactive({
- form: {
- approveTime: "",
- approveId: "",
- approveUser: "",
- approveDeptId: "",
- approveReason: "",
- checkResult: "",
- },
-});
-const { form } = toRefs(data);
-
-// 鑺傜偣鏍囬
-const getNodeTitle = (index, len) => {
- if (index === len - 1) return '缁撴潫';
- return '瀹℃壒';
-};
-
-// 鑾峰彇褰撳墠婵�娲绘楠�
-const getActiveStep = () => {
- // 濡傛灉鎵�鏈� isShen 閮戒负 false锛岃繑鍥炴渶鍚庝竴涓楠わ紙鍏ㄩ儴瀹屾垚锛�
- const hasActive = activities.value.some(a => a.isShen === true);
- if (!hasActive) return activities.value.length;
- // 褰撳墠鑺傜偣绱㈠紩
- return activities.value.findIndex(a => a.isShen == true);
-};
-// 姝ラicon
-const getNodeIcon = (activity, index) => {
- if (activity.approveNodeStatus === 2) return 'el-icon-warning'; // 涓嶉�氳繃
- if (activity.isShen) return 'Edit';
- return '';
-};
-
-// 鎵撳紑寮规
-const openDialog = (type, row) => {
- operationType.value = type;
- dialogFormVisible.value = true;
- currentQuotation.value = {}
- currentPurchase.value = {}
- userListNoPageByTenantId().then((res) => {
- userList.value = res.data;
- });
- form.value = {...row}
- // 绔嬪嵆娓呴櫎琛ㄥ崟楠岃瘉鐘舵�侊紙鍥犱负瀛楁鏄痙isabled鐨勶紝涓嶉渶瑕侀獙璇侊級
- nextTick(() => {
- if (formRef.value) {
- formRef.value.clearValidate();
- }
- });
- // 纭繚閫夐」鍔犺浇瀹屾垚鍚庡啀鍖归厤鍊肩被鍨�
- getProductOptions().then(() => {
- // 纭繚鍊肩被鍨嬪尮閰嶏紙濡傛灉閫夐」宸插姞杞斤級
- if (productOptions.value.length > 0 && form.value.approveDeptId) {
- const matchedOption = productOptions.value.find(opt =>
- opt.deptId == form.value.approveDeptId ||
- String(opt.deptId) === String(form.value.approveDeptId)
- );
- if (matchedOption) {
- form.value.approveDeptId = matchedOption.deptId;
- }
- }
- // 鍐嶆娓呴櫎楠岃瘉锛岀‘淇濋�夐」鍔犺浇鍚庡�煎尮閰嶆纭�
- nextTick(() => {
- if (formRef.value) {
- formRef.value.clearValidate();
- }
- });
- });
-
- // 鎶ヤ环瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鎶ヤ环鍗曞彿"鍘绘煡鎶ヤ环鍒楄〃
- 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)
- proxy.$modal.msgError('鏌ヨ閲囪喘璇︽儏澶辫触')
- }).finally(() => {
- purchaseLoading.value = false
- })
- }
- }
-
- approveProcessDetails(row.approveId).then((res) => {
- activities.value = res.data
- // 澧炲姞isApproval瀛楁
- activities.value.forEach(item => {
- if (item.url && item.url.includes('word')) {
- item.urlTem = item.url.replaceAll('word', 'img')
- } else {
- item.urlTem = item.url
- }
- if (item.approveNodeStatus === 2) {
- item.isApproval = '宸查┏鍥�';
- } else if (item.approveNodeStatus === 1) {
- item.isApproval = '宸插悓鎰�';
- } else {
- item.isApproval = '鏈鎵�';
- }
- })
- })
-}
-const getProductOptions = () => {
- return getDept().then((res) => {
- productOptions.value = res.data;
- });
-};
-// 鎻愪氦瀹℃壒
-const submitForm = (status) => {
- const filteredActivities = activities.value.filter(activity => activity.isShen);
- if (!filteredActivities || filteredActivities.length === 0) {
- proxy.$modal.msgError("鏈壘鍒板緟瀹℃壒鐨勮妭鐐�");
- return;
- }
- const currentActivity = filteredActivities[0];
- if (!currentActivity) {
- proxy.$modal.msgError("鏈壘鍒板緟瀹℃壒鐨勮妭鐐�");
- return;
- }
- currentActivity.approveNodeStatus = status;
- // 鍒ゆ柇鏄惁涓烘渶鍚庝竴姝�
- const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
- updateApproveNode({ ...currentActivity, isLast }).then(() => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeDia();
+ const props = defineProps({
+ approveType: {
+ type: [Number, String],
+ default: 0,
+ },
});
-};
-// 鍏抽棴寮规
-const closeDia = () => {
- proxy.resetForm("formRef");
- dialogFormVisible.value = false;
- quotationLoading.value = false
- currentQuotation.value = {}
- purchaseLoading.value = false
- currentPurchase.value = {}
- emit('close')
-};
-defineExpose({
- openDialog,
-});
+
+ const dialogFormVisible = ref(false);
+ const operationType = ref("");
+ const activities = ref([]);
+ const formRef = ref(null);
+ const userStore = useUserStore();
+ const productOptions = ref([]);
+ const userList = ref([]);
+ const quotationLoading = ref(false);
+ const currentQuotation = ref({});
+ const purchaseLoading = ref(false);
+ const currentPurchase = ref({});
+ const isQuotationApproval = computed(() => Number(props.approveType) === 6);
+ const isPurchaseApproval = computed(() => Number(props.approveType) === 5);
+
+ const data = reactive({
+ form: {
+ approveTime: "",
+ approveId: "",
+ approveUser: "",
+ approveDeptId: "",
+ approveReason: "",
+ checkResult: "",
+ },
+ });
+ const { form } = toRefs(data);
+
+ // 鑺傜偣鏍囬
+ const getNodeTitle = (index, len) => {
+ if (index === len - 1) return "缁撴潫";
+ return "瀹℃壒";
+ };
+
+ // 鑾峰彇褰撳墠婵�娲绘楠�
+ const getActiveStep = () => {
+ // 濡傛灉鎵�鏈� isShen 閮戒负 false锛岃繑鍥炴渶鍚庝竴涓楠わ紙鍏ㄩ儴瀹屾垚锛�
+ const hasActive = activities.value.some(a => a.isShen === true);
+ if (!hasActive) return activities.value.length;
+ // 褰撳墠鑺傜偣绱㈠紩
+ return activities.value.findIndex(a => a.isShen == true);
+ };
+ // 姝ラicon
+ const getNodeIcon = (activity, index) => {
+ if (activity.approveNodeStatus === 2) return "el-icon-warning"; // 涓嶉�氳繃
+ if (activity.isShen) return "Edit";
+ return "";
+ };
+
+ // 鎵撳紑寮规
+ const openDialog = (type, row) => {
+ operationType.value = type;
+ dialogFormVisible.value = true;
+ currentQuotation.value = {};
+ currentPurchase.value = {};
+ approveUserList({ approveType: props.approveType }).then(res => {
+ userList.value = res.data;
+ });
+ form.value = { ...row };
+ // 绔嬪嵆娓呴櫎琛ㄥ崟楠岃瘉鐘舵�侊紙鍥犱负瀛楁鏄痙isabled鐨勶紝涓嶉渶瑕侀獙璇侊級
+ nextTick(() => {
+ if (formRef.value) {
+ formRef.value.clearValidate();
+ }
+ });
+ // 纭繚閫夐」鍔犺浇瀹屾垚鍚庡啀鍖归厤鍊肩被鍨�
+ getProductOptions().then(() => {
+ // 纭繚鍊肩被鍨嬪尮閰嶏紙濡傛灉閫夐」宸插姞杞斤級
+ if (productOptions.value.length > 0 && form.value.approveDeptId) {
+ const matchedOption = productOptions.value.find(
+ opt =>
+ opt.deptId == form.value.approveDeptId ||
+ String(opt.deptId) === String(form.value.approveDeptId)
+ );
+ if (matchedOption) {
+ form.value.approveDeptId = matchedOption.deptId;
+ }
+ }
+ // 鍐嶆娓呴櫎楠岃瘉锛岀‘淇濋�夐」鍔犺浇鍚庡�煎尮閰嶆纭�
+ nextTick(() => {
+ if (formRef.value) {
+ formRef.value.clearValidate();
+ }
+ });
+ });
+
+ // 鎶ヤ环瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鎶ヤ环鍗曞彿"鍘绘煡鎶ヤ环鍒楄〃
+ 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);
+ proxy.$modal.msgError("鏌ヨ閲囪喘璇︽儏澶辫触");
+ })
+ .finally(() => {
+ purchaseLoading.value = false;
+ });
+ }
+ }
+
+ approveProcessDetails(row.approveId).then(res => {
+ activities.value = res.data;
+ // 澧炲姞isApproval瀛楁
+ activities.value.forEach(item => {
+ if (item.url && item.url.includes("word")) {
+ item.urlTem = item.url.replaceAll("word", "img");
+ } else {
+ item.urlTem = item.url;
+ }
+ if (item.approveNodeStatus === 2) {
+ item.isApproval = "宸查┏鍥�";
+ } else if (item.approveNodeStatus === 1) {
+ item.isApproval = "宸插悓鎰�";
+ } else {
+ item.isApproval = "鏈鎵�";
+ }
+ });
+ });
+ };
+ const getProductOptions = () => {
+ return getDept().then(res => {
+ productOptions.value = res.data;
+ });
+ };
+ // 鎻愪氦瀹℃壒
+ const submitForm = status => {
+ const filteredActivities = activities.value.filter(
+ activity => activity.isShen
+ );
+ if (!filteredActivities || filteredActivities.length === 0) {
+ proxy.$modal.msgError("鏈壘鍒板緟瀹℃壒鐨勮妭鐐�");
+ return;
+ }
+ const currentActivity = filteredActivities[0];
+ if (!currentActivity) {
+ proxy.$modal.msgError("鏈壘鍒板緟瀹℃壒鐨勮妭鐐�");
+ return;
+ }
+ currentActivity.approveNodeStatus = status;
+ // 鍒ゆ柇鏄惁涓烘渶鍚庝竴姝�
+ const isLast =
+ activities.value.findIndex(a => a.isShen) === activities.value.length - 1;
+ updateApproveNode({ ...currentActivity, isLast }).then(() => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ });
+ };
+ // 鍏抽棴寮规
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ quotationLoading.value = false;
+ currentQuotation.value = {};
+ purchaseLoading.value = false;
+ currentPurchase.value = {};
+ emit("close");
+ };
+ defineExpose({
+ openDialog,
+ });
</script>
<style scoped>
-
-.node-user {
- margin: 10px 0;
- font-size: 16px;
- font-weight: 600;
- display: flex;
- align-items: center;
- gap: 8px;
-}
-.node-status {
- color: #1890ff;
- margin-left: 8px;
- font-size: 14px;
-}
-.node-reason {
- font-size: 15px;
- color: #333;
- margin: 10px 0;
-}
-.user-avatar {
- cursor: pointer;
- width: 30px;
- height: 30px;
- border-radius: 50px;
-}
-.signImg {
- cursor: pointer;
- width: 200px;
- height: 60px;
-}
+ .node-user {
+ margin: 10px 0;
+ font-size: 16px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ .node-status {
+ color: #1890ff;
+ margin-left: 8px;
+ font-size: 14px;
+ }
+ .node-reason {
+ font-size: 15px;
+ color: #333;
+ margin: 10px 0;
+ }
+ .user-avatar {
+ cursor: pointer;
+ width: 30px;
+ height: 30px;
+ border-radius: 50px;
+ }
+ .signImg {
+ cursor: pointer;
+ width: 200px;
+ height: 60px;
+ }
</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
index 998fa33..cee3eeb 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -1,99 +1,103 @@
<template>
<div>
- <el-dialog
- v-model="dialogFormVisible"
- :title="operationType === 'add' ? '鏂板瀹℃壒娴佺▼' : '缂栬緫瀹℃壒娴佺▼'"
- width="50%"
- @close="closeDia"
- >
- <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
+ <el-dialog v-model="dialogFormVisible"
+ :title="operationType === 'add' ? '鏂板瀹℃壒娴佺▼' : '缂栬緫瀹℃壒娴佺▼'"
+ width="50%"
+ @close="closeDia">
+ <el-form :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
<el-row>
<el-col :span="24">
- <el-form-item label="娴佺▼缂栧彿锛�" prop="approveId">
- <el-input v-model="form.approveId" placeholder="鑷姩缂栧彿" clearable disabled/>
+ <el-form-item label="娴佺▼缂栧彿锛�"
+ prop="approveId">
+ <el-input v-model="form.approveId"
+ placeholder="鑷姩缂栧彿"
+ clearable
+ disabled />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
- <el-form-item label="鐢宠閮ㄩ棬锛�" prop="approveDeptName">
-<!-- <el-input v-model="form.approveDeptName" placeholder="璇疯緭鍏�" clearable/>-->
- <el-select
- v-model="form.approveDeptId"
- placeholder="閫夋嫨閮ㄩ棬"
- @change="handleDeptChange"
- >
- <el-option
- v-for="user in productOptions"
- :key="user.deptId"
- :label="user.deptName"
- :value="user.deptId"
- />
- </el-select>
+ <el-form-item label="鐢宠閮ㄩ棬锛�"
+ prop="approveDeptName">
+ <!-- <el-input v-model="form.approveDeptName" placeholder="璇疯緭鍏�" clearable/>-->
+ <el-select v-model="form.approveDeptId"
+ placeholder="閫夋嫨閮ㄩ棬"
+ @change="handleDeptChange">
+ <el-option v-for="user in productOptions"
+ :key="user.deptId"
+ :label="user.deptName"
+ :value="user.deptId" />
+ </el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
- <el-form-item :label="props.approveType == 5 ? '閲囪喘鍚堝悓鍙凤細' : '瀹℃壒浜嬬敱锛�'" prop="approveReason">
- <el-input v-model="form.approveReason" placeholder="璇疯緭鍏�" clearable type="textarea" />
+ <el-form-item :label="props.approveType == 5 ? '閲囪喘鍚堝悓鍙凤細' : '瀹℃壒浜嬬敱锛�'"
+ prop="approveReason">
+ <el-input v-model="form.approveReason"
+ placeholder="璇疯緭鍏�"
+ clearable
+ type="textarea" />
</el-form-item>
</el-col>
</el-row>
<!-- 璇峰亣鏃堕棿锛堜粎褰� approveType 涓� 2 鏃舵樉绀猴級 -->
- <el-row :gutter="30" v-if="props.approveType == 2">
+ <el-row :gutter="30"
+ v-if="props.approveType == 2">
<el-col :span="12">
- <el-form-item label="璇峰亣寮�濮嬫椂闂达細" prop="startDate">
- <el-date-picker
- v-model="form.startDate"
- type="date"
- placeholder="璇烽�夋嫨寮�濮嬫棩鏈�"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- clearable
- style="width: 100%"
- />
+ <el-form-item label="璇峰亣寮�濮嬫椂闂达細"
+ prop="startDate">
+ <el-date-picker v-model="form.startDate"
+ type="date"
+ placeholder="璇烽�夋嫨寮�濮嬫棩鏈�"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="璇峰亣缁撴潫鏃堕棿锛�" prop="endDate">
- <el-date-picker
- v-model="form.endDate"
- type="date"
- placeholder="璇烽�夋嫨缁撴潫鏃ユ湡"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- clearable
- style="width: 100%"
- />
+ <el-form-item label="璇峰亣缁撴潫鏃堕棿锛�"
+ prop="endDate">
+ <el-date-picker v-model="form.endDate"
+ type="date"
+ placeholder="璇烽�夋嫨缁撴潫鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<!-- 鎶ラ攢閲戦锛堜粎褰� approveType 涓� 4 鏃舵樉绀猴級 -->
<el-row v-if="props.approveType == 4">
<el-col :span="24">
- <el-form-item label="鎶ラ攢閲戦锛�" prop="price">
- <el-input-number
- v-model="form.price"
- placeholder="璇疯緭鍏ユ姤閿�閲戦"
- :min="0"
- :precision="2"
- :step="0.01"
- style="width: 100%"
- clearable
- />
+ <el-form-item label="鎶ラ攢閲戦锛�"
+ prop="price">
+ <el-input-number v-model="form.price"
+ placeholder="璇疯緭鍏ユ姤閿�閲戦"
+ :min="0"
+ :precision="2"
+ :step="0.01"
+ style="width: 100%"
+ clearable />
</el-form-item>
</el-col>
</el-row>
<!-- 鍑哄樊鍦扮偣锛堜粎褰� approveType 涓� 3 鏃舵樉绀猴級 -->
<el-row v-if="props.approveType == 3">
<el-col :span="24">
- <el-form-item label="鍑哄樊鍦扮偣锛�" prop="location">
- <el-input
- v-model="form.location"
- placeholder="璇疯緭鍏ュ嚭宸湴鐐�"
- clearable
- />
+ <el-form-item label="鍑哄樊鍦扮偣锛�"
+ prop="location">
+ <el-input v-model="form.location"
+ placeholder="璇疯緭鍏ュ嚭宸湴鐐�"
+ clearable />
</el-form-item>
</el-col>
</el-row>
@@ -103,37 +107,31 @@
<el-form-item>
<template #label>
<span>瀹℃壒浜洪�夋嫨锛�</span>
- <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">鏂板鑺傜偣</el-button>
+ <el-button type="primary"
+ @click="addApproverNode"
+ style="margin-left: 8px;">鏂板鑺傜偣</el-button>
</template>
<div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
- <div
- v-for="(node, index) in approverNodes"
- :key="node.id"
- style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
- >
+ <div v-for="(node, index) in approverNodes"
+ :key="node.id"
+ style="margin-right: 30px; text-align: center; margin-bottom: 10px;">
<div>
<span>瀹℃壒浜�</span>
鈫�
</div>
- <el-select
- v-model="node.userId"
- placeholder="閫夋嫨浜哄憳"
- style="width: 120px; margin-bottom: 8px;"
- >
- <el-option
- v-for="user in userList"
- :key="user.userId"
- :label="user.nickName"
- :value="user.userId"
- />
+ <el-select v-model="node.userId"
+ placeholder="閫夋嫨浜哄憳"
+ style="width: 120px; margin-bottom: 8px;">
+ <el-option v-for="user in userListApproval"
+ :key="user.userId"
+ :label="user.userName"
+ :value="user.userId" />
</el-select>
<div>
- <el-button
- type="danger"
- size="small"
- @click="removeApproverNode(index)"
- v-if="approverNodes.length > 1"
- >鍒犻櫎</el-button>
+ <el-button type="danger"
+ size="small"
+ @click="removeApproverNode(index)"
+ v-if="approverNodes.length > 1">鍒犻櫎</el-button>
</div>
</div>
</div>
@@ -142,45 +140,51 @@
</el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="鐢宠浜猴細" prop="approveUser">
- <el-select
- v-model="form.approveUser"
- placeholder="閫夋嫨浜哄憳"
- filterable
- default-first-option
- :reserve-keyword="false"
- >
- <el-option
- v-for="user in userList"
- :key="user.userId"
- :label="user.nickName"
- :value="user.userId"
- />
- </el-select>
+ <el-form-item label="鐢宠浜猴細"
+ prop="approveUser">
+ <el-select v-model="form.approveUser"
+ placeholder="閫夋嫨浜哄憳"
+ filterable
+ default-first-option
+ :reserve-keyword="false">
+ <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="approveTime">
- <el-date-picker
- v-model="form.approveTime"
- type="date"
- placeholder="璇烽�夋嫨鏃ユ湡"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- clearable
- style="width: 100%"
- />
+ <el-form-item label="鐢宠鏃ユ湡锛�"
+ prop="approveTime">
+ <el-date-picker v-model="form.approveTime"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">
- <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
- :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
- :on-success="handleUploadSuccess" :on-remove="handleRemove">
- <el-button type="primary" v-if="operationType !== 'view'">涓婁紶</el-button>
- <template #tip v-if="operationType !== 'view'">
+ <el-form-item label="闄勪欢鏉愭枡锛�"
+ prop="remark">
+ <el-upload v-model:file-list="fileList"
+ :action="upload.url"
+ multiple
+ ref="fileUpload"
+ auto-upload
+ :headers="upload.headers"
+ :before-upload="handleBeforeUpload"
+ :on-error="handleUploadError"
+ :on-success="handleUploadSuccess"
+ :on-remove="handleRemove">
+ <el-button type="primary"
+ v-if="operationType !== 'view'">涓婁紶</el-button>
+ <template #tip
+ v-if="operationType !== 'view'">
<div class="el-upload__tip">
鏂囦欢鏍煎紡鏀寔
doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
@@ -193,7 +197,8 @@
</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>
@@ -202,275 +207,286 @@
</template>
<script setup>
-import {ref, reactive, toRefs, getCurrentInstance} from "vue";
-import {
- approveProcessAdd, approveProcessGetInfo,
- approveProcessUpdate,
- getDept
-} from "@/api/collaborativeApproval/approvalProcess.js";
-import {
- delLedgerFile,
-} from "@/api/salesManagement/salesLedger.js";
-import {userListNoPageByTenantId} from "@/api/system/user.js";
-import { getToken } from "@/utils/auth";
-const { proxy } = getCurrentInstance()
-const emit = defineEmits(['close'])
-import useUserStore from "@/store/modules/user";
-import { getCurrentDate } from "@/utils/index.js";
-import log from "@/views/monitor/job/log.vue";
-const userStore = useUserStore();
+ import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+ import {
+ approveProcessAdd,
+ approveProcessGetInfo,
+ approveProcessUpdate,
+ getDept,
+ } from "@/api/collaborativeApproval/approvalProcess.js";
+ import { delLedgerFile } from "@/api/salesManagement/salesLedger.js";
+ import { userListNoPageByTenantId } from "@/api/system/user.js";
+ import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
+ import { getToken } from "@/utils/auth";
+ const { proxy } = getCurrentInstance();
+ const emit = defineEmits(["close"]);
+ import useUserStore from "@/store/modules/user";
+ import { getCurrentDate } from "@/utils/index.js";
+ import log from "@/views/monitor/job/log.vue";
+ const userStore = useUserStore();
-const dialogFormVisible = ref(false);
-const operationType = ref('')
-const fileList = ref([]);
-const upload = reactive({
- // 涓婁紶鐨勫湴鍧�
- url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
- // 璁剧疆涓婁紶鐨勮姹傚ご閮�
- headers: { Authorization: "Bearer " + getToken() },
-});
-const data = reactive({
- form: {
- approveTime: "",
- approveId: "",
- approveUser: "",
- approveDeptId: "",
- approveDeptName: "",
- approveReason: "",
- checkResult: "",
- tempFileIds: [],
- approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
- startDate: "", // 璇峰亣寮�濮嬫椂闂�
- endDate: "", // 璇峰亣缁撴潫鏃堕棿
- price: null, // 鎶ラ攢閲戦
- location: "" // 鍑哄樊鍦扮偣
- },
- rules: {
- approveTime: [{ required: false, message: "璇疯緭鍏�", trigger: "change" },],
- approveId: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
- approveUser: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
- approveDeptName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- approveReason: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- checkResult: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
- startDate: [{ required: true, message: "璇烽�夋嫨璇峰亣寮�濮嬫椂闂�", trigger: "change" }],
- endDate: [{ required: true, message: "璇烽�夋嫨璇峰亣缁撴潫鏃堕棿", trigger: "change" }],
- price: [{ required: true, message: "璇疯緭鍏ユ姤閿�閲戦", trigger: "blur" }],
- location: [{ required: true, message: "璇疯緭鍏ュ嚭宸湴鐐�", trigger: "blur" }],
- },
-});
-const { form, rules } = toRefs(data);
-const productOptions = ref([]);
-const currentApproveStatus = ref(0)
-const props = defineProps({
- approveType: {
- type: [Number, String],
- default: 0
- }
-})
-
-// 瀹℃壒浜鸿妭鐐圭浉鍏�
-const approverNodes = ref([
- { id: 1, userId: null }
-])
-let nextApproverId = 2
-const userList = ref([])
-function addApproverNode() {
- approverNodes.value.push({ id: nextApproverId++, userId: null })
-}
-function removeApproverNode(index) {
- approverNodes.value.splice(index, 1)
-}
-// 澶勭悊閮ㄩ棬閫夋嫨鍙樺寲
-const handleDeptChange = (deptId) => {
- if (deptId) {
- const selectedDept = productOptions.value.find(dept => dept.deptId === deptId);
- if (selectedDept) {
- form.value.approveDeptName = selectedDept.deptName;
- }
- } else {
- form.value.approveDeptName = '';
- }
-};
-// 鎵撳紑寮规
-const openDialog = (type, row) => {
- operationType.value = type;
- dialogFormVisible.value = true;
- userListNoPageByTenantId().then((res) => {
- userList.value = res.data;
+ const dialogFormVisible = ref(false);
+ const operationType = ref("");
+ const fileList = ref([]);
+ const upload = reactive({
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
});
- form.value = {}
- approverNodes.value = [
- { id: 1, userId: null }
- ]
- form.value.approveUser = userStore.id;
- form.value.approveTime = getCurrentDate();
-
- // 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅骞惰缃儴闂↖D
- form.value.approveDeptId = userStore.currentDeptId
-
- // 鍔犺浇閮ㄩ棬閫夐」锛屽苟鍦ㄥ姞杞藉畬鎴愬悗璁剧疆閮ㄩ棬鍚嶇О
- getProductOptions();
- if (operationType.value === 'edit') {
- 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 => {
- form.value = {...res.data}
- // 鍙嶆樉瀹℃壒浜�
- if (res.data && res.data.approveUserIds) {
- const userIds = res.data.approveUserIds.split(',')
- approverNodes.value = userIds.map((userId, idx) => ({
- id: idx + 1,
- userId: parseInt(userId.trim())
- }))
- nextApproverId = userIds.length + 1
- } else {
- approverNodes.value = [{ id: 1, userId: null }]
- nextApproverId = 2
- }
- })
+ const data = reactive({
+ form: {
+ approveTime: "",
+ approveId: "",
+ approveUser: "",
+ approveDeptId: "",
+ approveDeptName: "",
+ approveReason: "",
+ checkResult: "",
+ tempFileIds: [],
+ approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
+ startDate: "", // 璇峰亣寮�濮嬫椂闂�
+ endDate: "", // 璇峰亣缁撴潫鏃堕棿
+ price: null, // 鎶ラ攢閲戦
+ location: "", // 鍑哄樊鍦扮偣
+ },
+ rules: {
+ approveTime: [{ required: false, message: "璇疯緭鍏�", trigger: "change" }],
+ approveId: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+ approveUser: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+ approveDeptName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ approveReason: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ checkResult: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+ startDate: [
+ { required: true, message: "璇烽�夋嫨璇峰亣寮�濮嬫椂闂�", trigger: "change" },
+ ],
+ endDate: [
+ { required: true, message: "璇烽�夋嫨璇峰亣缁撴潫鏃堕棿", trigger: "change" },
+ ],
+ price: [{ required: true, message: "璇疯緭鍏ユ姤閿�閲戦", trigger: "blur" }],
+ location: [{ required: true, message: "璇疯緭鍏ュ嚭宸湴鐐�", trigger: "blur" }],
+ },
+ });
+ const { form, rules } = toRefs(data);
+ const productOptions = ref([]);
+ const currentApproveStatus = ref(0);
+ const props = defineProps({
+ approveType: {
+ type: [Number, String],
+ default: 0,
+ },
+ });
+
+ // 瀹℃壒浜鸿妭鐐圭浉鍏�
+ const approverNodes = ref([{ id: 1, userId: null }]);
+ let nextApproverId = 2;
+ const userList = ref([]);
+ const userListApproval = ref([]);
+
+ function addApproverNode() {
+ approverNodes.value.push({ id: nextApproverId++, userId: null });
}
-}
-const getProductOptions = () => {
- return getDept().then((res) => {
- productOptions.value = res.data;
- // 濡傛灉宸叉湁閮ㄩ棬ID锛岃嚜鍔ㄨ缃儴闂ㄥ悕绉帮紙鐢ㄤ簬楠岃瘉锛�
- if (form.value.approveDeptId && productOptions.value.length > 0) {
- const matchedDept = productOptions.value.find(dept =>
- dept.deptId == form.value.approveDeptId ||
- String(dept.deptId) === String(form.value.approveDeptId)
+ function removeApproverNode(index) {
+ approverNodes.value.splice(index, 1);
+ }
+ // 澶勭悊閮ㄩ棬閫夋嫨鍙樺寲
+ const handleDeptChange = deptId => {
+ if (deptId) {
+ const selectedDept = productOptions.value.find(
+ dept => dept.deptId === deptId
);
- if (matchedDept) {
- form.value.approveDeptName = matchedDept.deptName;
+ if (selectedDept) {
+ form.value.approveDeptName = selectedDept.deptName;
}
+ } else {
+ form.value.approveDeptName = "";
}
- });
-};
-function convertIdToValue(data) {
- return data.map((item) => {
- const { id, children, ...rest } = item;
- const newItem = {
- ...rest,
- value: id, // 灏� id 鏀逛负 value
- };
- if (children && children.length > 0) {
- newItem.children = convertIdToValue(children);
- }
-
- return newItem;
- });
-}
-// 鎻愪氦浜у搧琛ㄥ崟
-const submitForm = () => {
- // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
- form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
- form.value.approveType = props.approveType
- // 瀹℃壒浜哄繀濉牎楠�
- const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
- if (hasEmptyApprover) {
- proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒")
- return
- }
- // 褰� approveType 涓� 2 鏃讹紝鏍¢獙璇峰亣鏃堕棿
- if (props.approveType == 2) {
- if (!form.value.startDate) {
- proxy.$modal.msgError("璇烽�夋嫨璇峰亣寮�濮嬫椂闂达紒")
- return
- }
- if (!form.value.endDate) {
- proxy.$modal.msgError("璇烽�夋嫨璇峰亣缁撴潫鏃堕棿锛�")
- return
- }
- // 鏍¢獙缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂�
- if (new Date(form.value.endDate) < new Date(form.value.startDate)) {
- proxy.$modal.msgError("璇峰亣缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂达紒")
- return
- }
- }
- // 褰� approveType 涓� 3 鏃讹紝鏍¢獙鍑哄樊鍦扮偣
- if (props.approveType == 3) {
- if (!form.value.location || form.value.location.trim() === '') {
- proxy.$modal.msgError("璇疯緭鍏ュ嚭宸湴鐐癸紒")
- return
- }
- }
- // 褰� approveType 涓� 4 鏃讹紝鏍¢獙鎶ラ攢閲戦
- if (props.approveType == 4) {
- if (!form.value.price || form.value.price <= 0) {
- proxy.$modal.msgError("璇疯緭鍏ユ湁鏁堢殑鎶ラ攢閲戦锛�")
- return
- }
- }
- proxy.$refs.formRef.validate(valid => {
- if (valid) {
- if (operationType.value === "add" || currentApproveStatus.value == 3) {
- approveProcessAdd(form.value).then(res => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeDia();
- })
- } else {
- approveProcessUpdate(form.value).then(res => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeDia();
- })
- }
- }
- })
-}
-// 鍏抽棴寮规
-const closeDia = () => {
- fileList.value = []
- proxy.resetForm("formRef");
- dialogFormVisible.value = false;
- emit('close')
-};
+ };
+ // 鎵撳紑寮规
+ const openDialog = (type, row) => {
+ operationType.value = type;
+ dialogFormVisible.value = true;
+ userListNoPageByTenantId().then(res => {
+ userList.value = res.data;
+ });
+ approveUserList({ approveType: props.approveType }).then(res => {
+ userListApproval.value = res.data;
+ });
+ form.value = {};
+ approverNodes.value = [{ id: 1, userId: null }];
+ form.value.approveUser = userStore.id;
+ form.value.approveTime = getCurrentDate();
-// 涓婁紶鍓嶆牎妫�
-function handleBeforeUpload(file) {
- // 鏍℃鏂囦欢澶у皬
- // if (file.size > 1024 * 1024 * 10) {
- // proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
- // return false;
- // }
- proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
- return true;
-}
-// 涓婁紶澶辫触
-function handleUploadError(err) {
- proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
- proxy.$modal.closeLoading();
-}
-// 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file, uploadFiles) {
- proxy.$modal.closeLoading();
- if (res.code === 200) {
- // 纭繚 tempFileIds 瀛樺湪涓斾负鏁扮粍
- if (!form.value.tempFileIds) {
- form.value.tempFileIds = [];
+ // 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅骞惰缃儴闂↖D
+ form.value.approveDeptId = userStore.currentDeptId;
+
+ // 鍔犺浇閮ㄩ棬閫夐」锛屽苟鍦ㄥ姞杞藉畬鎴愬悗璁剧疆閮ㄩ棬鍚嶇О
+ getProductOptions();
+ if (operationType.value === "edit") {
+ 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 => {
+ form.value = { ...res.data };
+ // 鍙嶆樉瀹℃壒浜�
+ if (res.data && res.data.approveUserIds) {
+ const userIds = res.data.approveUserIds.split(",");
+ approverNodes.value = userIds.map((userId, idx) => ({
+ id: idx + 1,
+ userId: parseInt(userId.trim()),
+ }));
+ nextApproverId = userIds.length + 1;
+ } else {
+ approverNodes.value = [{ id: 1, userId: null }];
+ nextApproverId = 2;
+ }
+ }
+ );
}
- form.value.tempFileIds.push(res.data.tempId);
- proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
- } else {
- proxy.$modal.msgError(res.msg);
- proxy.$refs.fileUpload.handleRemove(file);
- }
-}
-// 绉婚櫎鏂囦欢
-function handleRemove(file) {
- if (operationType.value === "edit") {
- let ids = [];
- ids.push(file.id);
- delLedgerFile(ids).then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ };
+ const getProductOptions = () => {
+ return getDept().then(res => {
+ productOptions.value = res.data;
+ // 濡傛灉宸叉湁閮ㄩ棬ID锛岃嚜鍔ㄨ缃儴闂ㄥ悕绉帮紙鐢ㄤ簬楠岃瘉锛�
+ if (form.value.approveDeptId && productOptions.value.length > 0) {
+ const matchedDept = productOptions.value.find(
+ dept =>
+ dept.deptId == form.value.approveDeptId ||
+ String(dept.deptId) === String(form.value.approveDeptId)
+ );
+ if (matchedDept) {
+ form.value.approveDeptName = matchedDept.deptName;
+ }
+ }
+ });
+ };
+ function convertIdToValue(data) {
+ return data.map(item => {
+ const { id, children, ...rest } = item;
+ const newItem = {
+ ...rest,
+ value: id, // 灏� id 鏀逛负 value
+ };
+ if (children && children.length > 0) {
+ newItem.children = convertIdToValue(children);
+ }
+
+ return newItem;
});
}
-}
+ // 鎻愪氦浜у搧琛ㄥ崟
+ const submitForm = () => {
+ // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
+ form.value.approveUserIds = approverNodes.value
+ .map(node => node.userId)
+ .join(",");
+ form.value.approveType = props.approveType;
+ // 瀹℃壒浜哄繀濉牎楠�
+ const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
+ if (hasEmptyApprover) {
+ proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
+ return;
+ }
+ // 褰� approveType 涓� 2 鏃讹紝鏍¢獙璇峰亣鏃堕棿
+ if (props.approveType == 2) {
+ if (!form.value.startDate) {
+ proxy.$modal.msgError("璇烽�夋嫨璇峰亣寮�濮嬫椂闂达紒");
+ return;
+ }
+ if (!form.value.endDate) {
+ proxy.$modal.msgError("璇烽�夋嫨璇峰亣缁撴潫鏃堕棿锛�");
+ return;
+ }
+ // 鏍¢獙缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂�
+ if (new Date(form.value.endDate) < new Date(form.value.startDate)) {
+ proxy.$modal.msgError("璇峰亣缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂达紒");
+ return;
+ }
+ }
+ // 褰� approveType 涓� 3 鏃讹紝鏍¢獙鍑哄樊鍦扮偣
+ if (props.approveType == 3) {
+ if (!form.value.location || form.value.location.trim() === "") {
+ proxy.$modal.msgError("璇疯緭鍏ュ嚭宸湴鐐癸紒");
+ return;
+ }
+ }
+ // 褰� approveType 涓� 4 鏃讹紝鏍¢獙鎶ラ攢閲戦
+ if (props.approveType == 4) {
+ if (!form.value.price || form.value.price <= 0) {
+ proxy.$modal.msgError("璇疯緭鍏ユ湁鏁堢殑鎶ラ攢閲戦锛�");
+ return;
+ }
+ }
+ proxy.$refs.formRef.validate(valid => {
+ if (valid) {
+ if (operationType.value === "add" || currentApproveStatus.value == 3) {
+ approveProcessAdd(form.value).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ });
+ } else {
+ approveProcessUpdate(form.value).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ });
+ }
+ }
+ });
+ };
+ // 鍏抽棴寮规
+ const closeDia = () => {
+ fileList.value = [];
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ emit("close");
+ };
-defineExpose({
- openDialog,
-});
+ // 涓婁紶鍓嶆牎妫�
+ function handleBeforeUpload(file) {
+ // 鏍℃鏂囦欢澶у皬
+ // if (file.size > 1024 * 1024 * 10) {
+ // proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
+ // return false;
+ // }
+ proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+ return true;
+ }
+ // 涓婁紶澶辫触
+ function handleUploadError(err) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+ proxy.$modal.closeLoading();
+ }
+ // 涓婁紶鎴愬姛鍥炶皟
+ function handleUploadSuccess(res, file, uploadFiles) {
+ proxy.$modal.closeLoading();
+ if (res.code === 200) {
+ // 纭繚 tempFileIds 瀛樺湪涓斾负鏁扮粍
+ if (!form.value.tempFileIds) {
+ form.value.tempFileIds = [];
+ }
+ form.value.tempFileIds.push(res.data.tempId);
+ proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+ } else {
+ proxy.$modal.msgError(res.msg);
+ proxy.$refs.fileUpload.handleRemove(file);
+ }
+ }
+ // 绉婚櫎鏂囦欢
+ function handleRemove(file) {
+ if (operationType.value === "edit") {
+ let ids = [];
+ ids.push(file.id);
+ delLedgerFile(ids).then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ });
+ }
+ }
+
+ defineExpose({
+ openDialog,
+ });
</script>
<style scoped>
-
</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index 33bde47..a9df154 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -1,364 +1,589 @@
<template>
<div class="app-container">
<!-- 鏍囩椤靛垏鎹笉鍚岀殑瀹℃壒绫诲瀷 -->
- <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
- <el-tab-pane label="鍏嚭绠$悊" name="1"></el-tab-pane>
- <el-tab-pane label="璇峰亣绠$悊" name="2"></el-tab-pane>
- <el-tab-pane label="鍑哄樊绠$悊" name="3"></el-tab-pane>
- <el-tab-pane label="鎶ラ攢绠$悊" name="4"></el-tab-pane>
- <el-tab-pane label="閲囪喘瀹℃壒" name="5"></el-tab-pane>
- <el-tab-pane label="鎶ヤ环瀹℃壒" name="6"></el-tab-pane>
- <el-tab-pane label="鍙戣揣瀹℃壒" name="7"></el-tab-pane>
+ <el-tabs v-model="activeTab"
+ @tab-change="handleTabChange"
+ class="approval-tabs">
+ <el-tab-pane label="鍏嚭绠$悊"
+ name="1"></el-tab-pane>
+ <el-tab-pane label="璇峰亣绠$悊"
+ name="2"></el-tab-pane>
+ <el-tab-pane label="鍑哄樊绠$悊"
+ name="3"></el-tab-pane>
+ <el-tab-pane label="鎶ラ攢绠$悊"
+ name="4"></el-tab-pane>
+ <el-tab-pane label="閲囪喘瀹℃壒"
+ name="5"></el-tab-pane>
+ <el-tab-pane label="鎶ヤ环瀹℃壒"
+ name="6"></el-tab-pane>
+ <el-tab-pane label="鍙戣揣瀹℃壒"
+ name="7"></el-tab-pane>
</el-tabs>
-
<div class="search_form">
<div>
<span class="search_title">娴佺▼缂栧彿锛�</span>
- <el-input
- v-model="searchForm.approveId"
- style="width: 240px"
- placeholder="璇疯緭鍏ユ祦绋嬬紪鍙锋悳绱�"
- @change="handleQuery"
- clearable
- :prefix-icon="Search"
- />
+ <el-input v-model="searchForm.approveId"
+ style="width: 240px"
+ placeholder="璇疯緭鍏ユ祦绋嬬紪鍙锋悳绱�"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search" />
<span class="search_title ml10">瀹℃壒鐘舵�侊細</span>
- <el-select v-model="searchForm.approveStatus" clearable @change="handleQuery" style="width: 240px">
- <el-option label="寰呭鏍�" :value="0" />
- <el-option label="瀹℃牳涓�" :value="1" />
- <el-option label="瀹℃牳瀹屾垚" :value="2" />
- <el-option label="瀹℃牳鏈�氳繃" :value="3" />
- <el-option label="宸查噸鏂版彁浜�" :value="4" />
- </el-select>
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
- >鎼滅储</el-button
- >
+ <el-select v-model="searchForm.approveStatus"
+ clearable
+ @change="handleQuery"
+ style="width: 240px">
+ <el-option label="寰呭鏍�"
+ :value="0" />
+ <el-option label="瀹℃牳涓�"
+ :value="1" />
+ <el-option label="瀹℃牳瀹屾垚"
+ :value="2" />
+ <el-option label="瀹℃牳鏈�氳繃"
+ :value="3" />
+ <el-option label="宸查噸鏂版彁浜�"
+ :value="4" />
+ </el-select>
+ <el-button type="primary"
+ @click="handleQuery"
+ style="margin-left: 10px">鎼滅储</el-button>
</div>
<div>
- <el-button
- type="primary"
- @click="openForm('add')"
- v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
- >鏂板</el-button>
- <el-button @click="handleOut">瀵煎嚭</el-button>
- <el-button
- type="danger"
- plain
- @click="handleDelete"
- v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
- >鍒犻櫎</el-button>
+ <el-button @click="handleOut">瀹℃壒浜虹淮鎶�</el-button>
+ <el-button type="primary"
+ @click="openForm('add')"
+ v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7">鏂板</el-button>
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ <el-button type="danger"
+ plain
+ @click="handleDelete"
+ v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7">鍒犻櫎</el-button>
</div>
</div>
<div class="table_list">
- <PIMTable
- rowKey="id"
- :column="tableColumnCopy"
- :tableData="tableData"
- :page="page"
- :isSelection="true"
- @selection-change="handleSelectionChange"
- :tableLoading="tableLoading"
- @pagination="pagination"
- :total="page.total"
- ></PIMTable>
+ <PIMTable rowKey="id"
+ :column="tableColumnCopy"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ :total="page.total"></PIMTable>
</div>
- <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
- <approval-dia ref="approvalDia" @close="handleQuery" :approveType="currentApproveType"></approval-dia>
+ <info-form-dia ref="infoFormDia"
+ @close="handleQuery"
+ :approveType="currentApproveType"></info-form-dia>
+ <approval-dia ref="approvalDia"
+ @close="handleQuery"
+ :approveType="currentApproveType"></approval-dia>
<FileList ref="fileListRef" />
+ <!-- 瀹℃壒浜虹淮鎶ゅ璇濇 -->
+ <el-dialog v-model="approverDialogVisible"
+ title="瀹℃壒浜虹淮鎶�"
+ width="800px">
+ <div class="approver-dialog">
+ <div class="selected-info"
+ v-if="selectedApprovers.length > 0">
+ <div class="info-title">宸查�夋嫨鐨勫鎵逛汉锛�</div>
+ <div class="selected-list">
+ <el-tag v-for="approver in selectedApprovers"
+ :key="approver.id"
+ class="approver-tag">
+ {{ approver.userName }}
+ <el-icon class="el-tag__close el-icon--close"
+ @click="removeApprover(approver)">
+ <CircleClose />
+ </el-icon>
+ </el-tag>
+ </div>
+ </div>
+ <el-table ref="approverTable"
+ :data="approverList"
+ style="width: 100%"
+ @selection-change="handleApproverSelectionChange"
+ v-loading="approverLoading">
+ <el-table-column type="selection"
+ width="55"></el-table-column>
+ <el-table-column prop="userId"
+ label="ID"></el-table-column>
+ <el-table-column prop="userName"
+ label="濮撳悕"></el-table-column>
+ <el-table-column prop="createTime"
+ label="鍒涘缓鏃堕棿"></el-table-column>
+ </el-table>
+ </div>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="approverDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="submitApprovers"
+ :disabled="selectedApprovers.length === 0">
+ 鎻愪氦
+ </el-button>
+ </span>
+ </template>
+ </el-dialog>
</div>
</template>
<script setup>
-import FileList from "./fileList.vue";
-import { Search } from "@element-plus/icons-vue";
-import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
-import {ElMessageBox} from "element-plus";
-import { useRoute } from 'vue-router';
-import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
-import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
-import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
-import useUserStore from "@/store/modules/user";
+ import FileList from "./fileList.vue";
+ import { Search } from "@element-plus/icons-vue";
+ import {
+ onMounted,
+ ref,
+ computed,
+ reactive,
+ toRefs,
+ nextTick,
+ getCurrentInstance,
+ } from "vue";
+ import { ElMessageBox } from "element-plus";
+ import { useRoute } from "vue-router";
+ import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
+ import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
+ import {
+ approveProcessDelete,
+ approveProcessListPage,
+ approveUserList,
+ addApproveUser,
+ deleteApproveUser,
+ } from "@/api/collaborativeApproval/approvalProcess.js";
+ import { userListNoPageByTenantId } from "@/api/system/user.js";
+ import useUserStore from "@/store/modules/user";
-const userStore = useUserStore();
-const route = useRoute();
+ const userStore = useUserStore();
+ const route = useRoute();
-// 褰撳墠閫変腑鐨勬爣绛鹃〉锛岄粯璁や负鍏嚭绠$悊
-const activeTab = ref('1');
+ // 褰撳墠閫変腑鐨勬爣绛鹃〉锛岄粯璁や负鍏嚭绠$悊
+ const activeTab = ref("1");
-// 褰撳墠瀹℃壒绫诲瀷锛屾牴鎹�変腑鐨勬爣绛鹃〉璁$畻
-const currentApproveType = computed(() => {
- return Number(activeTab.value);
-});
+ // 褰撳墠瀹℃壒绫诲瀷锛屾牴鎹�変腑鐨勬爣绛鹃〉璁$畻
+ const currentApproveType = computed(() => {
+ return Number(activeTab.value);
+ });
-// 鏍囩椤靛垏鎹㈠鐞�
-const handleTabChange = (tabName) => {
- // 鍒囨崲鏍囩椤垫椂閲嶇疆鎼滅储鏉′欢鍜屽垎椤碉紝骞堕噸鏂板姞杞芥暟鎹�
- searchForm.value.approveId = '';
- searchForm.value.approveStatus = '';
- page.current = 1;
- getList();
-};
+ // 鏍囩椤靛垏鎹㈠鐞�
+ const handleTabChange = tabName => {
+ // 鍒囨崲鏍囩椤垫椂閲嶇疆鎼滅储鏉′欢鍜屽垎椤碉紝骞堕噸鏂板姞杞芥暟鎹�
+ searchForm.value.approveId = "";
+ searchForm.value.approveStatus = "";
+ page.current = 1;
+ getList();
+ };
+ const data = reactive({
+ searchForm: {
+ approveId: "",
+ approveStatus: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
-const data = reactive({
- searchForm: {
- approveId: "",
- approveStatus: "",
- },
-});
-const { searchForm } = toRefs(data);
+ // 鍔ㄦ�佽〃鏍煎垪閰嶇疆锛屾牴鎹鎵圭被鍨嬬敓鎴愬垪
+ const tableColumnCopy = computed(() => {
+ const isLeaveType = currentApproveType.value === 2; // 璇峰亣绠$悊
+ const isReimburseType = currentApproveType.value === 4; // 鎶ラ攢绠$悊
+ const isQuotationType = currentApproveType.value === 6; // 鎶ヤ环瀹℃壒
+ const isPurchaseType = currentApproveType.value === 5; // 閲囪喘瀹℃壒
-// 鍔ㄦ�佽〃鏍煎垪閰嶇疆锛屾牴鎹鎵圭被鍨嬬敓鎴愬垪
-const tableColumnCopy = computed(() => {
- const isLeaveType = currentApproveType.value === 2; // 璇峰亣绠$悊
- const isReimburseType = currentApproveType.value === 4; // 鎶ラ攢绠$悊
- const isQuotationType = currentApproveType.value === 6; // 鎶ヤ环瀹℃壒
- const isPurchaseType = currentApproveType.value === 5; // 閲囪喘瀹℃壒
-
- // 鍩虹鍒楅厤缃�
- const baseColumns = [
- {
- label: "瀹℃壒鐘舵��",
- prop: "approveStatus",
- dataType: "tag",
- width: 100,
- formatData: (params) => {
- if (params == 0) {
- return "寰呭鏍�";
- } else if (params == 1) {
- return "瀹℃牳涓�";
- } else if (params == 2) {
- return "瀹℃牳瀹屾垚";
- } else if (params == 4) {
- return "宸查噸鏂版彁浜�";
- } else {
- return '涓嶉�氳繃';
- }
+ // 鍩虹鍒楅厤缃�
+ const baseColumns = [
+ {
+ label: "瀹℃壒鐘舵��",
+ prop: "approveStatus",
+ dataType: "tag",
+ width: 100,
+ formatData: params => {
+ if (params == 0) {
+ return "寰呭鏍�";
+ } else if (params == 1) {
+ return "瀹℃牳涓�";
+ } else if (params == 2) {
+ return "瀹℃牳瀹屾垚";
+ } else if (params == 4) {
+ return "宸查噸鏂版彁浜�";
+ } else {
+ return "涓嶉�氳繃";
+ }
+ },
+ formatType: params => {
+ if (params == 0) {
+ return "warning";
+ } else if (params == 1) {
+ return "primary";
+ } else if (params == 2) {
+ return "success";
+ } else if (params == 4) {
+ return "info";
+ } else {
+ return "danger";
+ }
+ },
},
- formatType: (params) => {
- if (params == 0) {
- return "warning";
- } else if (params == 1) {
- return "primary";
- } else if (params == 2) {
- return "success";
- } else if (params == 4) {
- return "info";
- } else {
- return 'danger';
- }
+ {
+ label: "娴佺▼缂栧彿",
+ prop: "approveId",
+ width: 170,
},
- },
- {
- label: "娴佺▼缂栧彿",
- prop: "approveId",
- width: 170
- },
- {
- label: "鐢宠閮ㄩ棬",
- prop: "approveDeptName",
- width: 220
- },
- {
- label: isQuotationType ? "鎶ヤ环鍗曞彿" : isPurchaseType ? "閲囪喘鍚堝悓鍙�" : "瀹℃壒浜嬬敱",
- prop: "approveReason",
- },
- {
- label: "鐢宠浜�",
- prop: "approveUserName",
- width: 120
+ {
+ label: "鐢宠閮ㄩ棬",
+ prop: "approveDeptName",
+ width: 220,
+ },
+ {
+ label: isQuotationType
+ ? "鎶ヤ环鍗曞彿"
+ : isPurchaseType
+ ? "閲囪喘鍚堝悓鍙�"
+ : "瀹℃壒浜嬬敱",
+ prop: "approveReason",
+ },
+ {
+ label: "鐢宠浜�",
+ prop: "approveUserName",
+ width: 120,
+ },
+ ];
+
+ // 閲戦鍒楋紙浠呮姤閿�绠$悊鏄剧ず锛�
+ if (isReimburseType) {
+ baseColumns.push({
+ label: "閲戦锛堝厓锛�",
+ prop: "price",
+ width: 120,
+ });
}
- ];
-
- // 閲戦鍒楋紙浠呮姤閿�绠$悊鏄剧ず锛�
- if (isReimburseType) {
+
+ // 鏃ユ湡鍒楋紙鏍规嵁绫诲瀷鍔ㄦ�侀厤缃級
+ baseColumns.push(
+ {
+ label: isLeaveType ? "寮�濮嬫棩鏈�" : "鐢宠鏃ユ湡",
+ prop: isLeaveType ? "startDate" : "approveTime",
+ width: 200,
+ },
+ {
+ label: "缁撴潫鏃ユ湡",
+ prop: isLeaveType ? "endDate" : "approveOverTime",
+ width: 120,
+ }
+ );
+
+ // 褰撳墠瀹℃壒浜哄垪
baseColumns.push({
- label: "閲戦锛堝厓锛�",
- prop: "price",
- width: 120
+ label: "褰撳墠瀹℃壒浜�",
+ prop: "approveUserCurrentName",
+ width: 120,
});
- }
-
- // 鏃ユ湡鍒楋紙鏍规嵁绫诲瀷鍔ㄦ�侀厤缃級
- baseColumns.push(
- {
- label: isLeaveType ? "寮�濮嬫棩鏈�" : "鐢宠鏃ユ湡",
- prop: isLeaveType ? "startDate" : "approveTime",
- width: 200
- },
- {
- label: "缁撴潫鏃ユ湡",
- prop: isLeaveType ? "endDate" : "approveOverTime",
- width: 120
+
+ // 鎿嶄綔鍒�
+ const actionOperations = [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: row => {
+ openForm("edit", row);
+ },
+ disabled: row =>
+ currentApproveType.value === 5 ||
+ currentApproveType.value === 6 ||
+ currentApproveType.value === 7 ||
+ row.approveStatus == 2 ||
+ row.approveStatus == 1 ||
+ row.approveStatus == 4,
+ },
+ {
+ name: "瀹℃牳",
+ type: "text",
+ clickFun: row => {
+ openApprovalDia("approval", row);
+ },
+ disabled: row =>
+ row.approveUserCurrentId == null ||
+ row.approveStatus == 2 ||
+ row.approveStatus == 3 ||
+ row.approveStatus == 4 ||
+ row.approveUserCurrentId !== userStore.id,
+ },
+ {
+ name: "璇︽儏",
+ type: "text",
+ clickFun: row => {
+ openApprovalDia("view", row);
+ },
+ },
+ ];
+
+ // 鎶ヤ环瀹℃壒锛堢被鍨� 6锛変笉灞曠ず鈥滈檮浠垛�濇搷浣�
+ if (!isQuotationType) {
+ actionOperations.push({
+ name: "闄勪欢",
+ type: "text",
+ clickFun: row => {
+ downLoadFile(row);
+ },
+ });
}
- );
-
- // 褰撳墠瀹℃壒浜哄垪
- baseColumns.push({
- label: "褰撳墠瀹℃壒浜�",
- prop: "approveUserCurrentName",
- width: 120
- });
-
- // 鎿嶄綔鍒�
- const actionOperations = [
- {
- name: "缂栬緫",
- type: "text",
- clickFun: (row) => {
- openForm("edit", row);
- },
- disabled: (row) =>
- currentApproveType.value === 5 ||
- currentApproveType.value === 6 ||
- currentApproveType.value === 7 ||
- row.approveStatus == 2 ||
- row.approveStatus == 1 ||
- row.approveStatus == 4
- },
- {
- name: "瀹℃牳",
- type: "text",
- clickFun: (row) => {
- openApprovalDia("approval", row);
- },
- disabled: (row) =>
- row.approveUserCurrentId == null ||
- row.approveStatus == 2 ||
- row.approveStatus == 3 ||
- row.approveStatus == 4 ||
- row.approveUserCurrentId !== userStore.id
- },
- {
- name: "璇︽儏",
- type: "text",
- clickFun: (row) => {
- openApprovalDia("view", row);
- },
- },
- ];
- // 鎶ヤ环瀹℃壒锛堢被鍨� 6锛変笉灞曠ず鈥滈檮浠垛�濇搷浣�
- if (!isQuotationType) {
- actionOperations.push({
- name: "闄勪欢",
- type: "text",
- clickFun: (row) => {
- downLoadFile(row);
- },
+ baseColumns.push({
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 230,
+ operation: actionOperations,
});
- }
- baseColumns.push({
- dataType: "action",
- label: "鎿嶄綔",
- align: "center",
- fixed: "right",
- width: 230,
- operation: actionOperations,
+ return baseColumns;
});
-
- return baseColumns;
-});
-const tableData = ref([]);
-const selectedRows = ref([]);
-const tableLoading = ref(false);
-const page = reactive({
- current: 1,
- size: 100,
- total: 0
-});
-const infoFormDia = ref()
-const approvalDia = ref()
-const { proxy } = getCurrentInstance()
+ const tableData = ref([]);
+ const selectedRows = ref([]);
+ const tableLoading = ref(false);
+ const page = reactive({
+ current: 1,
+ size: 100,
+ total: 0,
+ });
+ const infoFormDia = ref();
+ const approvalDia = ref();
+ const { proxy } = getCurrentInstance();
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
- page.current = 1;
- getList();
-};
-const fileListRef = ref(null)
-const downLoadFile = (row) => {
- fileListRef.value.open(row.commonFileList)
+ // 瀹℃壒浜虹淮鎶ゅ璇濇
+ const approverDialogVisible = ref(false);
+ const selectedApprovers = ref([]);
+ const existingApprovers = ref([]); // 宸叉湁鐨勫鎵逛汉鍒楄〃
+ const approverLoading = ref(false); // 鍔犺浇鐘舵��
-}
-const pagination = (obj) => {
- page.current = obj.page;
- page.size = obj.limit;
- getList();
-};
-const getList = () => {
- tableLoading.value = true;
- approveProcessListPage({...page, ...searchForm.value, approveType: currentApproveType.value}).then(res => {
- tableLoading.value = false;
- tableData.value = res.data.records
- page.total = res.data.total;
- }).catch(err => {
- tableLoading.value = false;
- })
-};
-// 瀵煎嚭
-const handleOut = () => {
- const type = currentApproveType.value
- const urlMap = {
- 0: "/approveProcess/exportZero",
- 1: "/approveProcess/exportOne",
- 2: "/approveProcess/exportTwo",
- 3: "/approveProcess/exportThree",
- 4: "/approveProcess/exportFour",
- 5: "/approveProcess/exportFive",
- 6: "/approveProcess/exportSix",
- 7: "/approveProcess/exportSeven",
- }
- const url = urlMap[type] || urlMap[0]
- const nameMap = {
- 0: "鍗忓悓瀹℃壒绠$悊琛�",
- 1: "鍏嚭绠$悊瀹℃壒琛�",
- 2: "璇峰亣绠$悊瀹℃壒琛�",
- 3: "鍑哄樊绠$悊瀹℃壒琛�",
- 4: "鎶ラ攢绠$悊瀹℃壒琛�",
- 5: "閲囪喘鐢宠瀹℃壒琛�",
- 6: "鎶ヤ环瀹℃壒琛�",
- 7: "鍙戣揣瀹℃壒琛�",
- }
- const fileName = nameMap[type] || nameMap[0]
- proxy.download(url, {}, `${fileName}.xlsx`)
-}
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
-};
+ // 瀹℃壒浜哄垪琛ㄦ暟鎹�
+ const approverList = ref([]);
+ const approverTable = ref(null);
-// 鎵撳紑鏂板銆佺紪杈戝脊妗�
-const openForm = (type, row) => {
- nextTick(() => {
- infoFormDia.value?.openDialog(type, row)
- })
-};
-// 鎵撳紑鏂板妫�楠屽脊妗�
-const openApprovalDia = (type, row) => {
- nextTick(() => {
- approvalDia.value?.openDialog(type, row)
- })
-};
+ // 鏌ヨ鍒楄〃
+ /** 鎼滅储鎸夐挳鎿嶄綔 */
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
+ };
+ const fileListRef = ref(null);
+ const downLoadFile = row => {
+ fileListRef.value.open(row.commonFileList);
+ };
+ const pagination = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+ const getList = () => {
+ tableLoading.value = true;
+ approveProcessListPage({
+ ...page,
+ ...searchForm.value,
+ approveType: currentApproveType.value,
+ })
+ .then(res => {
+ tableLoading.value = false;
+ tableData.value = res.data.records;
+ page.total = res.data.total;
+ })
+ .catch(err => {
+ tableLoading.value = false;
+ });
+ };
+ // 瀵煎嚭
+ const handleExport = () => {
+ const type = currentApproveType.value;
+ const urlMap = {
+ 0: "/approveProcess/exportZero",
+ 1: "/approveProcess/exportOne",
+ 2: "/approveProcess/exportTwo",
+ 3: "/approveProcess/exportThree",
+ 4: "/approveProcess/exportFour",
+ 5: "/approveProcess/exportFive",
+ 6: "/approveProcess/exportSix",
+ 7: "/approveProcess/exportSeven",
+ };
+ const url = urlMap[type] || urlMap[0];
+ const nameMap = {
+ 0: "鍗忓悓瀹℃壒绠$悊琛�",
+ 1: "鍏嚭绠$悊瀹℃壒琛�",
+ 2: "璇峰亣绠$悊瀹℃壒琛�",
+ 3: "鍑哄樊绠$悊瀹℃壒琛�",
+ 4: "鎶ラ攢绠$悊瀹℃壒琛�",
+ 5: "閲囪喘鐢宠瀹℃壒琛�",
+ 6: "鎶ヤ环瀹℃壒琛�",
+ 7: "鍙戣揣瀹℃壒琛�",
+ };
+ const fileName = nameMap[type] || nameMap[0];
+ proxy.download(url, {}, `${fileName}.xlsx`);
+ };
-// 鍒犻櫎
-const handleDelete = () => {
- let ids = [];
- if (selectedRows.value.length > 0) {
- ids = selectedRows.value.map((item) => item.approveId);
- } else {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
+ // 瀹℃壒浜虹淮鎶�
+ const handleOut = () => {
+ approverLoading.value = true;
+ // 浠� API 鑾峰彇鎵�鏈夌敤鎴峰垪琛�
+ userListNoPageByTenantId()
+ .then(res => {
+ // 杞崲 API 杩斿洖鐨勬暟鎹粨鏋勪负琛ㄦ牸闇�瑕佺殑鏍煎紡
+ approverList.value = res.data.map(user => ({
+ userId: user.userId,
+ userName: user.nickName,
+ createTime: user.createTime || "",
+ }));
+
+ // 鑾峰彇褰撳墠瀹℃壒绫诲瀷宸叉湁鐨勫鎵逛汉鍒楄〃
+ const currentType = currentApproveType.value;
+ approveUserList({ approveType: currentType })
+ .then(approversRes => {
+ existingApprovers.value = approversRes.data || [];
+ // approverList.value = approversRes.data;
+ selectedApprovers.value = existingApprovers.value;
+ // approverList.value = ;
+ // 鏍囪宸叉湁鐨勫鎵逛汉
+
+ // approverList.value = allUsers.map(user => ({
+ // ...user,
+ // id:
+ // existingApprovers.value.find(
+ // approver => approver.userId === user.userId
+ // )?.id || 0,
+ // isExisting: existingApprovers.value.some(
+ // approver => approver.userId === user.userId
+ // ),
+ // }));
+ console.log(approverList.value, "==approverList.value==");
+ approverDialogVisible.value = true;
+ approverLoading.value = false;
+
+ // 鏇存柊琛ㄦ牸鍕鹃�夌姸鎬�
+ nextTick(() => {
+ if (approverTable.value) {
+ // 鍏堟竻绌烘墍鏈夊嬀閫�
+ approverList.value.forEach(row => {
+ approverTable.value.toggleRowSelection(row, false);
+ });
+ // 鍐嶅嬀閫夊凡鏈夌殑瀹℃壒浜�
+ existingApprovers.value.forEach(existingApprover => {
+ const row = approverList.value.find(
+ user => user.userId === existingApprover.userId
+ );
+ if (row) {
+ approverTable.value.toggleRowSelection(row, true);
+ }
+ });
+ }
+ });
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸叉湁瀹℃壒浜哄垪琛ㄥけ璐�:", err);
+ proxy.$modal.msgError("鑾峰彇宸叉湁瀹℃壒浜哄垪琛ㄥけ璐�");
+ approverLoading.value = false;
+ });
+ })
+ .catch(err => {
+ console.error("鑾峰彇鐢ㄦ埛鍒楄〃澶辫触:", err);
+ proxy.$modal.msgError("鑾峰彇鐢ㄦ埛鍒楄〃澶辫触");
+ approverLoading.value = false;
+ });
+ };
+
+ // 澶勭悊瀹℃壒浜洪�夋嫨
+ const handleApproverSelectionChange = selection => {
+ selectedApprovers.value = selection;
+ };
+
+ // 绉婚櫎瀹℃壒浜�
+ const removeApprover = approver => {
+ selectedApprovers.value = selectedApprovers.value.filter(
+ item => item.id !== approver.id
+ );
+ approverTable.value.toggleRowSelection(approver, false);
+ };
+
+ // 鎻愪氦瀹℃壒浜�
+ const submitApprovers = () => {
+ if (selectedApprovers.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨瀹℃壒浜�");
+ return;
+ }
+
+ const currentType = currentApproveType.value;
+ const selectedIds = selectedApprovers.value.map(approver => approver.userId);
+ const existingIds = existingApprovers.value.map(approver => approver.userId);
+ // 闇�瑕佸垹闄ょ殑瀹℃壒浜猴紙鍘熸湁鐨勪絾鏈閫夋嫨鐨勶級
+ const toDelete = existingApprovers.value
+ .filter(approver => !selectedIds.includes(approver.userId))
+ .map(approver => approver.id);
+
+ // 闇�瑕佹坊鍔犵殑瀹℃壒浜猴紙鏂伴�夋嫨鐨勪絾涓嶅湪鐜版湁鍒楄〃涓殑锛�
+ const toAdd = selectedApprovers.value
+ .filter(approver => !existingIds.includes(approver.userId))
+ .map(approver => ({
+ approveType: currentType,
+ id: 0,
+ userId: approver.userId,
+ userName: approver.userName,
+ }));
+ console.log(toDelete, "==鍒犻櫎==");
+ console.log(toAdd, "==娣诲姞==");
+
+ // 鍏堝垹闄や笉闇�瑕佺殑瀹℃壒浜�
+ const deletePromise =
+ toDelete.length > 0 ? deleteApproveUser(toDelete) : Promise.resolve();
+
+ deletePromise
.then(() => {
- approveProcessDelete(ids).then((res) => {
+ // 鐒跺悗娣诲姞鏂扮殑瀹℃壒浜�
+ if (toAdd.length === 0) {
+ return Promise.resolve();
+ }
+ // 閫愪釜娣诲姞瀹℃壒浜�
+ return Promise.all(toAdd.map(user => addApproveUser(user)));
+ })
+ .then(() => {
+ proxy.$modal.msgSuccess("瀹℃壒浜虹淮鎶ゆ垚鍔�");
+ approverDialogVisible.value = false;
+ selectedApprovers.value = [];
+ })
+ .catch(err => {
+ console.error("瀹℃壒浜虹淮鎶ゅけ璐�:", err);
+ proxy.$modal.msgError("瀹℃壒浜虹淮鎶ゅけ璐�");
+ });
+ };
+ // 琛ㄦ牸閫夋嫨鏁版嵁
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ // 鎵撳紑鏂板銆佺紪杈戝脊妗�
+ const openForm = (type, row) => {
+ nextTick(() => {
+ infoFormDia.value?.openDialog(type, row);
+ });
+ };
+ // 鎵撳紑鏂板妫�楠屽脊妗�
+ const openApprovalDia = (type, row) => {
+ nextTick(() => {
+ approvalDia.value?.openDialog(type, row);
+ });
+ };
+
+ // 鍒犻櫎
+ const handleDelete = () => {
+ let ids = [];
+ if (selectedRows.value.length > 0) {
+ ids = selectedRows.value.map(item => item.approveId);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ approveProcessDelete(ids).then(res => {
proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
getList();
});
@@ -366,29 +591,65 @@
.catch(() => {
proxy.$modal.msg("宸插彇娑�");
});
-};
-onMounted(() => {
- // 鏍规嵁URL鍙傛暟璁剧疆鏍囩椤靛拰鏌ヨ鏉′欢
- const approveType = route.query.approveType;
- const approveId = route.query.approveId;
-
- if (approveType) {
- // 璁剧疆鏍囩椤碉紙approveType 瀵瑰簲 activeTab 鐨� name锛�
- activeTab.value = String(approveType);
- }
-
- if (approveId) {
- // 璁剧疆娴佺▼缂栧彿鏌ヨ鏉′欢
- searchForm.value.approveId = String(approveId);
- }
-
- // 鏌ヨ鍒楄〃
- getList();
-});
+ };
+ onMounted(() => {
+ // 鏍规嵁URL鍙傛暟璁剧疆鏍囩椤靛拰鏌ヨ鏉′欢
+ const approveType = route.query.approveType;
+ const approveId = route.query.approveId;
+
+ if (approveType) {
+ // 璁剧疆鏍囩椤碉紙approveType 瀵瑰簲 activeTab 鐨� name锛�
+ activeTab.value = String(approveType);
+ }
+
+ if (approveId) {
+ // 璁剧疆娴佺▼缂栧彿鏌ヨ鏉′欢
+ searchForm.value.approveId = String(approveId);
+ }
+
+ // 鏌ヨ鍒楄〃
+ getList();
+ });
</script>
<style scoped>
-.approval-tabs {
- margin-bottom: 10px;
-}
+ .approval-tabs {
+ margin-bottom: 10px;
+ }
+
+ /* 瀹℃壒浜虹淮鎶ゅ璇濇鏍峰紡 */
+ .approver-dialog {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+
+ .selected-info {
+ /* margin-top: 20px; */
+ padding: 15px;
+ background: #f5f7fa;
+ border-radius: 4px;
+ }
+
+ .info-title {
+ font-weight: 600;
+ margin-bottom: 10px;
+ color: #303133;
+ }
+
+ .selected-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+
+ .approver-tag {
+ margin-right: 10px;
+ }
+
+ .dialog-footer {
+ width: 100%;
+ display: flex;
+ justify-content: flex-end;
+ }
</style>
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 7167904..4479251 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -53,7 +53,9 @@
<div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
<el-button type="primary"
@click="openForm('add')">鏂板鍙拌处</el-button>
- <el-button type="primary" plain @click="handleImport">瀵煎叆</el-button>
+ <el-button type="primary"
+ plain
+ @click="handleImport">瀵煎叆</el-button>
<el-button @click="handleOut">瀵煎嚭</el-button>
<el-button type="danger"
plain
@@ -94,7 +96,6 @@
prop="availableQuality" />
<el-table-column label="閫�璐ф暟閲�"
prop="returnQuality" />
-
<el-table-column label="绋庣巼(%)"
prop="taxRate" />
<el-table-column label="鍚◣鍗曚环(鍏�)"
@@ -119,11 +120,11 @@
show-overflow-tooltip />
<el-table-column label="閿�鍞悎鍚屽彿"
prop="salesContractNo"
- width="160"
+ width="160"
show-overflow-tooltip />
<el-table-column label="渚涘簲鍟嗗悕绉�"
prop="supplierName"
- width="160"
+ width="160"
show-overflow-tooltip />
<el-table-column label="椤圭洰鍚嶇О"
prop="projectName"
@@ -134,9 +135,8 @@
width="100"
show-overflow-tooltip>
<template #default="scope">
- <el-tag
- :type="getApprovalStatusType(scope.row.approvalStatus)"
- size="small">
+ <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)"
+ size="small">
{{ approvalStatusText[scope.row.approvalStatus] || '鏈煡鐘舵��' }}
</el-tag>
</template>
@@ -189,12 +189,12 @@
@pagination="paginationChange" />
</div>
<FormDialog v-model="dialogFormVisible"
- :title="operationType === 'add' ? '鏂板閲囪喘鍙拌处椤甸潰' : '缂栬緫閲囪喘鍙拌处椤甸潰'"
- :width="'70%'"
- :operation-type="operationType"
- @close="closeDia"
- @confirm="submitForm"
- @cancel="closeDia">
+ :title="operationType === 'add' ? '鏂板閲囪喘鍙拌处椤甸潰' : '缂栬緫閲囪喘鍙拌处椤甸潰'"
+ :width="'70%'"
+ :operation-type="operationType"
+ @close="closeDia"
+ @confirm="submitForm"
+ @cancel="closeDia">
<el-form :model="form"
label-width="140px"
label-position="top"
@@ -236,7 +236,7 @@
<el-option v-for="item in supplierList"
:key="item.id"
:label="item.supplierName"
- :value="item.id" >{{item.supplierName + '---' + item.supplierType}}</el-option>
+ :value="item.id">{{item.supplierName + '---' + item.supplierType}}</el-option>
</el-select>
</el-form-item>
</el-col>
@@ -304,38 +304,33 @@
<template #label>
<div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
<span>瀹℃壒浜洪�夋嫨锛�</span>
- <el-button type="primary" size="small" @click="addApproverNode" icon="Plus">鏂板鑺傜偣</el-button>
+ <el-button type="primary"
+ size="small"
+ @click="addApproverNode"
+ icon="Plus">鏂板鑺傜偣</el-button>
</div>
</template>
<div class="approver-nodes-container">
- <div
- v-for="(node, index) in approverNodes"
- :key="node.id"
- class="approver-node-item"
- >
+ <div v-for="(node, index) in approverNodes"
+ :key="node.id"
+ class="approver-node-item">
<div class="approver-node-header">
<span class="approver-node-label">瀹℃壒鑺傜偣 {{ index + 1 }}</span>
- <el-button
- v-if="approverNodes.length > 1"
- type="danger"
- size="small"
- text
- @click="removeApproverNode(index)"
- icon="Delete"
- >鍒犻櫎</el-button>
+ <el-button v-if="approverNodes.length > 1"
+ type="danger"
+ size="small"
+ text
+ @click="removeApproverNode(index)"
+ icon="Delete">鍒犻櫎</el-button>
</div>
- <el-select
- v-model="node.userId"
- placeholder="璇烽�夋嫨瀹℃壒浜�"
- filterable
- style="width: 100%;"
- >
- <el-option
- v-for="user in userList"
- :key="user.userId"
- :label="user.nickName"
- :value="user.userId"
- />
+ <el-select v-model="node.userId"
+ placeholder="璇烽�夋嫨瀹℃壒浜�"
+ filterable
+ style="width: 100%;">
+ <el-option v-for="user in userListApprove"
+ :key="user.userId"
+ :label="user.userName"
+ :value="user.userId" />
</el-select>
</div>
</div>
@@ -373,11 +368,10 @@
:value="item.templateName">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>{{ item.templateName }}</span>
- <el-icon
- v-if="item.id"
- class="delete-icon"
- @click.stop="handleDeleteTemplate(item)"
- style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
+ <el-icon v-if="item.id"
+ class="delete-icon"
+ @click.stop="handleDeleteTemplate(item)"
+ style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
<Delete />
</el-icon>
</div>
@@ -493,28 +487,24 @@
</el-form>
</FormDialog>
<!-- 瀵煎叆寮圭獥 -->
- <FormDialog
- v-model="importUpload.open"
- :title="importUpload.title"
- :width="'600px'"
- @close="importUpload.open = false"
- @confirm="submitImportFile"
- @cancel="importUpload.open = false"
- >
- <el-upload
- ref="importUploadRef"
- :limit="1"
- accept=".xlsx,.xls"
- :action="importUpload.url"
- :headers="importUpload.headers"
- :before-upload="importUpload.beforeUpload"
- :on-success="importUpload.onSuccess"
- :on-error="importUpload.onError"
- :on-progress="importUpload.onProgress"
- :on-change="importUpload.onChange"
- :auto-upload="false"
- drag
- >
+ <FormDialog v-model="importUpload.open"
+ :title="importUpload.title"
+ :width="'600px'"
+ @close="importUpload.open = false"
+ @confirm="submitImportFile"
+ @cancel="importUpload.open = false">
+ <el-upload ref="importUploadRef"
+ :limit="1"
+ accept=".xlsx,.xls"
+ :action="importUpload.url"
+ :headers="importUpload.headers"
+ :before-upload="importUpload.beforeUpload"
+ :on-success="importUpload.onSuccess"
+ :on-error="importUpload.onError"
+ :on-progress="importUpload.onProgress"
+ :on-change="importUpload.onChange"
+ :auto-upload="false"
+ drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em>
@@ -522,18 +512,20 @@
<template #tip>
<div class="el-upload__tip">
浠呮敮鎸� xls/xlsx锛屽ぇ灏忎笉瓒呰繃 10MB銆�
- <el-button link type="primary" @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
+ <el-button link
+ type="primary"
+ @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
</div>
</template>
</el-upload>
</FormDialog>
<FormDialog v-model="productFormVisible"
- :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
- :width="'40%'"
- :operation-type="productOperationType"
- @close="closeProductDia"
- @confirm="submitProduct"
- @cancel="closeProductDia">
+ :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
+ :width="'40%'"
+ :operation-type="productOperationType"
+ @close="closeProductDia"
+ @confirm="submitProduct"
+ @cancel="closeProductDia">
<el-form :model="productForm"
label-width="140px"
label-position="top"
@@ -694,11 +686,9 @@
</el-row>
</el-form>
</FormDialog>
- <FileListDialog
- ref="fileListRef"
- v-model="fileListDialogVisible"
- title="闄勪欢鍒楄〃"
- />
+ <FileListDialog ref="fileListRef"
+ v-model="fileListDialogVisible"
+ title="闄勪欢鍒楄〃" />
</div>
</template>
@@ -716,8 +706,10 @@
import { Search, Delete } from "@element-plus/icons-vue";
import { ElMessageBox, ElMessage } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
- import FormDialog from '@/components/Dialog/FormDialog.vue';
- import FileListDialog from '@/components/Dialog/FileListDialog.vue';
+ import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
+
+ import FormDialog from "@/components/Dialog/FormDialog.vue";
+ import FileListDialog from "@/components/Dialog/FileListDialog.vue";
import {
getSalesLedgerWithProducts,
addOrUpdateSalesLedgerProduct,
@@ -748,6 +740,7 @@
const productSelectedRows = ref([]);
const modelOptions = ref([]);
const userList = ref([]);
+ const userListApprove = ref([]);
const productOptions = ref([]);
const salesContractList = ref([]);
const supplierList = ref([]);
@@ -770,7 +763,7 @@
const addApproverNode = () => {
approverNodes.value.push({ id: nextApproverId++, userId: null });
};
- const removeApproverNode = (index) => {
+ const removeApproverNode = index => {
approverNodes.value.splice(index, 1);
};
@@ -783,12 +776,12 @@
};
// 鑾峰彇瀹℃壒鐘舵�佹爣绛剧被鍨�
- const getApprovalStatusType = (status) => {
+ const getApprovalStatusType = status => {
const typeMap = {
- 1: "info", // 寰呭鏍� - 鐏拌壊
- 2: "warning", // 瀹℃壒涓� - 姗欒壊
- 3: "success", // 瀹℃壒閫氳繃 - 缁胯壊
- 4: "danger", // 瀹℃壒澶辫触 - 绾㈣壊
+ 1: "info", // 寰呭鏍� - 鐏拌壊
+ 2: "warning", // 瀹℃壒涓� - 姗欒壊
+ 3: "success", // 瀹℃壒閫氳繃 - 缁胯壊
+ 4: "danger", // 瀹℃壒澶辫触 - 绾㈣壊
};
return typeMap[status] || "";
};
@@ -870,7 +863,8 @@
form.value.paymentMethod = matchedTemplate.paymentMethod;
}
// 妯℃澘鏁版嵁涓殑浜у搧瀛楁鏄� productList锛岄渶瑕佽浆鎹负 productData
- productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
+ productData.value =
+ matchedTemplate.productList || matchedTemplate.productData || [];
} else {
// 鏈尮閰嶅埌宸叉湁妯℃澘锛岃涓烘柊妯℃澘
currentTemplateId.value = null;
@@ -1004,7 +998,7 @@
url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import",
headers: { Authorization: "Bearer " + getToken() },
isUploading: false,
- beforeUpload: (file) => {
+ beforeUpload: file => {
const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isExcel) {
@@ -1053,7 +1047,11 @@
// 涓嬭浇瀵煎叆妯℃澘锛堝鍚庣璺緞涓嶅悓锛屽彲鍦ㄦ澶勮皟鏁达級
const downloadTemplate = () => {
- proxy.download("/purchase/ledger/exportTemplate", {}, "閲囪喘鍙拌处瀵煎叆妯℃澘.xlsx");
+ proxy.download(
+ "/purchase/ledger/exportTemplate",
+ {},
+ "閲囪喘鍙拌处瀵煎叆妯℃澘.xlsx"
+ );
};
const submitImportFile = () => {
@@ -1118,8 +1116,8 @@
// 妫�鏌ユ槸鍚︽湁浜у搧鏁版嵁
if (!productData.value || productData.value.length === 0) {
ElMessage({
- message: '璇峰厛娣诲姞浜у搧淇℃伅',
- type: 'warning',
+ message: "璇峰厛娣诲姞浜у搧淇℃伅",
+ type: "warning",
});
return;
}
@@ -1130,7 +1128,7 @@
.filter(node => node.userId)
.map(node => node.userId)
.join(",");
-
+
let params = {
productData: proxy.HaveJson(productData.value),
supplierId: form.value.supplierId,
@@ -1140,7 +1138,12 @@
approveUserIds: approveUserIds,
templateName: templateName.value.trim(),
};
- console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value);
+ console.log(
+ "template params ===>",
+ params,
+ "currentTemplateId:",
+ currentTemplateId.value
+ );
// 濡傛灉 currentTemplateId 鏈夊�硷紝璇存槑褰撳墠鏄�滅紪杈戝凡鏈夋ā鏉库�� 鈫� 璋冪敤鏇存柊鎺ュ彛
// 鍚﹀垯涓衡�滄柊寤烘ā鏉库�� 鈫� 璋冪敤鏂板鎺ュ彛
@@ -1279,7 +1282,7 @@
return;
}
}
-
+
await getTemplateList();
operationType.value = type;
form.value = {};
@@ -1298,7 +1301,9 @@
getSalesNo(),
getOptions(),
]);
-
+ approveUserList({ approveType: 5 }).then(res => {
+ userListApprove.value = res.data;
+ });
userList.value = userRes.data || [];
salesContractList.value = salesRes || [];
// 渚涘簲鍟嗚繃婊ゅ嚭isWhite=0 鐨勬暟鎹�
@@ -1334,7 +1339,7 @@
const approverIds = purchaseRes.approveUserIds.split(",");
approverNodes.value = approverIds.map((id, index) => ({
id: index + 1,
- userId: Number(id)
+ userId: Number(id),
}));
nextApproverId = approverIds.length + 1;
}
@@ -1412,8 +1417,10 @@
proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
return;
}
- const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
-
+ const approveUserIds = approverNodes.value
+ .map(node => node.userId)
+ .join(",");
+
if (productData.value.length > 0) {
// 鏂板鏃讹紝闇�瑕佷粠姣忎釜浜у搧瀵硅薄涓垹闄� id 瀛楁
let processedProductData = productData.value;
@@ -1470,17 +1477,17 @@
productForm.value = {};
proxy.resetForm("productFormRef");
productFormVisible.value = true;
-
+
// 鍏堣幏鍙栦骇鍝侀�夐」锛岀‘淇濇暟鎹姞杞藉畬鎴�
await getProductOptions();
-
+
// 绛夊緟 DOM 鏇存柊
await nextTick();
-
+
if (type === "edit") {
// 澶嶅埗琛屾暟鎹�
productForm.value = { ...row };
-
+
// 濡傛灉鏄粠妯℃澘鍔犺浇鐨勬暟鎹紝鍙兘娌℃湁 productId 鍜� productModelId
// 闇�瑕佹牴鎹� productCategory 鍜� specificationModel 鏉ユ煡鎵惧搴旂殑 ID
if (!productForm.value.productId && productForm.value.productCategory) {
@@ -1491,25 +1498,34 @@
return nodes[i].value;
}
if (nodes[i].children && nodes[i].children.length > 0) {
- const found = findProductIdByCategory(nodes[i].children, categoryName);
+ const found = findProductIdByCategory(
+ nodes[i].children,
+ categoryName
+ );
if (found) return found;
}
}
return null;
};
-
- const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory);
+
+ const productId = findProductIdByCategory(
+ productOptions.value,
+ productForm.value.productCategory
+ );
if (productId) {
productForm.value.productId = productId;
// 鑾峰彇鍨嬪彿鍒楄〃骞剁瓑寰呭畬鎴�
const modelRes = await modelList({ id: productId });
modelOptions.value = modelRes;
-
+
// 绛夊緟 DOM 鏇存柊
await nextTick();
-
+
// 鏍规嵁 specificationModel 鏌ユ壘 productModelId
- if (productForm.value.specificationModel && modelOptions.value.length > 0) {
+ if (
+ productForm.value.specificationModel &&
+ modelOptions.value.length > 0
+ ) {
const modelItem = modelOptions.value.find(
item => item.model === productForm.value.specificationModel
);
@@ -1523,15 +1539,15 @@
} else if (productForm.value.productId) {
// 濡傛灉鏈� productId锛屾甯稿姞杞藉瀷鍙峰垪琛�
await getModels(productForm.value.productId);
-
+
// 绛夊緟 DOM 鏇存柊
await nextTick();
-
+
if (productForm.value.productModelId) {
getProductModel(productForm.value.productModelId);
}
}
-
+
// 鏈�鍚庡啀绛夊緟涓�娆� DOM 鏇存柊锛岀‘淇濇墍鏈夋暟鎹兘宸茶缃�
await nextTick();
}
@@ -1654,11 +1670,9 @@
delProduct(ids).then(res => {
proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
closeProductDia();
- getPurchaseById({ id: currentId.value, type: 2 }).then(
- res => {
- productData.value = res.productData;
- }
- );
+ getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
+ productData.value = res.productData;
+ });
});
})
.catch(() => {
@@ -1866,12 +1880,12 @@
};
// 鍒犻櫎妯℃澘
- const handleDeleteTemplate = async (item) => {
+ const handleDeleteTemplate = async item => {
if (!item.id) {
proxy.$modal.msgWarning("鏃犳硶鍒犻櫎璇ユā鏉�");
return;
}
-
+
try {
await ElMessageBox.confirm(
`纭畾瑕佸垹闄ゆā鏉�"${item.templateName}"鍚楋紵`,
@@ -1882,7 +1896,7 @@
type: "warning",
}
);
-
+
const res = await delPurchaseTemplate([item.id]);
if (res && res.code === 200) {
ElMessage({
@@ -1935,7 +1949,7 @@
display: flex;
align-items: center;
}
-
+
// 瀹℃壒浜鸿妭鐐瑰鍣ㄦ牱寮�
.approver-nodes-container {
display: flex;
@@ -1946,7 +1960,7 @@
border-radius: 4px;
border: 1px solid #e4e7ed;
}
-
+
.approver-node-item {
flex: 0 0 calc(33.333% - 12px);
min-width: 200px;
@@ -1955,38 +1969,38 @@
border-radius: 4px;
border: 1px solid #dcdfe6;
transition: all 0.3s;
-
+
&:hover {
border-color: #409eff;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
}
}
-
+
.approver-node-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
-
+
.approver-node-label {
font-size: 13px;
font-weight: 500;
color: #606266;
}
-
+
@media (max-width: 1200px) {
.approver-node-item {
flex: 0 0 calc(50% - 8px);
}
}
-
+
@media (max-width: 768px) {
.approver-node-item {
flex: 0 0 100%;
}
}
-
+
// 鍒犻櫎鍥炬爣鏍峰紡
.delete-icon {
transition: all 0.3s;
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 0965cf1..b88986c 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -1,54 +1,75 @@
<template>
<div class="app-container">
<div class="search_form">
- <el-form :model="searchForm" :inline="true">
+ <el-form :model="searchForm"
+ :inline="true">
<el-form-item label="瀹㈡埛鍚嶇О锛�">
- <el-select
- v-model="searchForm.customerId"
- filterable
- placeholder="璇烽�夋嫨瀹㈡埛鍚嶇О"
- clearable
- style="width: 220px"
- @change="handleQuery"
- >
- <el-option
- v-for="item in customerOption"
- :key="item.id"
- :label="item.customerName"
- :value="item.id"
- >
+ <el-select v-model="searchForm.customerId"
+ filterable
+ placeholder="璇烽�夋嫨瀹㈡埛鍚嶇О"
+ clearable
+ style="width: 220px"
+ @change="handleQuery">
+ <el-option v-for="item in customerOption"
+ :key="item.id"
+ :label="item.customerName"
+ :value="item.id">
{{ item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber }}
</el-option>
</el-select>
</el-form-item>
<el-form-item label="閿�鍞悎鍚屽彿锛�">
- <el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
- @change="handleQuery" />
+ <el-input v-model="searchForm.salesContractNo"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ @change="handleQuery" />
</el-form-item>
<el-form-item label="椤圭洰鍚嶇О锛�">
- <el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
- @change="handleQuery" />
+ <el-input v-model="searchForm.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ @change="handleQuery" />
</el-form-item>
<el-form-item label="褰曞叆鏃ユ湡锛�">
- <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
- placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+ <el-date-picker v-model="searchForm.entryDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="daterange"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="changeDaterange" />
</el-form-item>
<el-form-item label="鍙戣揣鐘舵�侊細">
- <el-select v-model="searchForm.deliveryStatus" placeholder="璇烽�夋嫨" clearable style="width: 140px">
- <el-option label="鏈彂璐�" :value="1" />
- <el-option label="瀹℃壒涓�" :value="2" />
- <el-option label="瀹℃壒澶辫触" :value="3" />
- <el-option label="宸插彂璐�" :value="4" />
+ <el-select v-model="searchForm.deliveryStatus"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 140px">
+ <el-option label="鏈彂璐�"
+ :value="1" />
+ <el-option label="瀹℃壒涓�"
+ :value="2" />
+ <el-option label="瀹℃壒澶辫触"
+ :value="3" />
+ <el-option label="宸插彂璐�"
+ :value="4" />
</el-select>
</el-form-item>
<el-form-item label="鍏ュ簱鐘舵�侊細">
- <el-select v-model="searchForm.stockStatus" placeholder="璇烽�夋嫨" clearable style="width: 140px">
- <el-option label="鏈叆搴�" :value="0" />
- <el-option label="宸插叆搴�" :value="1" />
+ <el-select v-model="searchForm.stockStatus"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 140px">
+ <el-option label="鏈叆搴�"
+ :value="0" />
+ <el-option label="宸插叆搴�"
+ :value="1" />
</el-select>
</el-form-item>
<el-form-item>
- <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
+ <el-button type="primary"
+ @click="handleQuery"> 鎼滅储 </el-button>
</el-form-item>
</el-form>
</div>
@@ -58,128 +79,191 @@
<OtherAmountMaintenanceButton />
<ProcessFlowMaintenanceButton />
</div>
- <ProcessFlowConfigSelectDialog
- v-model:visible="processFlowSelectDialogVisible"
- :default-route-id="processFlowSelectDefaultRouteId"
- :bound-route-name="processFlowSelectBoundRouteName"
- @confirm="handleProcessFlowSelectConfirm"
- />
- <el-space wrap>
- <el-button type="primary" @click="handleSalesStock">鍏ュ簱</el-button>
- <el-button type="primary" @click="openForm('add')">鏂板鍙拌处</el-button>
- <el-button type="primary" @click="handleBulkDelivery">鍙戣揣</el-button>
- <el-button type="primary" plain @click="handleImport">瀵煎叆</el-button>
- <el-button @click="handleOut">瀵煎嚭</el-button>
-
- <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
-
- <el-dropdown @command="handlePrintCommand">
- <el-button type="primary" plain>
- 鎵撳嵃鍗曟嵁<el-icon class="el-icon--right">
- <ArrowDown />
- </el-icon>
- </el-button>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item command="finishedProcessCard">鐢熶骇娴佺▼鍗★紙鎴愬搧锛�</el-dropdown-item>
- <el-dropdown-item command="salesOrder">閿�鍞鍗�</el-dropdown-item>
- <el-dropdown-item command="salesDeliveryNote">閿�鍞彂璐у崟</el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- <el-button type="primary" plain @click="handlePrintLabel">鎵撳嵃鏍囩</el-button>
- </el-space>
+ <ProcessFlowConfigSelectDialog v-model:visible="processFlowSelectDialogVisible"
+ :default-route-id="processFlowSelectDefaultRouteId"
+ :bound-route-name="processFlowSelectBoundRouteName"
+ @confirm="handleProcessFlowSelectConfirm" />
+ <el-space wrap>
+ <el-button type="primary"
+ @click="handleSalesStock">鍏ュ簱</el-button>
+ <el-button type="primary"
+ @click="openForm('add')">鏂板鍙拌处</el-button>
+ <el-button type="primary"
+ @click="handleBulkDelivery">鍙戣揣</el-button>
+ <el-button type="primary"
+ plain
+ @click="handleImport">瀵煎叆</el-button>
+ <el-button @click="handleOut">瀵煎嚭</el-button>
+ <el-button type="danger"
+ plain
+ @click="handleDelete">鍒犻櫎</el-button>
+ <el-dropdown @command="handlePrintCommand">
+ <el-button type="primary"
+ plain>
+ 鎵撳嵃鍗曟嵁<el-icon class="el-icon--right">
+ <ArrowDown />
+ </el-icon>
+ </el-button>
+ <template #dropdown>
+ <el-dropdown-menu>
+ <el-dropdown-item command="finishedProcessCard">鐢熶骇娴佺▼鍗★紙鎴愬搧锛�</el-dropdown-item>
+ <el-dropdown-item command="salesOrder">閿�鍞鍗�</el-dropdown-item>
+ <el-dropdown-item command="salesDeliveryNote">閿�鍞彂璐у崟</el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+ <el-button type="primary"
+ plain
+ @click="handlePrintLabel">鎵撳嵃鏍囩</el-button>
+ </el-space>
</div>
- <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
- :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
- :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
- <el-table-column align="center" type="selection" width="55" fixed="left"/>
- <el-table-column type="expand" width="60" fixed="left">
+ <el-table :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ :expand-row-keys="expandedRowKeys"
+ :row-key="(row) => row.id"
+ :row-class-name="tableRowClassName"
+ show-summary
+ style="width: 100%"
+ :summary-method="summarizeMainTable"
+ @expand-change="expandChange"
+ height="calc(100vh - 18.5em)">
+ <el-table-column align="center"
+ type="selection"
+ width="55"
+ fixed="left" />
+ <el-table-column type="expand"
+ width="60"
+ fixed="left">
<template #default="props">
- <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
- <el-table-column align="center" label="搴忓彿" type="index"/>
- <el-table-column label="妤煎眰缂栧彿" prop="floorCode" min-width="100" show-overflow-tooltip />
- <el-table-column label="浜у搧澶х被" prop="productCategory" />
- <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
- <el-table-column label="鍘氬害" prop="thickness" min-width="90">
+ <el-table :data="props.row.children"
+ border
+ show-summary
+ :summary-method="summarizeChildrenTable">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index" />
+ <el-table-column label="妤煎眰缂栧彿"
+ prop="floorCode"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="浜у搧澶х被"
+ prop="productCategory" />
+ <el-table-column label="瑙勬牸鍨嬪彿"
+ prop="specificationModel" />
+ <el-table-column label="鍘氬害"
+ prop="thickness"
+ min-width="90">
<template #default="scope">
{{ scope.row.thickness ?? "" }}
</template>
</el-table-column>
- <el-table-column label="瀹�(mm)" prop="width" min-width="80">
- <template #default="scope">
- {{ scope.row.width ?? "" }}
- </template>
- </el-table-column>
- <el-table-column label="楂�(mm)" prop="height" min-width="80">
- <template #default="scope">
- {{ scope.row.height ?? "" }}
- </template>
- </el-table-column>
- <el-table-column label="鍛ㄩ暱(cm)" prop="perimeter" min-width="90">
- <template #default="scope">
- {{ scope.row.perimeter ?? "" }}
- </template>
- </el-table-column>
- <el-table-column label="鎬婚潰绉�(cm虏)" prop="actualTotalArea" min-width="100">
- <template #default="scope">
- {{ scope.row.actualTotalArea ?? "" }}
- </template>
- </el-table-column>
- <el-table-column label="鍔犲伐瑕佹眰" prop="processRequirement" min-width="120"
- show-overflow-tooltip />
- <el-table-column label="澶囨敞" prop="remark" min-width="120" show-overflow-tooltip />
- <el-table-column label="閲嶇" prop="heavyBox" min-width="80">
- <template #default="scope">
- {{ scope.row.heavyBox ?? "" }}
- </template>
- </el-table-column>
- <el-table-column label="浜у搧鐘舵��"
- width="100px"
- align="center">
+ <el-table-column label="瀹�(mm)"
+ prop="width"
+ min-width="80">
<template #default="scope">
-
- <el-tag v-if="scope.row.approveStatus === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)"
- type="success">鍏呰冻</el-tag>
- <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)"
- type="success">宸插嚭搴�</el-tag>
- <el-tag v-else type="danger">涓嶈冻</el-tag>
+ {{ scope.row.width ?? "" }}
</template>
</el-table-column>
- <!-- <el-table-column label="鍙戣揣鐘舵��" width="140" align="center">
+ <el-table-column label="楂�(mm)"
+ prop="height"
+ min-width="80">
+ <template #default="scope">
+ {{ scope.row.height ?? "" }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鍛ㄩ暱(cm)"
+ prop="perimeter"
+ min-width="90">
+ <template #default="scope">
+ {{ scope.row.perimeter ?? "" }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鎬婚潰绉�(cm虏)"
+ prop="actualTotalArea"
+ min-width="100">
+ <template #default="scope">
+ {{ scope.row.actualTotalArea ?? "" }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鍔犲伐瑕佹眰"
+ prop="processRequirement"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="閲嶇"
+ prop="heavyBox"
+ min-width="80">
+ <template #default="scope">
+ {{ scope.row.heavyBox ?? "" }}
+ </template>
+ </el-table-column>
+ <el-table-column label="浜у搧鐘舵��"
+ width="100px"
+ align="center">
+ <template #default="scope">
+ <el-tag v-if="scope.row.approveStatus === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)"
+ type="success">鍏呰冻</el-tag>
+ <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)"
+ type="success">宸插嚭搴�</el-tag>
+ <el-tag v-else
+ type="danger">涓嶈冻</el-tag>
+ </template>
+ </el-table-column>
+ <!-- <el-table-column label="鍙戣揣鐘舵��" width="140" align="center">
<template #default="scope">
<el-tag :type="getShippingStatusType(scope.row)" size="small">
{{ getShippingStatusText(scope.row) }}
</el-tag>
</template>
</el-table-column> -->
- <el-table-column label="蹇�掑叕鍙�" prop="expressCompany" show-overflow-tooltip />
- <el-table-column label="蹇�掑崟鍙�" prop="expressNumber" show-overflow-tooltip />
- <el-table-column label="鍙戣揣杞︾墝" minWidth="100px" align="center">
+ <el-table-column label="蹇�掑叕鍙�"
+ prop="expressCompany"
+ show-overflow-tooltip />
+ <el-table-column label="蹇�掑崟鍙�"
+ prop="expressNumber"
+ show-overflow-tooltip />
+ <el-table-column label="鍙戣揣杞︾墝"
+ minWidth="100px"
+ align="center">
<template #default="scope">
<div>
- <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
- <el-tag v-else type="info">-</el-tag>
+ <el-tag type="success"
+ v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
+ <el-tag v-else
+ type="info">-</el-tag>
</div>
</template>
</el-table-column>
- <el-table-column label="鍙戣揣鏃ユ湡"
- minWidth="100px"
- align="center">
+ <el-table-column label="鍙戣揣鏃ユ湡"
+ minWidth="100px"
+ align="center">
<template #default="scope">
<div>
<div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
- <el-tag v-else
- type="info">-</el-tag>
+ <el-tag v-else
+ type="info">-</el-tag>
</div>
</template>
</el-table-column>
- <el-table-column label="鏁伴噺" prop="quantity" />
- <el-table-column label="绋庣巼(%)" prop="taxRate" />
- <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
- <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
- <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
- <!--鎿嶄綔-->
+ <el-table-column label="鏁伴噺"
+ prop="quantity" />
+ <el-table-column label="绋庣巼(%)"
+ prop="taxRate" />
+ <el-table-column label="鍚◣鍗曚环(鍏�)"
+ prop="taxInclusiveUnitPrice"
+ :formatter="formattedNumber" />
+ <el-table-column label="鍚◣鎬讳环(鍏�)"
+ prop="taxInclusiveTotalPrice"
+ :formatter="formattedNumber" />
+ <el-table-column label="涓嶅惈绋庢�讳环(鍏�)"
+ prop="taxExclusiveTotalPrice"
+ :formatter="formattedNumber" />
+ <!--鎿嶄綔-->
<!-- <el-table-column Width="60px" label="鎿嶄綔" align="center">
<template #default="scope">
<el-button
@@ -194,77 +278,169 @@
</el-table>
</template>
</el-table-column>
- <el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column label="閿�鍞悎鍚屽彿" prop="salesContractNo" width="180" show-overflow-tooltip />
- <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" width="300" show-overflow-tooltip />
- <el-table-column label="涓氬姟鍛�" prop="salesman" width="100" show-overflow-tooltip />
- <el-table-column label="椤圭洰鍚嶇О" prop="projectName" width="180" show-overflow-tooltip />
- <el-table-column label="鍚堝悓閲戦(鍏�)" prop="contractAmount" width="220" show-overflow-tooltip
- :formatter="formattedNumber" />
- <el-table-column label="鍙戣揣鐘舵��" width="140" align="center">
- <template #default="scope">
- <el-tag v-if="Number(scope.row.deliveryStatus) === 1" type="info">鏈彂璐�</el-tag>
- <el-tag v-else-if="Number(scope.row.deliveryStatus) === 2" type="warning">瀹℃壒涓�</el-tag>
- <el-tag v-else-if="Number(scope.row.deliveryStatus) === 3" type="danger">瀹℃壒涓嶉�氳繃</el-tag>
- <el-tag v-else-if="Number(scope.row.deliveryStatus) === 4" type="primary">瀹℃壒閫氳繃</el-tag>
- <el-tag v-else-if="Number(scope.row.deliveryStatus) === 5" type="success">宸插彂璐�</el-tag>
- <el-tag v-else type="info">-</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="鍏ュ簱鐘舵��" width="120" align="center">
- <template #default="scope">
- <el-tag v-if="Number(scope.row.stockStatus) === 0" type="info">鏈叆搴�</el-tag>
- <el-tag v-else-if="Number(scope.row.stockStatus) === 1" type="success">宸插叆搴�</el-tag>
- <el-tag v-else type="info">-</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="褰曞叆浜�" prop="entryPersonName" width="100" show-overflow-tooltip />
- <el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
- <el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
- <el-table-column label="浜や粯鏃ユ湡" prop="deliveryDate" width="120" show-overflow-tooltip />
- <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
- <el-table-column fixed="right" label="鎿嶄綔" width="200" align="center">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="閿�鍞悎鍚屽彿"
+ prop="salesContractNo"
+ width="180"
+ show-overflow-tooltip />
+ <el-table-column label="瀹㈡埛鍚嶇О"
+ prop="customerName"
+ width="300"
+ show-overflow-tooltip />
+ <el-table-column label="涓氬姟鍛�"
+ prop="salesman"
+ width="100"
+ show-overflow-tooltip />
+ <el-table-column label="椤圭洰鍚嶇О"
+ prop="projectName"
+ width="180"
+ show-overflow-tooltip />
+ <el-table-column label="鍚堝悓閲戦(鍏�)"
+ prop="contractAmount"
+ width="220"
+ show-overflow-tooltip
+ :formatter="formattedNumber" />
+ <el-table-column label="鍙戣揣鐘舵��"
+ width="140"
+ align="center">
<template #default="scope">
- <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">缂栬緫</el-button>
- <el-button link type="primary" @click="openProcessFlowSelect(scope.row)" :disabled="!scope.row.isEdit">宸ヨ壓璺嚎</el-button>
- <el-button link type="primary" @click="downLoadFile(scope.row)">闄勪欢</el-button>
+ <el-tag v-if="Number(scope.row.deliveryStatus) === 1"
+ type="info">鏈彂璐�</el-tag>
+ <el-tag v-else-if="Number(scope.row.deliveryStatus) === 2"
+ type="warning">瀹℃壒涓�</el-tag>
+ <el-tag v-else-if="Number(scope.row.deliveryStatus) === 3"
+ type="danger">瀹℃壒涓嶉�氳繃</el-tag>
+ <el-tag v-else-if="Number(scope.row.deliveryStatus) === 4"
+ type="primary">瀹℃壒閫氳繃</el-tag>
+ <el-tag v-else-if="Number(scope.row.deliveryStatus) === 5"
+ type="success">宸插彂璐�</el-tag>
+ <el-tag v-else
+ type="info">-</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍏ュ簱鐘舵��"
+ width="120"
+ align="center">
+ <template #default="scope">
+ <el-tag v-if="Number(scope.row.stockStatus) === 0"
+ type="info">鏈叆搴�</el-tag>
+ <el-tag v-else-if="Number(scope.row.stockStatus) === 1"
+ type="success">宸插叆搴�</el-tag>
+ <el-tag v-else
+ type="info">-</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="褰曞叆浜�"
+ prop="entryPersonName"
+ width="100"
+ show-overflow-tooltip />
+ <el-table-column label="褰曞叆鏃ユ湡"
+ prop="entryDate"
+ width="120"
+ show-overflow-tooltip />
+ <el-table-column label="绛捐鏃ユ湡"
+ prop="executionDate"
+ width="120"
+ show-overflow-tooltip />
+ <el-table-column label="浜や粯鏃ユ湡"
+ prop="deliveryDate"
+ width="120"
+ show-overflow-tooltip />
+ <el-table-column label="澶囨敞"
+ prop="remarks"
+ width="200"
+ show-overflow-tooltip />
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="200"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="primary"
+ @click="openForm('edit', scope.row)"
+ :disabled="!scope.row.isEdit">缂栬緫</el-button>
+ <el-button link
+ type="primary"
+ @click="openProcessFlowSelect(scope.row)"
+ :disabled="!scope.row.isEdit">宸ヨ壓璺嚎</el-button>
+ <el-button link
+ type="primary"
+ @click="downLoadFile(scope.row)">闄勪欢</el-button>
</template>
</el-table-column>
</el-table>
- <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
- :page="page.current" :limit="page.size" @pagination="paginationChange" />
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
</div>
- <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '鏂板閿�鍞彴璐﹂〉闈�' : '缂栬緫閿�鍞彴璐﹂〉闈�'" :width="'70%'"
- :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
- <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
- <!-- 鎶ヤ环鍗曞鍏ュ叆鍙o細鏀惧湪琛ㄥ崟椤堕儴锛岄�夋嫨鍚庡弽鏄惧鎴�/涓氬姟鍛樼瓑 -->
- <el-row v-if="operationType === 'add'" style="margin-bottom: 10px;">
- <el-col :span="24" style="text-align: right;">
- <el-button type="primary" plain @click="openQuotationDialog">
- 浠庨攢鍞姤浠峰鍏�
- </el-button>
- </el-col>
- </el-row>
+ <FormDialog v-model="dialogFormVisible"
+ :title="operationType === 'add' ? '鏂板閿�鍞彴璐﹂〉闈�' : '缂栬緫閿�鍞彴璐﹂〉闈�'"
+ :width="'70%'"
+ :operation-type="operationType"
+ @close="closeDia"
+ @confirm="submitForm"
+ @cancel="closeDia">
+ <el-form :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
+ <!-- 鎶ヤ环鍗曞鍏ュ叆鍙o細鏀惧湪琛ㄥ崟椤堕儴锛岄�夋嫨鍚庡弽鏄惧鎴�/涓氬姟鍛樼瓑 -->
+ <el-row v-if="operationType === 'add'"
+ style="margin-bottom: 10px;">
+ <el-col :span="24"
+ style="text-align: right;">
+ <el-button type="primary"
+ plain
+ @click="openQuotationDialog">
+ 浠庨攢鍞姤浠峰鍏�
+ </el-button>
+ </el-col>
+ </el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="閿�鍞悎鍚屽彿锛�" prop="salesContractNo">
- <el-input v-model="form.salesContractNo" placeholder="鑷姩鐢熸垚" clearable disabled />
+ <el-form-item label="閿�鍞悎鍚屽彿锛�"
+ prop="salesContractNo">
+ <el-input v-model="form.salesContractNo"
+ placeholder="鑷姩鐢熸垚"
+ clearable
+ disabled />
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="涓氬姟鍛橈細" prop="salesman">
- <el-select v-model="form.salesman" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'">
- <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
- :value="item.nickName" />
+ <el-form-item label="涓氬姟鍛橈細"
+ prop="salesman">
+ <el-select v-model="form.salesman"
+ placeholder="璇烽�夋嫨"
+ clearable
+ :disabled="operationType === 'view'">
+ <el-option v-for="item in userList"
+ :key="item.nickName"
+ :label="item.nickName"
+ :value="item.nickName" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerId">
- <el-select v-model="form.customerId" filterable placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'">
- <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
+ <el-form-item label="瀹㈡埛鍚嶇О锛�"
+ prop="customerId">
+ <el-select v-model="form.customerId"
+ filterable
+ placeholder="璇烽�夋嫨"
+ clearable
+ :disabled="operationType === 'view'">
+ <el-option v-for="item in customerOption"
+ :key="item.id"
+ :label="item.customerName"
+ :value="item.id">
{{
item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
}}
@@ -272,3539 +448,3763 @@
</el-select>
</el-form-item>
</el-col>
- <el-col :span="12">
- <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
- <el-input v-model="form.projectName" placeholder="璇疯緭鍏�" clearable :disabled="operationType === 'view'" />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="绛捐鏃ユ湡锛�" prop="executionDate">
- <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
- format="YYYY-MM-DD" type="date" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'" />
- </el-form-item>
- </el-col>
<el-col :span="12">
- <el-form-item label="浜よ揣鏃ユ湡锛�" prop="deliveryDate">
- <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
- type="date" placeholder="璇烽�夋嫨" clearable />
+ <el-form-item label="椤圭洰鍚嶇О锛�"
+ prop="projectName">
+ <el-input v-model="form.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :disabled="operationType === 'view'" />
</el-form-item>
</el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="褰曞叆浜猴細" prop="entryPerson">
- <el-select v-model="form.entryPerson"
- filterable
- default-first-option
- :reserve-keyword="false" placeholder="璇烽�夋嫨" clearable @change="changs">
- <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
- <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
- type="date" placeholder="璇烽�夋嫨" clearable />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row>
- <el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
- <el-button
- v-if="operationType !== 'view'"
- type="primary"
- :disabled="hasEditingProductRow()"
- @click="addProductInline"
- >
- 娣诲姞
- </el-button>
- <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >鍒犻櫎</el-button>
- </el-form-item>
- </el-row>
- <el-table :data="productData" border @selection-change="productSelected" show-summary
- :summary-method="summarizeMainTable">
- <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
- :selectable="(row) => !isProductShipped(row)" />
- <el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column label="浜у搧澶х被" prop="productCategory" min-width="160">
- <template #default="scope">
- <el-tree-select
- v-if="scope.row.__editing"
- v-model="scope.row.__productCategoryId"
- placeholder="璇烽�夋嫨"
- clearable
- filterable
- check-strictly
- :data="productOptions"
- :render-after-expand="false"
- style="width: 100%"
- :filter-node-method="filterProductCategoryNode"
- @change="(val) => handleInlineProductCategoryChange(scope.row, val)"
- />
- <span v-else>{{ scope.row.productCategory ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" min-width="160">
- <template #default="scope">
- <el-select
- v-if="scope.row.__editing"
- v-model="scope.row.productModelId"
- placeholder="璇烽�夋嫨"
- clearable
- filterable
- style="width: 100%"
- @change="(val) => handleInlineProductModelChange(scope.row, val)"
- >
- <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
- </el-select>
- <span v-else>{{ scope.row.specificationModel ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="鍘氬害(mm)" prop="thickness" min-width="160">
- <template #default="scope">
- <el-input-number
- v-if="scope.row.__editing"
- v-model="scope.row.thickness"
- :min="0"
- :step="0.000000000000001"
- :precision="15"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- />
- <span v-else>{{ scope.row.thickness ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="瀹�(mm)" prop="width" min-width="160">
- <template #default="scope">
- <el-input-number
- v-if="scope.row.__editing"
- v-model="scope.row.width"
- :min="0"
- :step="1"
- :precision="2"
- style="width:100%"
- placeholder="璇疯緭鍏�"
- clearable
- @change="() => handleInlineSizeChange(scope.row)"
- @input="() => handleInlineSizeChange(scope.row)"
- />
- <span v-else>{{ scope.row.width ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="楂�(mm)" prop="height" min-width="160">
- <template #default="scope">
- <el-input-number
- v-if="scope.row.__editing"
- v-model="scope.row.height"
- :min="0"
- :step="1"
- :precision="2"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- @change="() => handleInlineSizeChange(scope.row)"
- @input="() => handleInlineSizeChange(scope.row)"
- />
- <span v-else>{{ scope.row.height ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="缁撶畻鍗曠墖闈㈢Н(銕�)" prop="settlePieceArea" min-width="160">
- <template #default="scope">
- <el-input-number
- v-if="scope.row.__editing"
- v-model="scope.row.settlePieceArea"
- :min="0"
- :step="0.00001"
- :precision="5"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- @change="() => handleInlineSettleAreaChange(scope.row)"
- />
- <span v-else>{{ scope.row.settlePieceArea ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="鏁伴噺" prop="quantity" min-width="150">
- <template #default="scope">
- <el-input-number
- v-if="scope.row.__editing"
- v-model="scope.row.quantity"
- :step="0.1"
- :min="0"
- :precision="2"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- @change="() => handleInlineQuantityChange(scope.row)"
- @input="() => handleInlineQuantityChange(scope.row)"
- />
- <span v-else>{{ scope.row.quantity ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="闈㈢Н(m虏)" prop="actualTotalArea" min-width="160">
- <template #default="scope">
- <el-input-number
- v-if="scope.row.__editing"
- v-model="scope.row.actualTotalArea"
- :min="0"
- :step="0.00001"
- :precision="5"
- style="width: 100%"
- placeholder="鑷姩璁$畻"
- />
- <span v-else>{{ scope.row.actualTotalArea ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" min-width="140">
- <template #default="scope">
- <el-input-number
- v-if="scope.row.__editing"
- :step="0.01"
- :min="0"
- :precision="2"
- style="width: 100%"
- v-model="scope.row.taxInclusiveUnitPrice"
- placeholder="璇疯緭鍏�"
- clearable
- @change="() => handleInlineUnitPriceChange(scope.row)"
- @input="() => handleInlineUnitPriceChange(scope.row)"
- />
- <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice ?? 0) }}</span>
- </template>
- </el-table-column>
- <el-table-column label="绋庣巼(%)" prop="taxRate" min-width="120">
- <template #default="scope">
- <el-select
- v-if="scope.row.__editing"
- v-model="scope.row.taxRate"
- placeholder="璇烽�夋嫨"
- clearable
- style="width: 100%"
- @change="() => handleInlineTaxRateChange(scope.row)"
- >
- <el-option label="1" value="1" />
- <el-option label="3" value="3" />
- <el-option label="6" value="6" />
- <el-option label="9" value="9" />
- <el-option label="13" value="13" />
- </el-select>
- <span v-else>{{ scope.row.taxRate ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" min-width="120"/>
- <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" min-width="120"/>
- <el-table-column label="鍙戠エ绫诲瀷" prop="invoiceType" min-width="120">
- <template #default="scope">
- <el-select
- v-if="scope.row.__editing"
- v-model="scope.row.invoiceType"
- placeholder="璇烽�夋嫨"
- clearable
- style="width: 100%"
- >
- <el-option label="澧炴櫘绁�" value="澧炴櫘绁�" />
- <el-option label="澧炰笓绁�" value="澧炰笓绁�" />
- </el-select>
- <span v-else>{{ scope.row.invoiceType ?? "" }}</span>
- </template>
- </el-table-column>
-
- <el-table-column label="鍔犲伐瑕佹眰" prop="processRequirement" min-width="160" show-overflow-tooltip>
- <template #default="scope">
- <el-input
- v-if="scope.row.__editing"
- v-model="scope.row.processRequirement"
- placeholder="璇疯緭鍏�"
- clearable
- style="width: 100%"
- />
- <span v-else>{{ scope.row.processRequirement ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="澶囨敞" prop="remark" min-width="140" show-overflow-tooltip>
- <template #default="scope">
- <el-input
- v-if="scope.row.__editing"
- v-model="scope.row.remark"
- placeholder="璇疯緭鍏�"
- clearable
- style="width: 100%"
- />
- <span v-else>{{ scope.row.remark ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="妤煎眰缂栧彿" prop="floorCode" min-width="140" show-overflow-tooltip>
- <template #default="scope">
- <el-input
- v-if="scope.row.__editing"
- v-model="scope.row.floorCode"
- placeholder="璇疯緭鍏�"
- clearable
- style="width: 100%"
- />
- <span v-else>{{ scope.row.floorCode ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="閲嶇" prop="heavyBox" min-width="100">
- <template #default="scope">
- <el-input
- v-if="scope.row.__editing"
- v-model="scope.row.heavyBox"
- placeholder="璇疯緭鍏�"
- clearable
- style="width: 100%"
- />
- <span v-else>{{ scope.row.heavyBox ?? "" }}</span>
- </template>
- </el-table-column>
- <el-table-column fixed="right" label="鎿嶄綔" min-width="220" align="center" v-if="operationType !== 'view'">
- <template #default="scope">
- <template v-if="scope.row.__editing">
- <el-button link type="primary" size="small" @click="saveProductInline(scope.row, scope.$index)">淇濆瓨</el-button>
- <el-button link type="danger" size="small" @click="cancelProductInline(scope.row, scope.$index)">鍙栨秷</el-button>
- <el-popover
- :width="560"
- trigger="click"
- :hide-after="0"
- :visible="scope.row.__otherAmountPopoverVisible"
- @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"
- >
- <template #reference>
- <el-button
- link
- type="primary"
- size="small"
- @click="openOtherAmountInline(scope.row)"
- >
- 鍏朵粬閲戦({{ (scope.row.salesProductProcessList || []).length || 0 }})
- </el-button>
- </template>
-
- <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;">
- <div style="font-weight: 600; color:#303133;">
- 鍏朵粬閲戦
- </div>
- <el-button type="primary" plain size="small" @click="startAddOtherAmountForRow(scope.row)">
- 鏂板
- </el-button>
- </div>
- <div
- v-if="scope.row.__inlineOtherAmountAdding"
- style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
- @click.stop
- >
- <el-select
- v-model="scope.row.__inlineOtherAmountAddId"
- filterable
- clearable
- placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
- style="width: 100%;"
- >
- <el-option
- v-for="item in otherAmountSelectOptions"
- :key="item.id"
- :label="item.processName"
- :value="item.id"
- />
- </el-select>
- <div style="display:flex; justify-content:flex-end; gap: 8px;">
- <el-button
- size="small"
- @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"
- >
- 鍙栨秷
- </el-button>
- <el-button
- type="primary"
- size="small"
- :disabled="scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
- @click="confirmAddOtherAmountForRow(scope.row)"
- >
- 纭娣诲姞
- </el-button>
- </div>
- </div>
-
- <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
- style="display:flex; flex-wrap:wrap; gap: 8px;"
- >
- <div
- v-for="(item, idx) in scope.row.salesProductProcessList"
- :key="String(item.id) + '_' + idx"
- style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"
- >
- <el-tag type="info" style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
- {{ item.processName }}
- </el-tag>
- <el-input-number
- v-model="item.quantity"
- :min="0"
- :step="1"
- :precision="0"
- style="width: 120px;"
- placeholder="鏁伴噺"
- :disabled="operationType === 'view'"
- @change="handleOtherAmountQuantityChange(scope.row)"
- />
- <el-button type="danger" link size="small" @click="removeOtherAmountAtForRow(scope.row, idx)">
- 鍒犻櫎
- </el-button>
- </div>
- </div>
- <div v-else style="color:#909399; font-size: 13px;">
- 鏆傛棤鍏朵粬閲戦
- </div>
- </el-popover>
- </template>
- <template v-else>
- <el-button
- link
- type="primary"
- size="small"
- :disabled="isProductShipped(scope.row)"
- @click="editProductInline(scope.row, scope.$index)"
- >
- 缂栬緫
- </el-button>
- <el-popover
- :width="560"
- trigger="click"
- :hide-after="0"
- :visible="scope.row.__otherAmountPopoverVisible"
- @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"
- >
- <template #reference>
- <el-button
- link
- type="primary"
- size="small"
- :disabled="isProductShipped(scope.row)"
- @click="openOtherAmountInline(scope.row)"
- >
- 鍏朵粬閲戦({{ (scope.row.salesProductProcessList || []).length || 0 }})
- </el-button>
- </template>
-
- <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;">
- <div style="font-weight: 600; color:#303133;">
- 鍏朵粬閲戦
- </div>
- <el-button
- type="primary"
- plain
- size="small"
- :disabled="isProductShipped(scope.row)"
- @click="startAddOtherAmountForRow(scope.row)"
- >
- 鏂板
- </el-button>
- </div>
- <div
- v-if="scope.row.__inlineOtherAmountAdding"
- style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
- @click.stop
- >
- <el-select
- v-model="scope.row.__inlineOtherAmountAddId"
- filterable
- clearable
- placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
- style="width: 100%;"
- :disabled="isProductShipped(scope.row)"
- >
- <el-option
- v-for="item in otherAmountSelectOptions"
- :key="item.id"
- :label="item.processName"
- :value="item.id"
- />
- </el-select>
- <div style="display:flex; justify-content:flex-end; gap: 8px;">
- <el-button
- size="small"
- :disabled="isProductShipped(scope.row)"
- @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"
- >
- 鍙栨秷
- </el-button>
- <el-button
- type="primary"
- size="small"
- :disabled="isProductShipped(scope.row) || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
- @click="confirmAddOtherAmountForRow(scope.row)"
- >
- 纭娣诲姞
- </el-button>
- </div>
- </div>
-
- <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
- style="display:flex; flex-wrap:wrap; gap: 8px;"
- >
- <div
- v-for="(item, idx) in scope.row.salesProductProcessList"
- :key="String(item.id) + '_' + idx"
- style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"
- >
- <el-tag type="info" style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
- {{ item.processName }}
- </el-tag>
- <el-input-number
- v-model="item.quantity"
- :min="0"
- :step="1"
- :precision="0"
- style="width: 120px;"
- placeholder="鏁伴噺"
- :disabled="operationType === 'view' || isProductShipped(scope.row)"
- @change="handleOtherAmountQuantityChange(scope.row)"
- />
- <el-button
- type="danger"
- link
- size="small"
- :disabled="isProductShipped(scope.row)"
- @click="removeOtherAmountAtForRow(scope.row, idx)"
- >
- 鍒犻櫎
- </el-button>
- </div>
- </div>
- <div v-else style="color:#909399; font-size: 13px;">
- 鏆傛棤鍏朵粬閲戦
- </div>
- </el-popover>
- </template>
- </template>
- </el-table-column>
- </el-table>
- <el-row :gutter="30">
- <el-col :span="24">
- <el-form-item label="澶囨敞锛�" prop="remarks">
- <el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="24">
- <el-form-item label="瀹㈡埛澶囨敞锛�" prop="customerRemarks">
- <el-input
- v-model="form.customerRemarks"
- placeholder="璇疯緭鍏�"
- clearable
- type="textarea"
- :rows="2"
- :disabled="operationType === 'view'"
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="24">
- <el-form-item label="闄勪欢鏉愭枡锛�" prop="salesLedgerFiles">
- <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
- :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
- :on-success="handleUploadSuccess" :on-remove="handleRemove">
- <el-button type="primary" v-if="operationType !== 'view'">涓婁紶</el-button>
- <template #tip v-if="operationType !== 'view'">
- <div class="el-upload__tip">
- 鏂囦欢鏍煎紡鏀寔
- doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
- </div>
- </template>
- </el-upload>
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
- </FormDialog>
-
- <!-- 浠庢姤浠峰崟瀵煎叆锛堜粎瀹℃壒閫氳繃锛� -->
- <el-dialog
- v-model="quotationDialogVisible"
- title="閫夋嫨瀹℃壒閫氳繃鐨勯攢鍞姤浠峰崟"
- width="80%"
- :close-on-click-modal="false"
- >
- <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
- <el-input
- v-model="quotationSearchForm.quotationNo"
- placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
- clearable
- style="max-width: 260px;"
- @change="fetchQuotationList"
- />
- <el-input
- v-model="quotationSearchForm.customer"
- placeholder="璇疯緭鍏ュ鎴峰悕绉�"
- clearable
- style="max-width: 260px;"
- @change="fetchQuotationList"
- />
- <el-button type="primary" @click="fetchQuotationList">鎼滅储</el-button>
- <el-button @click="resetQuotationSearch">閲嶇疆</el-button>
- </div>
-
- <el-table
- :data="quotationList"
- border
- stripe
- v-loading="quotationLoading"
- height="420px"
- >
- <el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" width="180" show-overflow-tooltip />
- <el-table-column prop="customer" label="瀹㈡埛鍚嶇О" min-width="220" show-overflow-tooltip />
- <el-table-column prop="salesperson" label="涓氬姟鍛�" width="120" show-overflow-tooltip />
- <el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="140" />
- <el-table-column prop="status" label="瀹℃壒鐘舵��" width="120" align="center" />
- <el-table-column prop="totalAmount" label="鎶ヤ环閲戦(鍏�)" width="160" align="right">
- <template #default="scope">
- {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
- </template>
- </el-table-column>
- <el-table-column fixed="right" label="鎿嶄綔" width="120" align="center">
- <template #default="scope">
- <el-button type="primary" link @click="applyQuotation(scope.row)">閫夋嫨</el-button>
- </template>
- </el-table-column>
- </el-table>
-
- <pagination
- v-show="quotationPage.total > 0"
- :total="quotationPage.total"
- layout="total, sizes, prev, pager, next, jumper"
- :page="quotationPage.current"
- :limit="quotationPage.size"
- @pagination="quotationPaginationChange"
- />
-
- <template #footer>
- <el-button @click="quotationDialogVisible = false">鍏抽棴</el-button>
- </template>
- </el-dialog>
- <FormDialog
- v-model="productFormVisible"
- :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
- :width="'60%'"
- :operation-type="productOperationType"
- @close="closeProductDia"
- @confirm="submitProduct"
- @cancel="closeProductDia">
- <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
- <!-- 姣忚涓変釜锛氫骇鍝佸ぇ绫�/瑙勬牸鍨嬪彿/鍗曚綅 -->
- <el-row :gutter="30">
- <el-col :span="8">
- <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
- <el-tree-select
- v-model="productForm.productCategory"
- placeholder="璇烽�夋嫨"
- clearable
- filterable
- :filter-node-method="filterProductCategoryNode"
- check-strictly
- @change="getModels"
- :data="productOptions"
- :render-after-expand="false"
- style="width: 100%"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
- <el-select
- v-model="productForm.productModelId"
- placeholder="璇烽�夋嫨"
- clearable
- @change="getProductModel"
- filterable
- style="width: 100%"
- >
- <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鍘氬害锛�" prop="thickness">
- <el-input-number
- v-model="productForm.thickness"
- :min="0"
- :step="0.000000000000001"
- :precision="15"
- style="width: 100%;"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- </el-row>
-
- <!-- 姣忚涓変釜锛氱◣鐜�/鍚◣鍗曚环/鏁伴噺 -->
- <el-row :gutter="30">
- <el-col :span="8">
- <el-form-item label="鍚◣鍗曚环(鍏�)锛�" prop="taxInclusiveUnitPrice">
- <el-input-number
- :step="0.01"
- :min="0"
- v-model="productForm.taxInclusiveUnitPrice"
- style="width: 100%"
- :precision="2"
- placeholder="璇疯緭鍏�"
- clearable
- @change="calculateFromUnitPrice"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="绋庣巼(%)锛�" prop="taxRate">
- <el-select v-model="productForm.taxRate" placeholder="璇烽�夋嫨" clearable @change="calculateFromTaxRate" style="width: 100%">
- <el-option label="1" value="1" />
- <el-option label="3" value="3" />
- <el-option label="6" value="6" />
- <el-option label="9" value="9" />
- <el-option label="13" value="13" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鏁伴噺锛�" prop="quantity">
- <el-input-number
- :step="0.1"
- :min="0"
- v-model="productForm.quantity"
- placeholder="璇疯緭鍏�"
- clearable
- :precision="2"
- @change="() => { calculateFromQuantity(); recalcAreaTotals(); }"
- style="width: 100%"
- />
- </el-form-item>
- </el-col>
- </el-row>
-
- <!-- 姣忚涓変釜锛氬惈绋庢�讳环/涓嶅惈绋庢�讳环/鍙戠エ绫诲瀷 -->
- <el-row :gutter="30">
- <el-col :span="8">
- <el-form-item label="鍚◣鎬讳环(鍏�)锛�" prop="taxInclusiveTotalPrice">
- <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="璇疯緭鍏�" clearable @change="calculateFromTotalPrice" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�" prop="taxExclusiveTotalPrice">
- <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="璇疯緭鍏�" clearable @change="calculateFromExclusiveTotalPrice" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鍙戠エ绫诲瀷锛�" prop="invoiceType">
- <el-select v-model="productForm.invoiceType" placeholder="璇烽�夋嫨" clearable style="width: 100%">
- <el-option label="澧炴櫘绁�" value="澧炴櫘绁�" />
- <el-option label="澧炰笓绁�" value="澧炰笓绁�" />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
-
- <!-- 姣忚涓変釜锛氬/楂�/瀹為檯鍗曠墖闈㈢Н -->
- <el-row :gutter="30">
- <el-col :span="8">
- <el-form-item label="瀹�(mm)锛�" prop="width">
- <el-input-number
- v-model="productForm.width"
- :min="0"
- :step="1"
- :precision="2"
- style="width: 100%"
- placeholder="璇疯緭鍏ュ(mm)"
- clearable
- @change="recalcAreaFromWidthHeight"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="楂�(mm)锛�" prop="height">
- <el-input-number
- v-model="productForm.height"
- :min="0"
- :step="1"
- :precision="2"
- style="width: 100%"
- placeholder="璇疯緭鍏ラ珮(mm)"
- clearable
- @change="recalcAreaFromWidthHeight"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鍛ㄩ暱(cm)锛�" prop="perimeter">
- <el-input-number
- v-model="productForm.perimeter"
- :min="0"
- :step="0.01"
- :precision="2"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- :disabled="true"
- />
- </el-form-item>
- </el-col>
- </el-row>
-
- <!-- 姣忚涓変釜锛氬疄闄呭崟鐗囬潰绉�/瀹為檯鎬婚潰绉�/缁撶畻鍗曠墖闈㈢Н -->
- <el-row :gutter="30">
- <el-col :span="8">
- <el-form-item label="瀹為檯鍗曠墖闈㈢Н(銕�)锛�" prop="actualPieceArea">
- <el-input-number
- v-model="productForm.actualPieceArea"
- :min="0"
- :step="0.00001"
- :precision="5"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- @change="recalcAreaTotals"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="瀹為檯鎬婚潰绉�(銕�)锛�" prop="actualTotalArea">
- <el-input-number
- v-model="productForm.actualTotalArea"
- :min="0"
- :step="0.00001"
- :precision="5"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="缁撶畻鍗曠墖闈㈢Н(銕�)锛�" prop="settlePieceArea">
- <el-input-number
- v-model="productForm.settlePieceArea"
- :min="0"
- :step="0.00001"
- :precision="5"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- @change="() => { recalcAreaTotals(); calculateFromUnitPrice(true); }"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="缁撶畻鎬婚潰绉�(銕�)锛�" prop="settleTotalArea">
- <el-input-number
- v-model="productForm.settleTotalArea"
- :min="0"
- :step="0.00001"
- :precision="5"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="缁撶畻鎬婚潰绉�(銕�)锛�" prop="settleTotalArea">
- <el-input-number
- v-model="productForm.settleTotalArea"
- :min="0"
- :step="0.00001"
- :precision="5"
- style="width: 100%"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="閲嶇锛�" prop="heavyBox">
- <el-input v-model="productForm.heavyBox" placeholder="璇疯緭鍏�" clearable @change="calculateFromExclusiveTotalPrice" />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="24">
- <el-form-item label="妤煎眰缂栧彿锛�" prop="floorCode">
- <el-input v-model="productForm.floorCode" placeholder="璇疯緭鍏ユゼ灞傜紪鍙�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
- </el-form-item>
- </el-col>
- </el-row>
- <!-- 鍏朵粬閲戦锛堝崰婊′竴琛岋細绛夊悓浜� 3 鍒楃綉鏍肩殑鏁磋锛� -->
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="鍔犲伐瑕佹眰锛�" prop="processRequirement">
- <el-input v-model="productForm.processRequirement" placeholder="璇疯緭鍏ュ姞宸ヨ姹�" clearable />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="澶囨敞锛�" prop="remark">
- <el-input v-model="productForm.remark" placeholder="璇疯緭鍏ュ娉�" clearable />
- </el-form-item>
- </el-col>
- </el-row>
-
- <el-row :gutter="30">
- <el-col :span="24">
- <el-form-item>
- <template #label>
- <div style="display:flex; align-items:center; gap: 12px; width: 100%;">
- <div style="font-weight: 600; color: #303133;">鍏朵粬閲戦锛�</div>
- <div style="color:#909399; font-size: 13px; flex: 1;">
- 宸查�夋嫨 {{ productForm?.salesProductProcessList?.length || 0 }} 椤�
- </div>
- <el-button
- v-if="operationType !== 'view'"
- type="primary"
- plain
- size="small"
- @click="startAddOtherAmount"
- >
- 鏂板
- </el-button>
- </div>
- </template>
-
- <div style="display:flex; flex-direction:column; gap: 12px;">
- <div v-if="Array.isArray(productForm?.salesProductProcessList) && productForm.salesProductProcessList.length > 0"
- style="display:flex; flex-wrap:wrap; gap: 12px; align-items:flex-start;"
- >
- <div
- v-for="(item, index) in productForm.salesProductProcessList"
- :key="String(item.id) + '_' + index"
- style="display:flex; gap: 10px; align-items:center; padding: 10px 12px; border: 1px solid #ebeef5; border-radius: 8px; box-sizing:border-box; min-width: 0;"
- :style="getOtherAmountCardFlexStyle()"
- >
- <div style="flex: 1; min-width: 0;">
- <el-tag type="info" style="width: 100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
- {{ item.processName }}
- </el-tag>
- </div>
- <div style="flex: 1;">
- <el-input-number
- v-model="item.quantity"
- :min="0"
- :step="1"
- :precision="0"
- style="width: 100%;"
- placeholder="璇疯緭鍏ユ暟閲�"
- :disabled="operationType === 'view'"
- @change="calculateFromUnitPrice(true)"
- />
- </div>
- <el-button
- v-if="operationType !== 'view'"
- type="danger"
- link
- size="small"
- @click="removeOtherAmountAt(index)"
- >
- 鍒犻櫎
- </el-button>
- </div>
- </div>
-
- <div v-else style="color:#909399; font-size: 13px;">
- 鏆傛棤鍏朵粬閲戦
- </div>
- </div>
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
- </FormDialog>
- <!-- 鍏朵粬閲戦锛氭柊澧炲脊妗� -->
- <el-dialog
- v-model="otherAmountAddDialogVisible"
- title="鏂板鍏朵粬閲戦"
- width="520px"
- :close-on-click-modal="false"
- >
- <div style="padding: 4px 0 10px;">
- <div style="font-size: 14px; color: #606266; margin-bottom: 10px;">
- 璇烽�夋嫨瑕佹柊澧炵殑鍏朵粬閲戦椤圭洰
- </div>
- <el-select
- v-model="otherAmountAddId"
- filterable
- clearable
- placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
- style="width: 100%;"
- :disabled="operationType === 'view'"
- >
- <el-option
- v-for="item in otherAmountSelectOptions"
- :key="item.id"
- :label="item.processName"
- :value="item.id"
- />
- </el-select>
- </div>
- <template #footer>
- <el-button @click="cancelAddOtherAmount">鍙栨秷</el-button>
- <el-button
- type="primary"
- @click="confirmAddOtherAmount"
- :disabled="operationType === 'view' || otherAmountAddId === null || otherAmountAddId === undefined || otherAmountAddId === ''"
- >
- 纭娣诲姞
- </el-button>
- </template>
- </el-dialog>
-
- <!-- 瀵煎叆寮圭獥 -->
- <FormDialog
- v-model="importUpload.open"
- :title="importUpload.title"
- :width="'600px'"
- @close="importUpload.open = false"
- @confirm="submitImportFile"
- @cancel="importUpload.open = false"
- >
- <el-upload
- ref="importUploadRef"
- :limit="1"
- accept=".xlsx,.xls"
- :action="importUpload.url"
- :headers="importUpload.headers"
- :before-upload="importUpload.beforeUpload"
- :on-success="importUpload.onSuccess"
- :on-error="importUpload.onError"
- :on-progress="importUpload.onProgress"
- :on-change="importUpload.onChange"
- :auto-upload="false"
- drag
- >
- <i class="el-icon-upload"></i>
- <div class="el-upload__text">
- 灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em>
- </div>
- <template #tip>
- <div class="el-upload__tip">
- 浠呮敮鎸� xls/xlsx锛屽ぇ灏忎笉瓒呰繃 10MB銆�
- <el-button link type="primary" @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
- </div>
- </template>
- </el-upload>
- </FormDialog>
- <!-- 闄勪欢鍒楄〃寮圭獥 -->
- <FileListDialog
- ref="fileListRef"
- v-model="fileListDialogVisible"
- title="闄勪欢鍒楄〃"
- />
- <!-- 鍙戣揣寮规 -->
- <el-dialog
- v-model="deliveryFormVisible"
- title="鍙戣揣淇℃伅"
- width="40%"
- @close="closeDeliveryDia"
- >
- <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
- <el-row :gutter="30">
- <el-col :span="24">
- <el-form-item label="鍙戣揣绫诲瀷锛�" prop="type">
- <el-select
- v-model="deliveryForm.type"
- placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
- style="width: 100%"
- >
- <el-option label="璐ц溅" value="璐ц溅" />
- <el-option label="蹇��" value="蹇��" />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
-
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="绛捐鏃ユ湡锛�"
+ prop="executionDate">
+ <el-date-picker style="width: 100%"
+ v-model="form.executionDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable
+ :disabled="operationType === 'view'" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="浜よ揣鏃ユ湡锛�"
+ prop="deliveryDate">
+ <el-date-picker style="width: 100%"
+ v-model="form.deliveryDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="褰曞叆浜猴細"
+ prop="entryPerson">
+ <el-select v-model="form.entryPerson"
+ filterable
+ default-first-option
+ :reserve-keyword="false"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="changs">
+ <el-option v-for="item in userList"
+ :key="item.userId"
+ :label="item.nickName"
+ :value="item.userId" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="褰曞叆鏃ユ湡锛�"
+ prop="entryDate">
+ <el-date-picker style="width: 100%"
+ v-model="form.entryDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-form-item label="浜у搧淇℃伅锛�"
+ prop="entryDate">
+ <el-button v-if="operationType !== 'view'"
+ type="primary"
+ :disabled="hasEditingProductRow()"
+ @click="addProductInline">
+ 娣诲姞
+ </el-button>
+ <el-button v-if="operationType !== 'view'"
+ plain
+ type="danger"
+ @click="deleteProduct">鍒犻櫎</el-button>
+ </el-form-item>
+ </el-row>
+ <el-table :data="productData"
+ border
+ @selection-change="productSelected"
+ show-summary
+ :summary-method="summarizeMainTable">
+ <el-table-column align="center"
+ type="selection"
+ width="55"
+ v-if="operationType !== 'view'"
+ :selectable="(row) => !isProductShipped(row)" />
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="浜у搧澶х被"
+ prop="productCategory"
+ min-width="160">
+ <template #default="scope">
+ <el-tree-select v-if="scope.row.__editing"
+ v-model="scope.row.__productCategoryId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ check-strictly
+ :data="productOptions"
+ :render-after-expand="false"
+ style="width: 100%"
+ :filter-node-method="filterProductCategoryNode"
+ @change="(val) => handleInlineProductCategoryChange(scope.row, val)" />
+ <span v-else>{{ scope.row.productCategory ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="瑙勬牸鍨嬪彿"
+ prop="specificationModel"
+ min-width="160">
+ <template #default="scope">
+ <el-select v-if="scope.row.__editing"
+ v-model="scope.row.productModelId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ style="width: 100%"
+ @change="(val) => handleInlineProductModelChange(scope.row, val)">
+ <el-option v-for="item in modelOptions"
+ :key="item.id"
+ :label="item.model"
+ :value="item.id" />
+ </el-select>
+ <span v-else>{{ scope.row.specificationModel ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍘氬害(mm)"
+ prop="thickness"
+ min-width="160">
+ <template #default="scope">
+ <el-input-number v-if="scope.row.__editing"
+ v-model="scope.row.thickness"
+ :min="0"
+ :step="0.000000000000001"
+ :precision="15"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ <span v-else>{{ scope.row.thickness ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="瀹�(mm)"
+ prop="width"
+ min-width="160">
+ <template #default="scope">
+ <el-input-number v-if="scope.row.__editing"
+ v-model="scope.row.width"
+ :min="0"
+ :step="1"
+ :precision="2"
+ style="width:100%"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="() => handleInlineSizeChange(scope.row)"
+ @input="() => handleInlineSizeChange(scope.row)" />
+ <span v-else>{{ scope.row.width ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="楂�(mm)"
+ prop="height"
+ min-width="160">
+ <template #default="scope">
+ <el-input-number v-if="scope.row.__editing"
+ v-model="scope.row.height"
+ :min="0"
+ :step="1"
+ :precision="2"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="() => handleInlineSizeChange(scope.row)"
+ @input="() => handleInlineSizeChange(scope.row)" />
+ <span v-else>{{ scope.row.height ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="缁撶畻鍗曠墖闈㈢Н(銕�)"
+ prop="settlePieceArea"
+ min-width="160">
+ <template #default="scope">
+ <el-input-number v-if="scope.row.__editing"
+ v-model="scope.row.settlePieceArea"
+ :min="0"
+ :step="0.00001"
+ :precision="5"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="() => handleInlineSettleAreaChange(scope.row)" />
+ <span v-else>{{ scope.row.settlePieceArea ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏁伴噺"
+ prop="quantity"
+ min-width="150">
+ <template #default="scope">
+ <el-input-number v-if="scope.row.__editing"
+ v-model="scope.row.quantity"
+ :step="0.1"
+ :min="0"
+ :precision="2"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="() => handleInlineQuantityChange(scope.row)"
+ @input="() => handleInlineQuantityChange(scope.row)" />
+ <span v-else>{{ scope.row.quantity ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="闈㈢Н(m虏)"
+ prop="actualTotalArea"
+ min-width="160">
+ <template #default="scope">
+ <el-input-number v-if="scope.row.__editing"
+ v-model="scope.row.actualTotalArea"
+ :min="0"
+ :step="0.00001"
+ :precision="5"
+ style="width: 100%"
+ placeholder="鑷姩璁$畻" />
+ <span v-else>{{ scope.row.actualTotalArea ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍚◣鍗曚环(鍏�)"
+ prop="taxInclusiveUnitPrice"
+ min-width="140">
+ <template #default="scope">
+ <el-input-number v-if="scope.row.__editing"
+ :step="0.01"
+ :min="0"
+ :precision="2"
+ style="width: 100%"
+ v-model="scope.row.taxInclusiveUnitPrice"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="() => handleInlineUnitPriceChange(scope.row)"
+ @input="() => handleInlineUnitPriceChange(scope.row)" />
+ <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice ?? 0) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="绋庣巼(%)"
+ prop="taxRate"
+ min-width="120">
+ <template #default="scope">
+ <el-select v-if="scope.row.__editing"
+ v-model="scope.row.taxRate"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%"
+ @change="() => handleInlineTaxRateChange(scope.row)">
+ <el-option label="1"
+ value="1" />
+ <el-option label="3"
+ value="3" />
+ <el-option label="6"
+ value="6" />
+ <el-option label="9"
+ value="9" />
+ <el-option label="13"
+ value="13" />
+ </el-select>
+ <span v-else>{{ scope.row.taxRate ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍚◣鎬讳环(鍏�)"
+ prop="taxInclusiveTotalPrice"
+ :formatter="formattedNumber"
+ min-width="120" />
+ <el-table-column label="涓嶅惈绋庢�讳环(鍏�)"
+ prop="taxExclusiveTotalPrice"
+ :formatter="formattedNumber"
+ min-width="120" />
+ <el-table-column label="鍙戠エ绫诲瀷"
+ prop="invoiceType"
+ min-width="120">
+ <template #default="scope">
+ <el-select v-if="scope.row.__editing"
+ v-model="scope.row.invoiceType"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%">
+ <el-option label="澧炴櫘绁�"
+ value="澧炴櫘绁�" />
+ <el-option label="澧炰笓绁�"
+ value="澧炰笓绁�" />
+ </el-select>
+ <span v-else>{{ scope.row.invoiceType ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍔犲伐瑕佹眰"
+ prop="processRequirement"
+ min-width="160"
+ show-overflow-tooltip>
+ <template #default="scope">
+ <el-input v-if="scope.row.__editing"
+ v-model="scope.row.processRequirement"
+ placeholder="璇疯緭鍏�"
+ clearable
+ style="width: 100%" />
+ <span v-else>{{ scope.row.processRequirement ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ min-width="140"
+ show-overflow-tooltip>
+ <template #default="scope">
+ <el-input v-if="scope.row.__editing"
+ v-model="scope.row.remark"
+ placeholder="璇疯緭鍏�"
+ clearable
+ style="width: 100%" />
+ <span v-else>{{ scope.row.remark ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="妤煎眰缂栧彿"
+ prop="floorCode"
+ min-width="140"
+ show-overflow-tooltip>
+ <template #default="scope">
+ <el-input v-if="scope.row.__editing"
+ v-model="scope.row.floorCode"
+ placeholder="璇疯緭鍏�"
+ clearable
+ style="width: 100%" />
+ <span v-else>{{ scope.row.floorCode ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="閲嶇"
+ prop="heavyBox"
+ min-width="100">
+ <template #default="scope">
+ <el-input v-if="scope.row.__editing"
+ v-model="scope.row.heavyBox"
+ placeholder="璇疯緭鍏�"
+ clearable
+ style="width: 100%" />
+ <span v-else>{{ scope.row.heavyBox ?? "" }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ min-width="220"
+ align="center"
+ v-if="operationType !== 'view'">
+ <template #default="scope">
+ <template v-if="scope.row.__editing">
+ <el-button link
+ type="primary"
+ size="small"
+ @click="saveProductInline(scope.row, scope.$index)">淇濆瓨</el-button>
+ <el-button link
+ type="danger"
+ size="small"
+ @click="cancelProductInline(scope.row, scope.$index)">鍙栨秷</el-button>
+ <el-popover :width="560"
+ trigger="click"
+ :hide-after="0"
+ :visible="scope.row.__otherAmountPopoverVisible"
+ @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)">
+ <template #reference>
+ <el-button link
+ type="primary"
+ size="small"
+ @click="openOtherAmountInline(scope.row)">
+ 鍏朵粬閲戦({{ (scope.row.salesProductProcessList || []).length || 0 }})
+ </el-button>
+ </template>
+ <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;">
+ <div style="font-weight: 600; color:#303133;">
+ 鍏朵粬閲戦
+ </div>
+ <el-button type="primary"
+ plain
+ size="small"
+ @click="startAddOtherAmountForRow(scope.row)">
+ 鏂板
+ </el-button>
+ </div>
+ <div v-if="scope.row.__inlineOtherAmountAdding"
+ style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
+ @click.stop>
+ <el-select v-model="scope.row.__inlineOtherAmountAddId"
+ filterable
+ clearable
+ placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
+ style="width: 100%;">
+ <el-option v-for="item in otherAmountSelectOptions"
+ :key="item.id"
+ :label="item.processName"
+ :value="item.id" />
+ </el-select>
+ <div style="display:flex; justify-content:flex-end; gap: 8px;">
+ <el-button size="small"
+ @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null">
+ 鍙栨秷
+ </el-button>
+ <el-button type="primary"
+ size="small"
+ :disabled="scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
+ @click="confirmAddOtherAmountForRow(scope.row)">
+ 纭娣诲姞
+ </el-button>
+ </div>
+ </div>
+ <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
+ style="display:flex; flex-wrap:wrap; gap: 8px;">
+ <div v-for="(item, idx) in scope.row.salesProductProcessList"
+ :key="String(item.id) + '_' + idx"
+ style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);">
+ <el-tag type="info"
+ style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
+ {{ item.processName }}
+ </el-tag>
+ <el-input-number v-model="item.quantity"
+ :min="0"
+ :step="1"
+ :precision="0"
+ style="width: 120px;"
+ placeholder="鏁伴噺"
+ :disabled="operationType === 'view'"
+ @change="handleOtherAmountQuantityChange(scope.row)" />
+ <el-button type="danger"
+ link
+ size="small"
+ @click="removeOtherAmountAtForRow(scope.row, idx)">
+ 鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ <div v-else
+ style="color:#909399; font-size: 13px;">
+ 鏆傛棤鍏朵粬閲戦
+ </div>
+ </el-popover>
+ </template>
+ <template v-else>
+ <el-button link
+ type="primary"
+ size="small"
+ :disabled="isProductShipped(scope.row)"
+ @click="editProductInline(scope.row, scope.$index)">
+ 缂栬緫
+ </el-button>
+ <el-popover :width="560"
+ trigger="click"
+ :hide-after="0"
+ :visible="scope.row.__otherAmountPopoverVisible"
+ @update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)">
+ <template #reference>
+ <el-button link
+ type="primary"
+ size="small"
+ :disabled="isProductShipped(scope.row)"
+ @click="openOtherAmountInline(scope.row)">
+ 鍏朵粬閲戦({{ (scope.row.salesProductProcessList || []).length || 0 }})
+ </el-button>
+ </template>
+ <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; margin-bottom: 8px;">
+ <div style="font-weight: 600; color:#303133;">
+ 鍏朵粬閲戦
+ </div>
+ <el-button type="primary"
+ plain
+ size="small"
+ :disabled="isProductShipped(scope.row)"
+ @click="startAddOtherAmountForRow(scope.row)">
+ 鏂板
+ </el-button>
+ </div>
+ <div v-if="scope.row.__inlineOtherAmountAdding"
+ style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
+ @click.stop>
+ <el-select v-model="scope.row.__inlineOtherAmountAddId"
+ filterable
+ clearable
+ placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
+ style="width: 100%;"
+ :disabled="isProductShipped(scope.row)">
+ <el-option v-for="item in otherAmountSelectOptions"
+ :key="item.id"
+ :label="item.processName"
+ :value="item.id" />
+ </el-select>
+ <div style="display:flex; justify-content:flex-end; gap: 8px;">
+ <el-button size="small"
+ :disabled="isProductShipped(scope.row)"
+ @click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null">
+ 鍙栨秷
+ </el-button>
+ <el-button type="primary"
+ size="small"
+ :disabled="isProductShipped(scope.row) || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
+ @click="confirmAddOtherAmountForRow(scope.row)">
+ 纭娣诲姞
+ </el-button>
+ </div>
+ </div>
+ <div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
+ style="display:flex; flex-wrap:wrap; gap: 8px;">
+ <div v-for="(item, idx) in scope.row.salesProductProcessList"
+ :key="String(item.id) + '_' + idx"
+ style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);">
+ <el-tag type="info"
+ style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
+ {{ item.processName }}
+ </el-tag>
+ <el-input-number v-model="item.quantity"
+ :min="0"
+ :step="1"
+ :precision="0"
+ style="width: 120px;"
+ placeholder="鏁伴噺"
+ :disabled="operationType === 'view' || isProductShipped(scope.row)"
+ @change="handleOtherAmountQuantityChange(scope.row)" />
+ <el-button type="danger"
+ link
+ size="small"
+ :disabled="isProductShipped(scope.row)"
+ @click="removeOtherAmountAtForRow(scope.row, idx)">
+ 鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ <div v-else
+ style="color:#909399; font-size: 13px;">
+ 鏆傛棤鍏朵粬閲戦
+ </div>
+ </el-popover>
+ </template>
+ </template>
+ </el-table-column>
+ </el-table>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�"
+ prop="remarks">
+ <el-input v-model="form.remarks"
+ placeholder="璇疯緭鍏�"
+ clearable
+ type="textarea"
+ :rows="2"
+ :disabled="operationType === 'view'" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="瀹㈡埛澶囨敞锛�"
+ prop="customerRemarks">
+ <el-input v-model="form.customerRemarks"
+ placeholder="璇疯緭鍏�"
+ clearable
+ type="textarea"
+ :rows="2"
+ :disabled="operationType === 'view'" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="闄勪欢鏉愭枡锛�"
+ prop="salesLedgerFiles">
+ <el-upload v-model:file-list="fileList"
+ :action="upload.url"
+ multiple
+ ref="fileUpload"
+ auto-upload
+ :headers="upload.headers"
+ :before-upload="handleBeforeUpload"
+ :on-error="handleUploadError"
+ :on-success="handleUploadSuccess"
+ :on-remove="handleRemove">
+ <el-button type="primary"
+ v-if="operationType !== 'view'">涓婁紶</el-button>
+ <template #tip
+ v-if="operationType !== 'view'">
+ <div class="el-upload__tip">
+ 鏂囦欢鏍煎紡鏀寔
+ doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
+ </div>
+ </template>
+ </el-upload>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </FormDialog>
+ <!-- 浠庢姤浠峰崟瀵煎叆锛堜粎瀹℃壒閫氳繃锛� -->
+ <el-dialog v-model="quotationDialogVisible"
+ title="閫夋嫨瀹℃壒閫氳繃鐨勯攢鍞姤浠峰崟"
+ width="80%"
+ :close-on-click-modal="false">
+ <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
+ <el-input v-model="quotationSearchForm.quotationNo"
+ placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
+ clearable
+ style="max-width: 260px;"
+ @change="fetchQuotationList" />
+ <el-input v-model="quotationSearchForm.customer"
+ placeholder="璇疯緭鍏ュ鎴峰悕绉�"
+ clearable
+ style="max-width: 260px;"
+ @change="fetchQuotationList" />
+ <el-button type="primary"
+ @click="fetchQuotationList">鎼滅储</el-button>
+ <el-button @click="resetQuotationSearch">閲嶇疆</el-button>
+ </div>
+ <el-table :data="quotationList"
+ border
+ stripe
+ v-loading="quotationLoading"
+ height="420px">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column prop="quotationNo"
+ label="鎶ヤ环鍗曞彿"
+ width="180"
+ show-overflow-tooltip />
+ <el-table-column prop="customer"
+ label="瀹㈡埛鍚嶇О"
+ min-width="220"
+ show-overflow-tooltip />
+ <el-table-column prop="salesperson"
+ label="涓氬姟鍛�"
+ width="120"
+ show-overflow-tooltip />
+ <el-table-column prop="quotationDate"
+ label="鎶ヤ环鏃ユ湡"
+ width="140" />
+ <el-table-column prop="status"
+ label="瀹℃壒鐘舵��"
+ width="120"
+ align="center" />
+ <el-table-column prop="totalAmount"
+ label="鎶ヤ环閲戦(鍏�)"
+ width="160"
+ align="right">
+ <template #default="scope">
+ {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="120"
+ align="center">
+ <template #default="scope">
+ <el-button type="primary"
+ link
+ @click="applyQuotation(scope.row)">閫夋嫨</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="quotationPage.total > 0"
+ :total="quotationPage.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="quotationPage.current"
+ :limit="quotationPage.size"
+ @pagination="quotationPaginationChange" />
+ <template #footer>
+ <el-button @click="quotationDialogVisible = false">鍏抽棴</el-button>
+ </template>
+ </el-dialog>
+ <FormDialog v-model="productFormVisible"
+ :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
+ :width="'60%'"
+ :operation-type="productOperationType"
+ @close="closeProductDia"
+ @confirm="submitProduct"
+ @cancel="closeProductDia">
+ <el-form :model="productForm"
+ label-width="140px"
+ label-position="top"
+ :rules="productRules"
+ ref="productFormRef">
+ <!-- 姣忚涓変釜锛氫骇鍝佸ぇ绫�/瑙勬牸鍨嬪彿/鍗曚綅 -->
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="浜у搧澶х被锛�"
+ prop="productCategory">
+ <el-tree-select v-model="productForm.productCategory"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ :filter-node-method="filterProductCategoryNode"
+ check-strictly
+ @change="getModels"
+ :data="productOptions"
+ :render-after-expand="false"
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="瑙勬牸鍨嬪彿锛�"
+ prop="productModelId">
+ <el-select v-model="productForm.productModelId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="getProductModel"
+ filterable
+ style="width: 100%">
+ <el-option v-for="item in modelOptions"
+ :key="item.id"
+ :label="item.model"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鍘氬害锛�"
+ prop="thickness">
+ <el-input-number v-model="productForm.thickness"
+ :min="0"
+ :step="0.000000000000001"
+ :precision="15"
+ style="width: 100%;"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 姣忚涓変釜锛氱◣鐜�/鍚◣鍗曚环/鏁伴噺 -->
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="鍚◣鍗曚环(鍏�)锛�"
+ prop="taxInclusiveUnitPrice">
+ <el-input-number :step="0.01"
+ :min="0"
+ v-model="productForm.taxInclusiveUnitPrice"
+ style="width: 100%"
+ :precision="2"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="calculateFromUnitPrice" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="绋庣巼(%)锛�"
+ prop="taxRate">
+ <el-select v-model="productForm.taxRate"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="calculateFromTaxRate"
+ style="width: 100%">
+ <el-option label="1"
+ value="1" />
+ <el-option label="3"
+ value="3" />
+ <el-option label="6"
+ value="6" />
+ <el-option label="9"
+ value="9" />
+ <el-option label="13"
+ value="13" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鏁伴噺锛�"
+ prop="quantity">
+ <el-input-number :step="0.1"
+ :min="0"
+ v-model="productForm.quantity"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :precision="2"
+ @change="() => { calculateFromQuantity(); recalcAreaTotals(); }"
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 姣忚涓変釜锛氬惈绋庢�讳环/涓嶅惈绋庢�讳环/鍙戠エ绫诲瀷 -->
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="鍚◣鎬讳环(鍏�)锛�"
+ prop="taxInclusiveTotalPrice">
+ <el-input v-model="productForm.taxInclusiveTotalPrice"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="calculateFromTotalPrice" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�"
+ prop="taxExclusiveTotalPrice">
+ <el-input v-model="productForm.taxExclusiveTotalPrice"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="calculateFromExclusiveTotalPrice" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鍙戠エ绫诲瀷锛�"
+ prop="invoiceType">
+ <el-select v-model="productForm.invoiceType"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%">
+ <el-option label="澧炴櫘绁�"
+ value="澧炴櫘绁�" />
+ <el-option label="澧炰笓绁�"
+ value="澧炰笓绁�" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 姣忚涓変釜锛氬/楂�/瀹為檯鍗曠墖闈㈢Н -->
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="瀹�(mm)锛�"
+ prop="width">
+ <el-input-number v-model="productForm.width"
+ :min="0"
+ :step="1"
+ :precision="2"
+ style="width: 100%"
+ placeholder="璇疯緭鍏ュ(mm)"
+ clearable
+ @change="recalcAreaFromWidthHeight" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="楂�(mm)锛�"
+ prop="height">
+ <el-input-number v-model="productForm.height"
+ :min="0"
+ :step="1"
+ :precision="2"
+ style="width: 100%"
+ placeholder="璇疯緭鍏ラ珮(mm)"
+ clearable
+ @change="recalcAreaFromWidthHeight" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鍛ㄩ暱(cm)锛�"
+ prop="perimeter">
+ <el-input-number v-model="productForm.perimeter"
+ :min="0"
+ :step="0.01"
+ :precision="2"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :disabled="true" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 姣忚涓変釜锛氬疄闄呭崟鐗囬潰绉�/瀹為檯鎬婚潰绉�/缁撶畻鍗曠墖闈㈢Н -->
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="瀹為檯鍗曠墖闈㈢Н(銕�)锛�"
+ prop="actualPieceArea">
+ <el-input-number v-model="productForm.actualPieceArea"
+ :min="0"
+ :step="0.00001"
+ :precision="5"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="recalcAreaTotals" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="瀹為檯鎬婚潰绉�(銕�)锛�"
+ prop="actualTotalArea">
+ <el-input-number v-model="productForm.actualTotalArea"
+ :min="0"
+ :step="0.00001"
+ :precision="5"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="缁撶畻鍗曠墖闈㈢Н(銕�)锛�"
+ prop="settlePieceArea">
+ <el-input-number v-model="productForm.settlePieceArea"
+ :min="0"
+ :step="0.00001"
+ :precision="5"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="() => { recalcAreaTotals(); calculateFromUnitPrice(true); }" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="缁撶畻鎬婚潰绉�(銕�)锛�"
+ prop="settleTotalArea">
+ <el-input-number v-model="productForm.settleTotalArea"
+ :min="0"
+ :step="0.00001"
+ :precision="5"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="缁撶畻鎬婚潰绉�(銕�)锛�"
+ prop="settleTotalArea">
+ <el-input-number v-model="productForm.settleTotalArea"
+ :min="0"
+ :step="0.00001"
+ :precision="5"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="閲嶇锛�"
+ prop="heavyBox">
+ <el-input v-model="productForm.heavyBox"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="calculateFromExclusiveTotalPrice" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="妤煎眰缂栧彿锛�"
+ prop="floorCode">
+ <el-input v-model="productForm.floorCode"
+ placeholder="璇疯緭鍏ユゼ灞傜紪鍙�"
+ clearable
+ type="textarea"
+ :rows="2"
+ :disabled="operationType === 'view'" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 鍏朵粬閲戦锛堝崰婊′竴琛岋細绛夊悓浜� 3 鍒楃綉鏍肩殑鏁磋锛� -->
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍔犲伐瑕佹眰锛�"
+ prop="processRequirement">
+ <el-input v-model="productForm.processRequirement"
+ placeholder="璇疯緭鍏ュ姞宸ヨ姹�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="澶囨敞锛�"
+ prop="remark">
+ <el-input v-model="productForm.remark"
+ placeholder="璇疯緭鍏ュ娉�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item>
+ <template #label>
+ <div style="display:flex; align-items:center; gap: 12px; width: 100%;">
+ <div style="font-weight: 600; color: #303133;">鍏朵粬閲戦锛�</div>
+ <div style="color:#909399; font-size: 13px; flex: 1;">
+ 宸查�夋嫨 {{ productForm?.salesProductProcessList?.length || 0 }} 椤�
+ </div>
+ <el-button v-if="operationType !== 'view'"
+ type="primary"
+ plain
+ size="small"
+ @click="startAddOtherAmount">
+ 鏂板
+ </el-button>
+ </div>
+ </template>
+ <div style="display:flex; flex-direction:column; gap: 12px;">
+ <div v-if="Array.isArray(productForm?.salesProductProcessList) && productForm.salesProductProcessList.length > 0"
+ style="display:flex; flex-wrap:wrap; gap: 12px; align-items:flex-start;">
+ <div v-for="(item, index) in productForm.salesProductProcessList"
+ :key="String(item.id) + '_' + index"
+ style="display:flex; gap: 10px; align-items:center; padding: 10px 12px; border: 1px solid #ebeef5; border-radius: 8px; box-sizing:border-box; min-width: 0;"
+ :style="getOtherAmountCardFlexStyle()">
+ <div style="flex: 1; min-width: 0;">
+ <el-tag type="info"
+ style="width: 100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
+ {{ item.processName }}
+ </el-tag>
+ </div>
+ <div style="flex: 1;">
+ <el-input-number v-model="item.quantity"
+ :min="0"
+ :step="1"
+ :precision="0"
+ style="width: 100%;"
+ placeholder="璇疯緭鍏ユ暟閲�"
+ :disabled="operationType === 'view'"
+ @change="calculateFromUnitPrice(true)" />
+ </div>
+ <el-button v-if="operationType !== 'view'"
+ type="danger"
+ link
+ size="small"
+ @click="removeOtherAmountAt(index)">
+ 鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ <div v-else
+ style="color:#909399; font-size: 13px;">
+ 鏆傛棤鍏朵粬閲戦
+ </div>
+ </div>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </FormDialog>
+ <!-- 鍏朵粬閲戦锛氭柊澧炲脊妗� -->
+ <el-dialog v-model="otherAmountAddDialogVisible"
+ title="鏂板鍏朵粬閲戦"
+ width="520px"
+ :close-on-click-modal="false">
+ <div style="padding: 4px 0 10px;">
+ <div style="font-size: 14px; color: #606266; margin-bottom: 10px;">
+ 璇烽�夋嫨瑕佹柊澧炵殑鍏朵粬閲戦椤圭洰
+ </div>
+ <el-select v-model="otherAmountAddId"
+ filterable
+ clearable
+ placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
+ style="width: 100%;"
+ :disabled="operationType === 'view'">
+ <el-option v-for="item in otherAmountSelectOptions"
+ :key="item.id"
+ :label="item.processName"
+ :value="item.id" />
+ </el-select>
+ </div>
+ <template #footer>
+ <el-button @click="cancelAddOtherAmount">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="confirmAddOtherAmount"
+ :disabled="operationType === 'view' || otherAmountAddId === null || otherAmountAddId === undefined || otherAmountAddId === ''">
+ 纭娣诲姞
+ </el-button>
+ </template>
+ </el-dialog>
+ <!-- 瀵煎叆寮圭獥 -->
+ <FormDialog v-model="importUpload.open"
+ :title="importUpload.title"
+ :width="'600px'"
+ @close="importUpload.open = false"
+ @confirm="submitImportFile"
+ @cancel="importUpload.open = false">
+ <el-upload ref="importUploadRef"
+ :limit="1"
+ accept=".xlsx,.xls"
+ :action="importUpload.url"
+ :headers="importUpload.headers"
+ :before-upload="importUpload.beforeUpload"
+ :on-success="importUpload.onSuccess"
+ :on-error="importUpload.onError"
+ :on-progress="importUpload.onProgress"
+ :on-change="importUpload.onChange"
+ :auto-upload="false"
+ drag>
+ <i class="el-icon-upload"></i>
+ <div class="el-upload__text">
+ 灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em>
+ </div>
+ <template #tip>
+ <div class="el-upload__tip">
+ 浠呮敮鎸� xls/xlsx锛屽ぇ灏忎笉瓒呰繃 10MB銆�
+ <el-button link
+ type="primary"
+ @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
+ </div>
+ </template>
+ </el-upload>
+ </FormDialog>
+ <!-- 闄勪欢鍒楄〃寮圭獥 -->
+ <FileListDialog ref="fileListRef"
+ v-model="fileListDialogVisible"
+ title="闄勪欢鍒楄〃" />
+ <!-- 鍙戣揣寮规 -->
+ <el-dialog v-model="deliveryFormVisible"
+ title="鍙戣揣淇℃伅"
+ width="40%"
+ @close="closeDeliveryDia">
+ <el-form :model="deliveryForm"
+ label-width="120px"
+ label-position="top"
+ :rules="deliveryRules"
+ ref="deliveryFormRef">
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="鍙戣揣绫诲瀷锛�"
+ prop="type">
+ <el-select v-model="deliveryForm.type"
+ placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
+ style="width: 100%">
+ <el-option label="璐ц溅"
+ value="璐ц溅" />
+ <el-option label="蹇��"
+ value="蹇��" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
<!-- 瀹℃壒浜洪�夋嫨锛堜豢鍗忓悓瀹℃壒閲岀殑瀹℃壒浜鸿妭鐐归�夋嫨锛� -->
<el-row>
<el-col :span="24">
<el-form-item>
<template #label>
<span>瀹℃壒浜洪�夋嫨锛�</span>
- <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">鏂板鑺傜偣</el-button>
+ <el-button type="primary"
+ @click="addApproverNode"
+ style="margin-left: 8px;">鏂板鑺傜偣</el-button>
</template>
<div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
- <div
- v-for="(node, index) in approverNodes"
- :key="node.id"
- style="margin-right: 20px; text-align: center; margin-bottom: 10px;"
- >
+ <div v-for="(node, index) in approverNodes"
+ :key="node.id"
+ style="margin-right: 20px; text-align: center; margin-bottom: 10px;">
<div>
<span>瀹℃壒浜�</span>
鈫�
</div>
- <el-select
- v-model="node.userId"
- placeholder="閫夋嫨浜哄憳"
- filterable
- style="width: 140px; margin-bottom: 8px;"
- >
- <el-option
- v-for="user in userList"
- :key="user.userId"
- :label="user.nickName"
- :value="user.userId"
- />
+ <el-select v-model="node.userId"
+ placeholder="閫夋嫨浜哄憳"
+ filterable
+ style="width: 140px; margin-bottom: 8px;">
+ <el-option v-for="user in userListApprove"
+ :key="user.userId"
+ :label="user.userName"
+ :value="user.userId" />
</el-select>
<div>
- <el-button
- type="danger"
- @click="removeApproverNode(index)"
- v-if="approverNodes.length > 1"
- >鍒犻櫎</el-button>
+ <el-button type="danger"
+ @click="removeApproverNode(index)"
+ v-if="approverNodes.length > 1">鍒犻櫎</el-button>
</div>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button type="primary" @click="submitDelivery">纭鍙戣揣</el-button>
- <el-button @click="closeDeliveryDia">鍙栨秷</el-button>
- </div>
- </template>
- </el-dialog>
-
- </div>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="submitDelivery">纭鍙戣揣</el-button>
+ <el-button @click="closeDeliveryDia">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
</template>
<script setup>
-import { getToken } from "@/utils/auth";
-import pagination from "@/components/PIMTable/Pagination.vue";
-import {onMounted, ref, getCurrentInstance, watch, nextTick} from "vue";
-import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
-import { ElMessageBox, ElMessage } from "element-plus";
-import { ArrowDown } from "@element-plus/icons-vue";
-import useUserStore from "@/store/modules/user";
-import { userListNoPage } from "@/api/system/user.js";
-import FileListDialog from '@/components/Dialog/FileListDialog.vue';
-import FormDialog from '@/components/Dialog/FormDialog.vue';
-import OtherAmountMaintenanceButton from "./components/OtherAmountMaintenanceButton.vue";
-import ProcessFlowMaintenanceButton from "./components/ProcessFlowMaintenanceButton.vue";
-import ProcessFlowConfigSelectDialog from "./components/ProcessFlowConfigSelectDialog.vue";
-import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
-import {
- ledgerListPage,
- productList,
- customerList,
- addOrUpdateSalesLedger,
- getSalesLedgerWithProducts,
- delLedger,
- addOrUpdateSalesLedgerProduct,
- delProduct,
- delLedgerFile,
- getProductInventory,
- salesLedgerProductProcessList,
- saleProcessBind,
- getSaleProcessBindInfo,
- getProcessCard,
- getSalesOrder,
- getSalesInvoices,
- getSalesLabel,
- salesStock,
-} from "@/api/salesManagement/salesLedger.js";
-import { modelList, productTreeList } from "@/api/basicData/product.js";
-import useFormData from "@/hooks/useFormData.js";
-import dayjs from "dayjs";
-import { getCurrentDate } from "@/utils/index.js";
-import { printFinishedProcessCard } from "./components/processCardPrint.js";
-import { printSalesOrder } from "./components/salesOrderPrint.js";
-import { printSalesDeliveryNote } from "./components/salesDeliveryPrint.js";
-import { printSalesLabel } from "./components/salesLabelPrint.js";
-// import { salesLedgerProductSetProcessFlowConfig } from "@/api/salesManagement/salesProcessFlowConfig.js";
+ import { getToken } from "@/utils/auth";
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import { onMounted, ref, getCurrentInstance, watch, nextTick } from "vue";
+ import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
+ import { ElMessageBox, ElMessage } from "element-plus";
+ import { ArrowDown } from "@element-plus/icons-vue";
+ import useUserStore from "@/store/modules/user";
+ import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
+ import { userListNoPage } from "@/api/system/user.js";
+ import FileListDialog from "@/components/Dialog/FileListDialog.vue";
+ import FormDialog from "@/components/Dialog/FormDialog.vue";
+ import OtherAmountMaintenanceButton from "./components/OtherAmountMaintenanceButton.vue";
+ import ProcessFlowMaintenanceButton from "./components/ProcessFlowMaintenanceButton.vue";
+ import ProcessFlowConfigSelectDialog from "./components/ProcessFlowConfigSelectDialog.vue";
+ import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
+ import {
+ ledgerListPage,
+ productList,
+ customerList,
+ addOrUpdateSalesLedger,
+ getSalesLedgerWithProducts,
+ delLedger,
+ addOrUpdateSalesLedgerProduct,
+ delProduct,
+ delLedgerFile,
+ getProductInventory,
+ salesLedgerProductProcessList,
+ saleProcessBind,
+ getSaleProcessBindInfo,
+ getProcessCard,
+ getSalesOrder,
+ getSalesInvoices,
+ getSalesLabel,
+ salesStock,
+ } from "@/api/salesManagement/salesLedger.js";
+ import { modelList, productTreeList } from "@/api/basicData/product.js";
+ import useFormData from "@/hooks/useFormData.js";
+ import dayjs from "dayjs";
+ import { getCurrentDate } from "@/utils/index.js";
+ import { printFinishedProcessCard } from "./components/processCardPrint.js";
+ import { printSalesOrder } from "./components/salesOrderPrint.js";
+ import { printSalesDeliveryNote } from "./components/salesDeliveryPrint.js";
+ import { printSalesLabel } from "./components/salesLabelPrint.js";
+ // import { salesLedgerProductSetProcessFlowConfig } from "@/api/salesManagement/salesProcessFlowConfig.js";
-const userStore = useUserStore();
-const { proxy } = getCurrentInstance();
-const tableData = ref([]);
-const productData = ref([]);
-const selectedRows = ref([]);
-const productSelectedRows = ref([]);
-const userList = ref([]);
-const customerOption = ref([]);
-const productOptions = ref([]);
-const modelOptions = ref([]);
-const tableLoading = ref(false);
-const page = reactive({
- current: 1,
- size: 100,
-});
-const total = ref(0);
-const fileList = ref([]);
+ const userStore = useUserStore();
+ const { proxy } = getCurrentInstance();
+ const tableData = ref([]);
+ const productData = ref([]);
+ const selectedRows = ref([]);
+ const productSelectedRows = ref([]);
+ const userList = ref([]);
+ const userListApprove = ref([]);
+ const customerOption = ref([]);
+ const productOptions = ref([]);
+ const modelOptions = ref([]);
+ const tableLoading = ref(false);
+ const page = reactive({
+ current: 1,
+ size: 100,
+ });
+ const total = ref(0);
+ const fileList = ref([]);
-// 宸ヨ壓璺嚎閰嶇疆閫夋嫨寮圭獥锛堢粦瀹氬埌鍙拌处浜у搧锛�
-const processFlowSelectDialogVisible = ref(false);
-const processFlowSelectLedgerRow = ref(null);
-const processFlowSelectDefaultRouteId = ref(null);
-const processFlowSelectBoundRouteId = ref(null);
-const processFlowSelectBoundRouteName = ref("");
+ // 宸ヨ壓璺嚎閰嶇疆閫夋嫨寮圭獥锛堢粦瀹氬埌鍙拌处浜у搧锛�
+ const processFlowSelectDialogVisible = ref(false);
+ const processFlowSelectLedgerRow = ref(null);
+ const processFlowSelectDefaultRouteId = ref(null);
+ const processFlowSelectBoundRouteId = ref(null);
+ const processFlowSelectBoundRouteName = ref("");
-// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
-const operationType = ref("");
-const dialogFormVisible = ref(false);
-const data = reactive({
- searchForm: {
- customerName: "", // 瀹㈡埛鍚嶇О
- customerId: "", // 瀹㈡埛ID锛堟煡璇笅鎷夛級
- salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
- entryDate: null, // 褰曞叆鏃ユ湡
- entryDateStart: undefined,
- entryDateEnd: undefined,
- deliveryStatus: undefined, // 鍙戣揣鐘舵�侊細1鏈彂璐� 2瀹℃壒涓� 3瀹℃壒澶辫触 4宸插彂璐�
- stockStatus: undefined, // 鍏ュ簱鐘舵�侊細0鏈叆搴� 1宸插叆搴�
- },
- form: {
- salesContractNo: "",
- salesman: "",
- customerId: "",
- entryPerson: "",
- entryDate: "",
- deliveryDate: "",
- maintenanceTime: "",
- productData: [],
- executionDate: "",
- },
- rules: {
- salesman: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- customerId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- entryPerson: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- deliveryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- },
-});
-const { form, rules } = toRefs(data);
-const { form: searchForm } = useFormData(data.searchForm);
+ // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+ const operationType = ref("");
+ const dialogFormVisible = ref(false);
+ const data = reactive({
+ searchForm: {
+ customerName: "", // 瀹㈡埛鍚嶇О
+ customerId: "", // 瀹㈡埛ID锛堟煡璇笅鎷夛級
+ salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
+ entryDate: null, // 褰曞叆鏃ユ湡
+ entryDateStart: undefined,
+ entryDateEnd: undefined,
+ deliveryStatus: undefined, // 鍙戣揣鐘舵�侊細1鏈彂璐� 2瀹℃壒涓� 3瀹℃壒澶辫触 4宸插彂璐�
+ stockStatus: undefined, // 鍏ュ簱鐘舵�侊細0鏈叆搴� 1宸插叆搴�
+ },
+ form: {
+ salesContractNo: "",
+ salesman: "",
+ customerId: "",
+ entryPerson: "",
+ entryDate: "",
+ deliveryDate: "",
+ maintenanceTime: "",
+ productData: [],
+ executionDate: "",
+ },
+ rules: {
+ salesman: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ customerId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ entryPerson: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ deliveryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+ });
+ const { form, rules } = toRefs(data);
+ const { form: searchForm } = useFormData(data.searchForm);
-// 鏂板鍙拌处锛氬綍鍏ユ棩鏈熷彉鏇存椂锛屼氦璐ф棩鏈熼粯璁や繚鎸佷负褰曞叆鏃ユ湡鍚庣 7 澶�
-watch(
- () => [operationType.value, form.value?.entryDate],
- () => {
- if (operationType.value !== "add") return;
- const ed = form.value?.entryDate;
- if (!ed) return;
- form.value.deliveryDate = dayjs(ed).add(7, "day").format("YYYY-MM-DD");
- }
-);
-// 浜у搧琛ㄥ崟寮规鏁版嵁
-const productFormVisible = ref(false);
-const productOperationType = ref("");
-const currentId = ref("");
-const productFormData = reactive({
- productForm: {
- productCategory: "",
- specificationModel: "",
- thickness:null,
- quantity: "",
- taxInclusiveUnitPrice: "",
- taxRate: "",
- taxInclusiveTotalPrice: "",
- taxExclusiveTotalPrice: "",
- invoiceType: "",
- // 鏂板锛氬昂瀵�/闈㈢Н/鍔犲伐涓庡叾浠栭噾棰�
- width: 0, // 瀹�(mm)
- height: 0, // 楂�(mm)
- perimeter: 0, // 鍛ㄩ暱(cm) = (瀹�+楂�)*2锛屽楂樹负 mm
- // 闈㈢Н瀛楁锛堛帯锛�
- actualPieceArea: 0, // 瀹為檯鍗曠墖闈㈢Н(銕�)
- actualTotalArea: 0, // 瀹為檯鎬婚潰绉�(銕�)
- settlePieceArea: 0, // 缁撶畻鍗曠墖闈㈢Н(銕�)
- settleTotalArea: 0, // 缁撶畻鎬婚潰绉�(銕�)
- processRequirement: "", // 鍔犲伐瑕佹眰
- remark: "", // 澶囨敞
- salesProductProcessList: [], // 鍏朵粬閲戦锛歔{id, processName, quantity}]
- processFlowConfigId: null, // 宸ヨ壓娴佺▼閰嶇疆缁戝畾
- floorCode: "", // 妤煎眰缂栧彿
- },
- productRules: {
- productCategory: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- },
-});
-const { productForm, productRules } = toRefs(productFormData);
-// 闃叉寰幆璁$畻鐨勬爣蹇�
-const isCalculating = ref(false);
+ // 鏂板鍙拌处锛氬綍鍏ユ棩鏈熷彉鏇存椂锛屼氦璐ф棩鏈熼粯璁や繚鎸佷负褰曞叆鏃ユ湡鍚庣 7 澶�
+ watch(
+ () => [operationType.value, form.value?.entryDate],
+ () => {
+ if (operationType.value !== "add") return;
+ const ed = form.value?.entryDate;
+ if (!ed) return;
+ form.value.deliveryDate = dayjs(ed).add(7, "day").format("YYYY-MM-DD");
+ }
+ );
+ // 浜у搧琛ㄥ崟寮规鏁版嵁
+ const productFormVisible = ref(false);
+ const productOperationType = ref("");
+ const currentId = ref("");
+ const productFormData = reactive({
+ productForm: {
+ productCategory: "",
+ specificationModel: "",
+ thickness: null,
+ quantity: "",
+ taxInclusiveUnitPrice: "",
+ taxRate: "",
+ taxInclusiveTotalPrice: "",
+ taxExclusiveTotalPrice: "",
+ invoiceType: "",
+ // 鏂板锛氬昂瀵�/闈㈢Н/鍔犲伐涓庡叾浠栭噾棰�
+ width: 0, // 瀹�(mm)
+ height: 0, // 楂�(mm)
+ perimeter: 0, // 鍛ㄩ暱(cm) = (瀹�+楂�)*2锛屽楂樹负 mm
+ // 闈㈢Н瀛楁锛堛帯锛�
+ actualPieceArea: 0, // 瀹為檯鍗曠墖闈㈢Н(銕�)
+ actualTotalArea: 0, // 瀹為檯鎬婚潰绉�(銕�)
+ settlePieceArea: 0, // 缁撶畻鍗曠墖闈㈢Н(銕�)
+ settleTotalArea: 0, // 缁撶畻鎬婚潰绉�(銕�)
+ processRequirement: "", // 鍔犲伐瑕佹眰
+ remark: "", // 澶囨敞
+ salesProductProcessList: [], // 鍏朵粬閲戦锛歔{id, processName, quantity}]
+ processFlowConfigId: null, // 宸ヨ壓娴佺▼閰嶇疆缁戝畾
+ floorCode: "", // 妤煎眰缂栧彿
+ },
+ productRules: {
+ productCategory: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+ });
+ const { productForm, productRules } = toRefs(productFormData);
+ // 闃叉寰幆璁$畻鐨勬爣蹇�
+ const isCalculating = ref(false);
-// 浜у搧琛屽唴缂栬緫锛氬彧鍏佽鍚屾椂缂栬緫涓�琛�
-const editingProductRow = ref(null);
+ // 浜у搧琛屽唴缂栬緫锛氬彧鍏佽鍚屾椂缂栬緫涓�琛�
+ const editingProductRow = ref(null);
-const ensureProductRowDefaults = (row) => {
- if (!row || typeof row !== "object") return;
- if (!Array.isArray(row.salesProductProcessList)) row.salesProductProcessList = [];
- if (row.__otherAmountPopoverVisible === undefined || row.__otherAmountPopoverVisible === null) row.__otherAmountPopoverVisible = false;
- if (row.__inlineOtherAmountAdding === undefined || row.__inlineOtherAmountAdding === null) row.__inlineOtherAmountAdding = false;
- if (row.__inlineOtherAmountAddId === undefined) row.__inlineOtherAmountAddId = null;
- if (row.width === undefined || row.width === null) row.width = 0;
- if (row.height === undefined || row.height === null) row.height = 0;
- if (row.perimeter === undefined || row.perimeter === null) row.perimeter = 0;
- if (row.actualPieceArea === undefined || row.actualPieceArea === null) row.actualPieceArea = 0;
- if (row.actualTotalArea === undefined || row.actualTotalArea === null) row.actualTotalArea = 0;
- if (row.settlePieceArea === undefined || row.settlePieceArea === null) row.settlePieceArea = 0;
- if (row.settleTotalArea === undefined || row.settleTotalArea === null) row.settleTotalArea = 0;
- if (row.processRequirement === undefined || row.processRequirement === null) row.processRequirement = "";
- if (row.remark === undefined || row.remark === null) row.remark = "";
- if (row.floorCode === undefined || row.floorCode === null) row.floorCode = "";
- if (row.invoiceType === undefined || row.invoiceType === null) row.invoiceType = "";
- if (row.taxRate === undefined || row.taxRate === null) row.taxRate = "";
- if (row.quantity === undefined || row.quantity === null) row.quantity = 0;
- if (row.taxInclusiveUnitPrice === undefined || row.taxInclusiveUnitPrice === null) row.taxInclusiveUnitPrice = 0;
- if (row.taxInclusiveTotalPrice === undefined || row.taxInclusiveTotalPrice === null) row.taxInclusiveTotalPrice = 0;
- if (row.taxExclusiveTotalPrice === undefined || row.taxExclusiveTotalPrice === null) row.taxExclusiveTotalPrice = 0;
-};
-
-const stopOtherEditingRows = () => {
- (productData.value || []).forEach((r) => {
- if (r && r.__editing) r.__editing = false;
- });
- editingProductRow.value = null;
-};
-
-const hasEditingProductRow = () => {
- return (productData.value || []).some((r) => r && r.__editing);
-};
-
-const addProductInline = async () => {
- if (operationType.value === "view") return;
- if (hasEditingProductRow()) {
- proxy.$modal.msgWarning("璇峰厛淇濆瓨鎴栧彇娑堝綋鍓嶇紪杈戣");
- return;
- }
- await getProductOptions();
- await fetchOtherAmountSelectOptions(true);
- const row = {
- id: null,
- __tempKey: `__temp_${Date.now()}_${Math.random().toString(16).slice(2)}`,
- __editing: true,
- __isNew: true,
- __productCategoryId: null,
- productCategory: "",
- productModelId: null,
- specificationModel: "",
- thickness: null,
- quantity: 0,
- taxInclusiveUnitPrice: 0,
- taxRate: "",
- taxInclusiveTotalPrice: 0,
- taxExclusiveTotalPrice: 0,
- invoiceType: "",
- width: 0,
- height: 0,
- perimeter: 0,
- actualPieceArea: 0,
- actualTotalArea: 0,
- settlePieceArea: 0,
- settleTotalArea: 0,
- processRequirement: "",
- remark: "",
- salesProductProcessList: [],
- processFlowConfigId: null,
- floorCode: "",
- heavyBox: "",
- };
- productData.value.push(row);
- editingProductRow.value = row;
- // 璁╃幇鏈夌殑璁$畻/鍏朵粬閲戦閫昏緫澶嶇敤褰撳墠琛�
- productForm.value = row;
-};
-
-const editProductInline = async (row, index) => {
- if (operationType.value === "view") return;
- if (!row) return;
- if (isProductShipped(row)) {
- proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
- return;
- }
- stopOtherEditingRows();
- await getProductOptions();
- await fetchOtherAmountSelectOptions(true);
- ensureProductRowDefaults(row);
- // 浜у搧澶х被 tree-select 鍥炴樉锛氬悕绉� -> id
- row.__productCategoryId = findNodeIdByLabel(productOptions.value, row.productCategory);
-
- // 鍏煎鍚庣瀛楁鍛藉悕锛堜繚鎸佸師閫昏緫锛�
- row.actualPieceArea = row?.actualPieceArea ?? row?.actual_piece_area ?? 0;
- row.actualTotalArea = row?.actualTotalArea ?? row?.actual_total_area ?? 0;
- row.settlePieceArea = row?.settlePieceArea ?? row?.settle_piece_area ?? 0;
- row.settleTotalArea = row?.settleTotalArea ?? row?.settle_total_area ?? 0;
- row.processRequirement = row?.processRequirement ?? row?.process_requirement ?? "";
- row.remark = row?.remark ?? row?.remarks ?? "";
- row.floorCode = row?.floorCode ?? row?.floor_code ?? "";
- row.processFlowConfigId = row?.processFlowConfigId ?? row?.process_flow_config_id ?? null;
- row.perimeter = row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0;
- row.thickness = row?.thickness;
-
- row.salesProductProcessList = normalizeOtherAmountsFromRow(row);
- mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
- row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
-
- // 澶囦唤鐢ㄤ簬鍙栨秷
- row.__backup = JSON.parse(JSON.stringify(row));
- row.__editing = true;
- editingProductRow.value = row;
- productForm.value = row;
-
- // 鏍规嵁浜у搧澶х被鍚嶇О鍙嶆煡 tree 鑺傜偣 id锛屽苟鍔犺浇瑙勬牸鍨嬪彿鍒楄〃
- try {
- const options = productOptions.value && productOptions.value.length > 0 ? productOptions.value : await getProductOptions();
- const categoryId = findNodeIdByLabel(options, row.productCategory);
- if (categoryId) {
- const models = await modelList({ id: categoryId });
- modelOptions.value = models || [];
- const currentModel = (modelOptions.value || []).find((m) => m.model === row.specificationModel);
- if (currentModel) row.productModelId = currentModel.id;
- }
- } catch (e) {
- console.error("鍔犺浇浜у搧瑙勬牸鍨嬪彿澶辫触", e);
- }
-
- // 鍚屾璁$畻涓�娆�
- recalcPerimeterFromWidthHeight();
- recalcAreaFromWidthHeight();
-};
-
-const validateInlineProductRow = (row) => {
- if (!row) return false;
- if (!row.productCategory) {
- proxy.$modal.msgWarning("璇烽�夋嫨浜у搧澶х被");
- return false;
- }
- if (!row.productModelId) {
- proxy.$modal.msgWarning("璇烽�夋嫨瑙勬牸鍨嬪彿");
- return false;
- }
- return true;
-};
-
-const saveProductInline = async (row, index) => {
- if (operationType.value === "view") return;
- if (!row) return;
- if (isProductShipped(row)) {
- proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
- return;
- }
- // 纭繚 productForm 鎸囧悜褰撳墠琛岋紝浠ュ鐢ㄨ绠楅�昏緫
- productForm.value = row;
- ensureProductRowDefaults(row);
-
- if (!validateInlineProductRow(row)) return;
-
- // 鍘氬害绮惧害澶勭悊
- if (row.thickness !== null && row.thickness !== undefined && row.thickness !== "") {
- row.thickness = Number(Number(row.thickness).toFixed(15));
- }
-
- // 鎻愪氦鍓嶅厹搴曡绠椾竴娆★紙娌跨敤鍘熼�昏緫锛�
- recalcAreaTotals();
- // 鎻愪氦鍏滃簳锛氱◣鐜�/鏁伴噺鏈~鏃舵寜鏁板瓧 0 浼犻��
- row.taxRate = Number(row.taxRate ?? 0) || 0;
- row.quantity = Number(row.quantity ?? 0) || 0;
-
- // 瑙勮寖鍖栧叾浠栭噾棰濇彁浜ょ粨鏋�
- row.salesProductProcessList = (Array.isArray(row.salesProductProcessList) ? row.salesProductProcessList : [])
- .map((it) => ({
- id: it?.id,
- processName: it?.processName ?? "",
- unitPrice: Number(it?.unitPrice ?? 0) || 0,
- quantity: Number(it?.quantity ?? 0) || 0,
- }))
- .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
-
- // 瑙勬牸鍨嬪彿锛氭牴鎹� productModelId 鍥炲~鍚嶇О
- const model = (modelOptions.value || []).find((m) => String(m.id) === String(row.productModelId));
- if (model?.model) row.specificationModel = model.model;
-
- if (operationType.value === "edit") {
- // 鍙拌处宸插瓨鍦細璧板師鎺ュ彛淇濆瓨鍒板悗绔紝鍐嶅洖鎷夊埛鏂�
- const payload = { ...row, salesLedgerId: currentId.value, type: 1 };
- delete payload.__backup;
- delete payload.__editing;
- delete payload.__isNew;
- delete payload.__productCategoryId;
- delete payload.__tempKey;
- await addOrUpdateSalesLedgerProduct(payload);
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- await getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
- productData.value = res.productData;
- });
- } else {
- // 鏂板鍙拌处锛氫粎鍦ㄦ湰鍦� productData 鐢熸晥锛屾渶缁堥殢鍙拌处涓�璧锋彁浜�
- row.__isNew = false;
- row.__editing = false;
- delete row.__backup;
- }
-
- stopOtherEditingRows();
-};
-
-const cancelProductInline = (row, index) => {
- if (!row) return;
- if (row.__isNew) {
- productData.value.splice(index, 1);
- } else if (row.__backup) {
- const restored = JSON.parse(JSON.stringify(row.__backup));
- // 淇濈暀 id 涓庣姸鎬佸瓧娈�
- const keepId = row.id;
- Object.keys(row).forEach((k) => delete row[k]);
- Object.assign(row, restored);
- row.id = keepId;
- row.__editing = false;
- delete row.__backup;
- }
- stopOtherEditingRows();
-};
-
-const openOtherAmountInline = async (row) => {
- if (!row) return;
- if (operationType.value === "view") return;
- if (isProductShipped(row)) {
- proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
- return;
- }
- ensureProductRowDefaults(row);
- productForm.value = row;
- otherAmountAddTargetRow.value = row;
- await fetchOtherAmountSelectOptions(true);
- mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
- row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
- // 鍙仛鏁版嵁鍑嗗涓庢墦寮�娴眰锛堟柊澧炵敱娴眰鍐呮寜閽Е鍙戯級
- row.__otherAmountPopoverVisible = true;
-};
-
-const keepOtherAmountPopoverOpenKey = ref(null);
-const keepOtherAmountPopoverOpenUntil = ref(0);
-
-const getOtherAmountRowKey = (row) => String(row?.__tempKey ?? row?.id ?? "");
-
-const lockOtherAmountPopoverOpen = (row, durationMs = 1200) => {
- const key = getOtherAmountRowKey(row);
- if (!key) return;
- keepOtherAmountPopoverOpenKey.value = key;
- keepOtherAmountPopoverOpenUntil.value = Date.now() + durationMs;
-};
-
-const handleOtherAmountPopoverVisibleChange = (row, visible) => {
- if (!row) return;
- if (visible) {
- row.__otherAmountPopoverVisible = true;
- return;
- }
- if (row.__inlineOtherAmountAdding) {
- row.__otherAmountPopoverVisible = true;
- return;
- }
- const key = getOtherAmountRowKey(row);
- const shouldKeepOpen = Boolean(
- key &&
- keepOtherAmountPopoverOpenKey.value === key &&
- Date.now() < keepOtherAmountPopoverOpenUntil.value
- );
- row.__otherAmountPopoverVisible = shouldKeepOpen;
-};
-
-const startAddOtherAmountForRow = async (row) => {
- if (!row) return;
- if (operationType.value === "view") return;
- if (isProductShipped(row)) {
- proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
- return;
- }
- ensureProductRowDefaults(row);
- productForm.value = row;
- await fetchOtherAmountSelectOptions(true);
- mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
- row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
- row.__inlineOtherAmountAddId = null;
- row.__inlineOtherAmountAdding = true;
- row.__otherAmountPopoverVisible = true;
-};
-
-const confirmAddOtherAmountForRow = (row) => {
- if (!row) return;
- ensureProductRowDefaults(row);
- productForm.value = row;
- const selectedId = row.__inlineOtherAmountAddId;
- if (selectedId === null || selectedId === undefined || selectedId === "") return;
- const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(selectedId));
- if (!opt) return;
- const exists = (row.salesProductProcessList ?? []).some(
- (it) => String(it?.id) === String(opt.id)
- );
- if (exists) {
- proxy.$modal.msgWarning("璇ュ叾浠栭噾棰濋」鐩凡娣诲姞");
- return;
- }
- row.salesProductProcessList.push({
- id: opt.id,
- processName: opt.processName,
- unitPrice: opt.unitPrice ?? 0,
- quantity: 0,
- });
- row.__inlineOtherAmountAddId = null;
- row.__inlineOtherAmountAdding = false;
- row.__otherAmountPopoverVisible = true;
- calculateFromUnitPrice(true);
-};
-
-const removeOtherAmountAtForRow = (row, index) => {
- if (!row) return;
- if (operationType.value === "view") return;
- if (isProductShipped(row)) return;
- productForm.value = row;
- removeOtherAmountAt(index);
-};
-
-const handleOtherAmountQuantityChange = (row) => {
- if (!row) return;
- productForm.value = row;
- calculateFromUnitPrice(true);
-};
-
-const handleInlineProductCategoryChange = async (row, val) => {
- if (!row) return;
- productForm.value = row;
- // 澶嶇敤鍘熸湁閫昏緫锛氫細鍐欏叆 productCategory(鍚嶇О)銆侀噸缃鏍�/鍘氬害骞舵媺鍙栧瀷鍙�
- await getModels(val);
- // 琛屽唴缂栬緫鏃舵妸閫変腑鐨� id 璁板綍涓嬫潵锛屼究浜庡洖鏄�
- row.__productCategoryId = val;
-};
-
-const handleInlineProductModelChange = (row, val) => {
- if (!row) return;
- productForm.value = row;
- // 澶嶇敤鍘熸湁閫昏緫锛氫細鍐欏叆 specificationModel銆佸帤搴�
- getProductModel(val);
-};
-
-const handleInlineSizeChange = (row) => {
- if (!row) return;
- productForm.value = row;
- recalcPerimeterFromWidthHeight();
- recalcAreaFromWidthHeight();
- recalcAreaTotals();
-};
-
-const handleInlineUnitPriceChange = (row) => {
- if (!row) return;
- productForm.value = row;
- calculateFromUnitPrice();
- recalcAreaTotals();
-};
-
-const handleInlineQuantityChange = (row) => {
- if (!row) return;
- productForm.value = row;
- calculateFromQuantity();
- recalcAreaTotals();
-};
-
-const handleInlineTaxRateChange = (row) => {
- if (!row) return;
- productForm.value = row;
- calculateFromTaxRate();
-};
-
-const handleInlineSettleAreaChange = (row) => {
- if (!row) return;
- productForm.value = row;
- recalcAreaTotals();
- calculateFromUnitPrice(true);
-};
-const upload = reactive({
- // 涓婁紶鐨勫湴鍧�
- url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
- // 璁剧疆涓婁紶鐨勮姹傚ご閮�
- headers: { Authorization: "Bearer " + getToken() },
-});
-// 鎶ヤ环鍗曞鍏ョ浉鍏�
-const quotationDialogVisible = ref(false);
-const quotationLoading = ref(false);
-const quotationList = ref([]);
-const quotationSearchForm = reactive({
- quotationNo: "",
- customer: "",
-});
-// 鎶ヤ环鍗曞脊妗嗗垎椤�
-const quotationPage = reactive({
- current: 1,
- size: 10,
- total: 0,
-});
-const selectedQuotation = ref(null);
-
-// 鍙戣揣鐩稿叧
-const deliveryFormVisible = ref(false);
-const currentDeliveryRows = ref([]);
-const deliveryFormData = reactive({
- deliveryForm: {
- type: "璐ц溅", // 璐ц溅, 蹇��
- },
- deliveryRules: {
- type: [
- { required: true, message: "璇烽�夋嫨鍙戣揣绫诲瀷", trigger: "change" }
- ]
- },
-});
-const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
-
-// 浜у搧寮规锛氬叾浠栭噾棰濆閫変笅鎷夛紙鍩轰簬鈥滃叾浠栭噾棰濈淮鎶も�濇煡璇㈡帴鍙o級
-const otherAmountSelectOptions = ref([]); // [{id, processName}]
-const otherAmountSelectOptionsLoading = ref(false);
-
-const fetchOtherAmountSelectOptions = async (force = false) => {
- if (!force && otherAmountSelectOptions.value.length > 0) return;
- otherAmountSelectOptionsLoading.value = true;
- try {
- const params = {
- current: 1,
- // 涓嬫媺妗嗗敖閲忎竴娆℃�ф媺鍏紝閬垮厤澶氭鍒嗛〉褰卞搷閫夋嫨浣撻獙
- size: 1000,
- };
- const res = await salesLedgerProductProcessList(params);
- const records = res?.records ?? res?.data?.records ?? [];
-
- otherAmountSelectOptions.value = records.map((item) => ({
- id: item.id,
- processName: item.processName ?? "",
- unitPrice: item.unitPrice ?? 0,
- }));
- } finally {
- otherAmountSelectOptionsLoading.value = false;
- }
-};
-
-const normalizeOtherAmountsFromRow = (row) => {
- if (!row) return [];
- const raw =
- row.other_amounts ??
- row.otherAmounts ??
- row.otherAmountProjects ??
- row.otherAmountList ??
- row.salesProductProcessList ??
- [];
-
- if (!Array.isArray(raw)) return [];
- if (raw.length === 0) return [];
-
- // 鎯呭喌1锛氬悗绔洿鎺ヨ繑鍥� [{id, processName}...]
- if (typeof raw[0] === "object") {
- return raw
- .map((it) => {
- const id = it?.id ?? it?.processId ?? it?.otherAmountId ?? null;
- const quantity =
- Number(
- it?.quantity ??
- it?.processQuantity ??
- it?.otherQuantity ??
- it?.process_quantity ??
- it?.other_amount_quantity
- ) || 0;
- return {
- id,
- processName: it?.processName ?? "",
- quantity,
- };
- })
- .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
- }
-
- // 鎯呭喌2锛氬悗绔彧杩斿洖 ids: [1,2,3]
- return raw
- .map((id) => ({
- id,
- processName: "",
- quantity: 0,
- }))
- .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
-};
-
-const mergeOtherAmountOptionsBySelection = (selected) => {
- const list = Array.isArray(selected) ? selected : [];
- for (const s of list) {
- const exists = otherAmountSelectOptions.value.some((o) => String(o.id) === String(s.id));
- if (!exists) {
- otherAmountSelectOptions.value.push({
- id: s.id,
- processName: s.processName ?? "",
- });
- }
- }
-};
-
-const fillOtherAmountProcessName = (selected) => {
- const list = Array.isArray(selected) ? selected : [];
- return list.map((s) => {
- const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(s.id));
- return {
- id: s.id,
- processName: opt?.processName ?? s.processName ?? "",
- unitPrice: opt?.unitPrice ?? s.unitPrice ?? 0,
- quantity: Number(s.quantity ?? 0) || 0,
- };
- });
-};
-
-// 鈥滃叾浠栭噾棰濃�濆崱鐗囧竷灞�锛氬彧鏈変竴鏉℃椂鍗犳弧鏁磋锛�>=2鏃朵袱鍒楁帓甯�
-const getOtherAmountCardFlexStyle = () => {
- const list = productForm.value?.salesProductProcessList;
- const len = Array.isArray(list) ? list.length : 0;
- if (len === 1) {
- return { flex: "0 0 100%", maxWidth: "100%", width: "100%" };
- }
- return {
- flex: "0 0 calc(50% - 6px)",
- maxWidth: "calc(50% - 6px)",
- width: "calc(50% - 6px)",
- };
-};
-
-// 鍏朵粬閲戦锛氱偣鍑烩�滄柊澧炩�濆悗鍦ㄥ脊绐楅噷閫夋嫨涓�涓」鐩�
-const otherAmountAddDialogVisible = ref(false);
-const otherAmountAddId = ref(null);
-const otherAmountAddTargetRow = ref(null);
-const otherAmountAddTargetRowKey = ref(null);
-
-const startAddOtherAmount = () => {
- if (operationType.value === "view") return;
- otherAmountAddDialogVisible.value = true;
- otherAmountAddId.value = null;
- // 閫氬父 openProductForm 宸茬粡鎷夎繃 options锛岃繖閲屽厹搴�
- if (otherAmountSelectOptions.value.length === 0) {
- fetchOtherAmountSelectOptions(true);
- }
-};
-
-const cancelAddOtherAmount = () => {
- otherAmountAddDialogVisible.value = false;
- otherAmountAddId.value = null;
- otherAmountAddTargetRow.value = null;
- otherAmountAddTargetRowKey.value = null;
- keepOtherAmountPopoverOpenKey.value = null;
- keepOtherAmountPopoverOpenUntil.value = 0;
-};
-
-const handleOtherAmountSelected = (id) => {
- const selectedId = id ?? otherAmountAddId.value;
- if (selectedId === null || selectedId === undefined || selectedId === "") return;
- const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(selectedId));
- if (!opt) return;
-
- const exists = (productForm.value?.salesProductProcessList ?? []).some(
- (it) => String(it?.id) === String(opt.id)
- );
- if (exists) {
- proxy.$modal.msgWarning("璇ュ叾浠栭噾棰濋」鐩凡娣诲姞");
- return;
- }
-
- productForm.value.salesProductProcessList.push({
- id: opt.id,
- processName: opt.processName,
- unitPrice: opt.unitPrice ?? 0,
- quantity: 0,
- });
- calculateFromUnitPrice(true);
-
- // 閫夋嫨瀹屾垚鍚庡叧闂�滄柊澧炲叾浠栭噾棰濃�濆脊绐楋紝骞朵繚鎸佽鍐呪�滃叾浠栭噾棰濃�濆脊灞傚紑鍚紝渚夸簬鐩存帴濉啓鏁伴噺
- otherAmountAddDialogVisible.value = false;
- otherAmountAddId.value = null;
- const reopenOtherAmountPopover = () => {
- let targetRow = otherAmountAddTargetRow.value;
- const rowKey = otherAmountAddTargetRowKey.value;
- if (rowKey) {
- const matchedRow = (productData.value || []).find(
- (it) => String(it?.__tempKey ?? it?.id ?? "") === rowKey
- );
- if (matchedRow) targetRow = matchedRow;
- }
- if (targetRow && typeof targetRow === "object") {
- lockOtherAmountPopoverOpen(targetRow, 1500);
- targetRow.__otherAmountPopoverVisible = true;
- }
- };
- nextTick(() => {
- reopenOtherAmountPopover();
- setTimeout(reopenOtherAmountPopover, 0);
- setTimeout(reopenOtherAmountPopover, 80);
- });
- otherAmountAddTargetRow.value = null;
- otherAmountAddTargetRowKey.value = null;
-};
-
-const confirmAddOtherAmount = () => {
- handleOtherAmountSelected(otherAmountAddId.value);
-};
-
-const removeOtherAmountAt = (index) => {
- if (operationType.value === "view") return;
- if (!Array.isArray(productForm.value?.salesProductProcessList)) return;
- productForm.value.salesProductProcessList.splice(index, 1);
- calculateFromUnitPrice(true);
-};
-
-// 鍙戣揣瀹℃壒浜鸿妭鐐癸紙浠垮崗鍚屽鎵� infoFormDia.vue锛�
-const approverNodes = ref([{ id: 1, userId: null }]);
-let nextApproverId = 2;
-const addApproverNode = () => {
- approverNodes.value.push({ id: nextApproverId++, userId: null });
-};
-const removeApproverNode = (index) => {
- approverNodes.value.splice(index, 1);
-};
-
-// 瀵煎叆鐩稿叧
-const importUploadRef = ref(null);
-const importUpload = reactive({
- title: "瀵煎叆閿�鍞彴璐�",
- open: false,
- url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
- headers: { Authorization: "Bearer " + getToken() },
- isUploading: false,
- beforeUpload: (file) => {
- const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
- const isLt10M = file.size / 1024 / 1024 < 10;
- if (!isExcel) {
- proxy.$modal.msgError("涓婁紶鏂囦欢鍙兘鏄� xlsx/xls 鏍煎紡!");
- return false;
- }
- if (!isLt10M) {
- proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!");
- return false;
- }
- return true;
- },
- onChange: (file, fileList) => {
- console.log('鏂囦欢鐘舵�佹敼鍙�', file, fileList);
- },
- onProgress: (event, file, fileList) => {
- console.log('涓婁紶涓�...', event.percent);
- },
- onSuccess: (response, file, fileList) => {
- console.log('涓婁紶鎴愬姛', response, file, fileList);
- importUpload.isUploading = false;
- if (response.code === 200) {
- proxy.$modal.msgSuccess("瀵煎叆鎴愬姛");
- importUpload.open = false;
- if (importUploadRef.value) {
- importUploadRef.value.clearFiles();
- }
- getList();
- } else {
- proxy.$modal.msgError(response.msg || "瀵煎叆澶辫触");
- }
- },
- onError: (error, file, fileList) => {
- console.error('涓婁紶澶辫触', error, file, fileList);
- importUpload.isUploading = false;
- proxy.$modal.msgError("瀵煎叆澶辫触锛岃閲嶈瘯");
- },
-});
-
-const changeDaterange = (value) => {
- if (value) {
- searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
- searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
- } else {
- searchForm.entryDateStart = undefined;
- searchForm.entryDateEnd = undefined;
- }
- handleQuery();
-};
-
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
- // 鍙湁鍦ㄧ偣鍑绘悳绱㈡寜閽椂鎵嶉噸缃〉鐮佸埌绗竴椤�
- // 閬垮厤琛ㄥ崟瀛楁change浜嬩欢骞叉壈鍒嗛〉
- if (arguments.length === 0) {
- page.current = 1;
- }
- expandedRowKeys.value = [];
- getList();
-};
-const paginationChange = (obj) => {
- page.current = obj.page;
- page.size = obj.limit;
- getList();
-};
-const getList = () => {
- tableLoading.value = true;
- const { entryDate, ...rest } = searchForm;
- // 灏嗚寖鍥存棩鏈熷瓧娈典紶閫掔粰鍚庣
- const params = { ...rest, ...page };
- // 绉婚櫎褰曞叆鏃ユ湡鐨勯粯璁ゅ�艰缃紝鍙繚鐣欒寖鍥存棩鏈熷瓧娈�
- delete params.entryDate;
- // 鏌ヨ瀹㈡埛鍚嶇О涓庢柊澧炰繚鎸佷竴鑷达細鍏堥�� customerId锛屽啀鏄犲皠涓� customerName 鏌ヨ
- const selectedCustomer = (customerOption.value || []).find(
- (item) => String(item?.id ?? "") === String(params.customerId ?? "")
- );
- if (selectedCustomer?.customerName) {
- params.customerName = String(selectedCustomer.customerName).trim();
- } else {
- const cn = params.customerName != null ? String(params.customerName).trim() : "";
- if (cn) {
- params.customerName = cn;
- } else {
- delete params.customerName;
- }
- }
- delete params.customerId;
- return ledgerListPage(params)
- .then((res) => {
- tableLoading.value = false;
- tableData.value = res.records;
- tableData.value.map((item) => {
- item.children = [];
- });
- total.value = res.total;
- return res;
- })
- .catch(() => {
- tableLoading.value = false;
- });
-};
-
-// 鍏ュ簱锛堥攢鍞彴璐� -> 鍏ュ簱鐘舵�侊級
-const handleSalesStock = async () => {
- if (selectedRows.value.length !== 1) {
- ElMessage.warning("璇峰嬀閫変竴鏉″彴璐︽暟鎹繘琛屽叆搴�");
- return;
- }
- const row = selectedRows.value[0] || {};
- const id = row?.id;
- if (!id) {
- ElMessage.warning("鎵�閫夋暟鎹己灏慽d锛屾棤娉曞叆搴�");
- return;
- }
- if (Number(row.stockStatus) === 1) {
- ElMessage.info("璇ュ彴璐﹀凡鍏ュ簱锛屾棤闇�閲嶅鎿嶄綔");
- return;
- }
- try {
- await ElMessageBox.confirm("纭瀵规墍閫夊彴璐︽墽琛屽叆搴擄紵", "鎻愮ず", {
- confirmButtonText: "纭畾",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- });
- } catch {
- return;
- }
- proxy?.$modal?.loading?.("姝e湪鍏ュ簱锛岃绋嶅��...");
- try {
- await salesStock({ id });
- proxy?.$modal?.msgSuccess?.("鍏ュ簱鎴愬姛");
- await getList();
- } catch (e) {
- proxy?.$modal?.msgError?.("鍏ュ簱澶辫触锛岃绋嶅悗閲嶈瘯");
- } finally {
- proxy?.$modal?.closeLoading?.();
- }
-};
-
-// 鎵撳紑鈥滃伐鑹鸿矾绾块厤缃�濋�夋嫨寮圭獥锛堝繀椤绘樉寮忛�夋嫨锛�
-const openProcessFlowSelect = async (ledgerRow) => {
- if (!ledgerRow) return;
- if (!ledgerRow.isEdit) return;
-
- processFlowSelectLedgerRow.value = ledgerRow;
- processFlowSelectDefaultRouteId.value = null;
- processFlowSelectBoundRouteId.value = null;
- processFlowSelectBoundRouteName.value = "";
-
- try {
- const res = await getSaleProcessBindInfo(ledgerRow.id);
- const info = res?.data ?? res ?? {};
- const boundId =
- info?.processRouteId ??
- info?.routeId ??
- info?.id ??
- null;
- const boundName =
- info?.processRouteName ??
- info?.routeName ??
- info?.name ??
- "";
- processFlowSelectBoundRouteId.value = boundId;
- processFlowSelectBoundRouteName.value = boundName;
- processFlowSelectDefaultRouteId.value = boundId;
- } catch (e) {
- // 鏌ヨ澶辫触鏃舵寜鏈粦瀹氬鐞嗭紝涓嶉樆濉炲脊绐�
- processFlowSelectBoundRouteId.value = null;
- processFlowSelectBoundRouteName.value = "";
- processFlowSelectDefaultRouteId.value = null;
- }
-
- processFlowSelectDialogVisible.value = true;
-};
-
-// 缁戝畾宸ヨ壓璺嚎鍒板綋鍓嶅彴璐︽暟鎹�
-const handleProcessFlowSelectConfirm = async (routeId) => {
- const ledgerRow = processFlowSelectLedgerRow.value;
- if (!ledgerRow?.id) return;
-
- const finalRouteId = routeId ?? null;
- if (!finalRouteId) return;
-
- const oldRouteId = processFlowSelectBoundRouteId.value;
- if (oldRouteId !== null && oldRouteId !== undefined && oldRouteId !== "" && String(oldRouteId) !== String(finalRouteId)) {
- try {
- await ElMessageBox.confirm(
- "璇ヨ鍗曞凡缁戝畾宸ヨ壓璺嚎锛屾槸鍚︾‘瀹氭洿鎹紵",
- "鎻愮ず",
- {
- confirmButtonText: "纭畾",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- }
- );
- } catch {
- return;
- }
- }
-
- proxy?.$modal?.loading?.("姝e湪缁戝畾宸ヨ壓璺嚎锛岃绋嶅��...");
- try {
- await saleProcessBind({
- salesLedgerId: ledgerRow.id,
- processRouteId: finalRouteId,
- });
-
- proxy?.$modal?.msgSuccess?.("宸ヨ壓璺嚎缁戝畾鎴愬姛");
- processFlowSelectDialogVisible.value = false;
- // 缁戝畾鍚庡埛鏂板垪琛紝纭繚鎿嶄綔鍒楀啀娆$偣鍑昏兘鍥炴樉缁戝畾
- await getList();
- } catch (e) {
- proxy?.$modal?.msgError?.("缁戝畾澶辫触锛岃绋嶅悗閲嶈瘯");
- } finally {
- proxy?.$modal?.closeLoading?.();
- }
-};
-
-// 鑾峰彇浜у搧澶х被tree鏁版嵁
-const getProductOptions = () => {
- // 杩斿洖 Promise锛屼究浜庡湪缂栬緫浜у搧鏃剁瓑寰呭姞杞藉畬鎴�
- return productTreeList().then((res) => {
- productOptions.value = convertIdToValue(res);
- return productOptions.value;
- });
-};
-const formattedNumber = (row, column, cellValue) => {
- return parseFloat(cellValue).toFixed(2);
-};
-// 鑾峰彇tree瀛愭暟鎹�
-const getModels = (value) => {
- // 浜у搧澶х被鍙樺寲鏃讹紝閲嶇疆瑙勬牸鍨嬪彿涓庡帤搴︼紝閬垮厤鏃у�兼畫鐣�
- productForm.value.productModelId = null;
- productForm.value.specificationModel = "";
- productForm.value.thickness = null;
-
- if (!value) {
- productForm.value.productCategory = "";
- modelOptions.value = [];
- return;
- }
-
- productForm.value.productCategory = findNodeById(productOptions.value, value);
- modelList({ id: value }).then((res) => {
- modelOptions.value = res || [];
- });
-};
-const getProductModel = (value) => {
- const index = modelOptions.value.findIndex((item) => item.id === value);
- if (index !== -1) {
- productForm.value.specificationModel = modelOptions.value[index].model;
- const selectedModel = modelOptions.value[index];
- const modelThickness =
- selectedModel?.thickness ??
- selectedModel?.modelThickness ??
- selectedModel?.thick ??
- null;
- productForm.value.thickness =
- modelThickness === null || modelThickness === undefined || modelThickness === ""
- ? null
- : Number(modelThickness);
- } else {
- productForm.value.specificationModel = null;
- productForm.value.thickness = null;
- }
-};
-const filterProductCategoryNode = (value, data) => {
- if (!value) return true;
- return String(data?.label || "").toLowerCase().includes(String(value).toLowerCase());
-};
-const findNodeById = (nodes, productId) => {
- for (let i = 0; i < nodes.length; i++) {
- if (nodes[i].value === productId) {
- return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣
- }
- if (nodes[i].children && nodes[i].children.length > 0) {
- const foundNode = findNodeById(nodes[i].children, productId);
- if (foundNode) {
- return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖璇ヨ妭鐐�
- }
- }
- }
- return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
-};
-function convertIdToValue(data, level = 0) {
- return data.map((item) => {
- const { id, children, ...rest } = item;
- const hasChildren = Array.isArray(children) && children.length > 0;
- const newItem = {
- ...rest,
- value: id, // 灏� id 鏀逛负 value
- // 浠呭厑璁稿彾瀛愯妭鐐硅閫夋嫨锛堟湁瀛愯妭鐐圭殑鍒嗙被鑺傜偣缁熶竴绂佺敤锛�
- disabled: Boolean(rest?.disabled) || hasChildren,
- };
- if (hasChildren) {
- newItem.children = convertIdToValue(children, level + 1);
- }
-
- return newItem;
- });
-}
-// 鏍规嵁鍚嶇О鍙嶆煡浜у搧澶х被 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
-function 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 handleSelectionChange = (selection) => {
- // 杩囨护鎺夊瓙鏁版嵁
- selectedRows.value = selection.filter((item) => item.children !== undefined);
- console.log("selection", selectedRows.value);
-};
-const productSelected = (selectedRows) => {
- productSelectedRows.value = selectedRows;
-};
-const expandedRowKeys = ref([]);
-// 灞曞紑琛�
-const expandChange = (row, expandedRows) => {
- if (expandedRows.length > 0) {
- expandedRowKeys.value = [];
- try {
- productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
- const index = tableData.value.findIndex((item) => item.id === row.id);
- if (index > -1) {
- tableData.value[index].children = res.data;
- }
- expandedRowKeys.value.push(row.id);
- });
- } catch (error) {
- console.log(error);
- }
- } else {
- expandedRowKeys.value = [];
- }
-};
-
-// 娣诲姞琛ㄨ绫诲悕鏂规硶
-const tableRowClassName = ({ row }) => {
- if (!row.deliveryDate) return '';
- if (row.isFh) return '';
-
- const diff = row.deliveryDaysDiff;
- if (diff === 15) {
- return 'yellow';
- } else if (diff === 10) {
- return 'pink';
- } else if (diff === 2) {
- return 'purple';
- } else if (diff < 2) {
- return 'red';
- }
-};
-// 涓昏〃鍚堣鏂规硶
-const summarizeMainTable = (param) => {
- return proxy.summarizeTable(param, [
- "contractAmount",
- "taxInclusiveTotalPrice",
- "taxExclusiveTotalPrice",
- ]);
-};
-// 瀛愯〃鍚堣鏂规硶
-const summarizeChildrenTable = (param) => {
- return proxy.summarizeTable(param, [
- "taxInclusiveUnitPrice",
- "taxInclusiveTotalPrice",
- "taxExclusiveTotalPrice",
- ]);
-};
-// 鎵撳紑寮规
-const openForm = async (type, row) => {
- operationType.value = type;
- form.value = {};
- productData.value = [];
- selectedQuotation.value = null;
- let userLists = await userListNoPage();
- userList.value = userLists.data;
- customerList().then((res) => {
- customerOption.value = res;
- });
- form.value.entryPerson = userStore.id;
- if (type === "add") {
- // 鏂板鏃惰缃綍鍏ユ棩鏈熶负褰撳ぉ
- form.value.entryDate = getCurrentDate();
- // 绛捐鏃ユ湡榛樿涓哄綋澶�
- form.value.executionDate = getCurrentDate();
- form.value.customerRemarks = "";
- } else {
- currentId.value = row.id;
- getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
- form.value = { ...res };
- form.value.entryPerson = Number(res.entryPerson);
- // 瀛楁鍚嶅吋瀹癸細鍚庣鍙兘杩斿洖 customer_remarks
- form.value.customerRemarks = res?.customerRemarks ?? res?.customer_remarks ?? "";
- productData.value = form.value.productData;
- fileList.value = form.value.salesLedgerFiles;
- });
- }
- // let userAll = await userStore.getInfo()
- // userList.value.forEach(element => {
- // if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
- // form.value.entryPerson = userAll.user.userId // 璁剧疆榛樿涓氬姟鍛樹负褰撳墠鐢ㄦ埛
- // }
- // });
- form.value.entryDate = getCurrentDate(); // 璁剧疆榛樿褰曞叆鏃ユ湡涓哄綋鍓嶆棩鏈�
- if (type === "add") {
- form.value.deliveryDate = dayjs(form.value.entryDate).add(7, "day").format("YYYY-MM-DD");
- }
- dialogFormVisible.value = true;
-};
-
-// 鎵撳紑鎶ヤ环鍗曢�夋嫨寮圭獥锛堜粎瀹℃壒閫氳繃锛�
-const openQuotationDialog = async () => {
- if (operationType.value === "view") return;
- quotationDialogVisible.value = true;
- // 鎵撳紑寮圭獥鏃堕噸缃垎椤靛埌绗竴椤�
- quotationPage.current = 1;
- // 鍏堢‘淇濆鎴峰垪琛ㄥ凡鍔犺浇锛屼究浜庡悗缁洖濉� customerId
- if (!customerOption.value || customerOption.value.length === 0) {
- try {
- const res = await customerList();
- customerOption.value = res;
- } catch (e) {
- // ignore锛屽厑璁哥敤鎴峰悗缁墜鍔ㄩ�夋嫨瀹㈡埛
- }
- }
- await fetchQuotationList();
-};
-
-const fetchQuotationList = async () => {
- quotationLoading.value = true;
- try {
- const params = {
- // 鍚庣鍒嗛〉瀛楁锛歝urrent / size
- current: quotationPage.current,
- size: quotationPage.size,
- ...quotationSearchForm,
- status: "閫氳繃",
- };
- const res = await getQuotationList(params);
- quotationList.value = res?.data?.records || [];
- quotationPage.total = res?.data?.total || 0;
- } finally {
- quotationLoading.value = false;
- }
-};
-
-const resetQuotationSearch = async () => {
- quotationSearchForm.quotationNo = "";
- quotationSearchForm.customer = "";
- quotationPage.current = 1;
- await fetchQuotationList();
-};
-
-// 鎶ヤ环鍗曞脊妗嗗垎椤靛垏鎹�
-const quotationPaginationChange = (obj) => {
- quotationPage.current = obj.page;
- quotationPage.size = obj.limit;
- fetchQuotationList();
-};
-
-// 閫変腑鎶ヤ环鍗曞悗鍥炲~鍒板彴璐﹁〃鍗�
-const applyQuotation = (row) => {
- if (!row) return;
- selectedQuotation.value = row;
-
- // 涓氬姟鍛�
- form.value.salesman = (row.salesperson || "").trim();
-
- // 瀹㈡埛鍚嶇О -> customerId
- const qCustomerName = String(row.customer || "").trim();
- const customer = (customerOption.value || []).find((c) => {
- const name = String(c.customerName || "").trim();
- return name === qCustomerName || name.includes(qCustomerName) || qCustomerName.includes(name);
- });
- if (customer?.id) {
- form.value.customerId = customer.id;
- } else {
- // 濡傛灉鎵句笉鍒帮紝淇濈暀鍘熷�硷紙鍏佽鐢ㄦ埛鎵嬪姩閫夋嫨/涓嶆墦鏂凡鏈夎緭鍏ワ級
- form.value.customerId = form.value.customerId || "";
- }
-
- // 浜у搧淇℃伅鏄犲皠锛氭姤浠� products -> 鍙拌处 productData
- const products = Array.isArray(row.products) ? row.products : [];
- productData.value = products.map((p) => {
- const quantity = Number(p.quantity ?? 0) || 0;
- const unitPrice = Number(p.unitPrice ?? 0) || 0;
- const settlePieceArea = Number(p.settlePieceArea ?? 0) || 1;
- const taxRate = "13"; // 榛樿 13%锛屼究浜庣洿鎺ユ彁浜わ紙濡傞渶鍙湪浜у搧涓嚜琛屼慨鏀癸級
- const taxInclusiveTotalPrice = (unitPrice * settlePieceArea * quantity).toFixed(2);
- const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
- return {
- // 鍙拌处瀛楁
- productCategory: p.product || p.productName || "",
- specificationModel: p.specification || "",
- thickness: p.thickness,
- quantity: quantity,
- taxRate: taxRate,
- taxInclusiveUnitPrice: unitPrice.toFixed(2),
- taxInclusiveTotalPrice: taxInclusiveTotalPrice,
- taxExclusiveTotalPrice: taxExclusiveTotalPrice,
- invoiceType: "澧炴櫘绁�",
- // 鏂板锛氶粯璁ゅ�硷紙閬垮厤鍚庣画鎻愪氦鏃跺瓧娈电己澶憋級
- width: 0,
- height: 0,
- actualPieceArea: 0,
- actualTotalArea: 0,
- settlePieceArea: 0,
- settleTotalArea: 0,
- processRequirement: "",
- floorCode: "",
- remark: "",
- salesProductProcessList: [],
- };
- });
-
- quotationDialogVisible.value = false;
-};
-function changs(val) {
- console.log(val);
-}
-// 涓婁紶鍓嶆牎妫�
-function handleBeforeUpload(file) {
- // 鏍℃鏂囦欢澶у皬
- // if (file.size > 1024 * 1024 * 10) {
- // proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
- // return false;
- // }
- proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
- return true;
-}
-// 涓婁紶澶辫触
-function handleUploadError(err) {
- proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
- proxy.$modal.closeLoading();
-}
-// 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file, uploadFiles) {
- proxy.$modal.closeLoading();
- if (res.code === 200) {
- file.tempId = res.data.tempId;
- proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
- } else {
- proxy.$modal.msgError(res.msg);
- proxy.$refs.fileUpload.handleRemove(file);
- }
-}
-// 绉婚櫎鏂囦欢
-function handleRemove(file) {
- if (operationType.value === "edit") {
- let ids = [];
- ids.push(file.id);
- delLedgerFile(ids).then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
- });
- }
-}
-// 鎻愪氦琛ㄥ崟
-const submitForm = () => {
- proxy.$refs["formRef"].validate((valid) => {
- if (valid) {
- console.log('productData.value--', productData.value)
- // 琛屽唴缂栬緫鏈繚瀛樻椂涓嶅厑璁告彁浜わ紝閬垮厤鑴忔暟鎹�/涓存椂瀛楁杩涘叆鍚庣
- const hasEditingRow = (productData.value || []).some((r) => r && r.__editing);
- if (hasEditingRow) {
- proxy.$modal.msgWarning("浜у搧淇℃伅瀛樺湪鏈繚瀛樼殑缂栬緫琛岋紝璇峰厛淇濆瓨鎴栧彇娑�");
- return;
- }
- if (productData.value !== null && productData.value.length > 0) {
- const cleanedProducts = (productData.value || []).map((p) => {
- if (!p || typeof p !== "object") return p;
- const { __editing, __isNew, __backup, __productCategoryId, __tempKey, __otherAmountPopoverVisible, ...rest } = p;
- rest.taxRate = Number(rest.taxRate ?? 0) || 0;
- rest.quantity = Number(rest.quantity ?? 0) || 0;
- return rest;
- });
- form.value.productData = proxy.HaveJson(cleanedProducts);
- } else {
- proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
- return;
- }
- let tempFileIds = [];
- if (fileList.value !== null && fileList.value.length > 0) {
- tempFileIds = fileList.value.map((item) => item.tempId);
- }
- form.value.tempFileIds = tempFileIds;
- form.value.type = 1;
- const submitPayload = { ...form.value };
- delete submitPayload.paymentMethod;
- addOrUpdateSalesLedger(submitPayload).then((res) => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeDia();
- getList();
- });
- }
- });
-};
-// 鍏抽棴寮规
-const closeDia = () => {
- proxy.resetForm("formRef");
- dialogFormVisible.value = false;
-};
-
-const productIndex = ref(0);
-// 鎵撳紑浜у搧寮规
-const openProductForm = async (type, row, index) => {
- // 缂栬緫鏃舵鏌ヤ骇鍝佹槸鍚﹀凡鍙戣揣鎴栧鏍搁�氳繃
- if (type === "edit" && isProductShipped(row)) {
- proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
- return;
- }
-
- productOperationType.value = type;
- productForm.value = {};
- proxy.resetForm("productFormRef");
- // 纭繚澶氶�夐」榛樿鏄暟缁勶紝閬垮厤 el-select multiple 鎶ラ敊
- productForm.value.salesProductProcessList = [];
- otherAmountAddDialogVisible.value = false;
- otherAmountAddId.value = null;
- if (type === "edit") {
- productForm.value = { ...row };
-
- // 瀛楁鍛藉悕鍏煎锛氫紭鍏堥┘宄帮紙濡� actualPieceArea锛夛紝鍏煎鍚庣鍙兘杩斿洖涓嬪垝绾匡紙濡� actual_piece_area锛�
- productForm.value.actualPieceArea = row?.actualPieceArea ?? row?.actual_piece_area ?? 0;
- productForm.value.actualTotalArea = row?.actualTotalArea ?? row?.actual_total_area ?? 0;
- productForm.value.settlePieceArea = row?.settlePieceArea ?? row?.settle_piece_area ?? 0;
- productForm.value.settleTotalArea = row?.settleTotalArea ?? row?.settle_total_area ?? 0;
-
- // 鍔犲伐瑕佹眰/澶囨敞鍏煎锛氬悗绔彲鑳戒娇鐢ㄥ叾瀹冨懡鍚�
- productForm.value.processRequirement =
- row?.processRequirement ?? row?.process_requirement ?? "";
- productForm.value.remark = row?.remark ?? row?.remarks ?? "";
- productForm.value.floorCode = row?.floorCode ?? row?.floor_code ?? "";
- // 宸ヨ壓娴佺▼閰嶇疆缁戝畾瀛楁锛堝悗缁敱鍚庣纭瀛楁鍚嶏級
- productForm.value.processFlowConfigId =
- row?.processFlowConfigId ?? row?.process_flow_config_id ?? null;
-
- // 鍛ㄩ暱鍥炴樉锛堝鍚庣杩斿洖锛涙渶缁堜粛浠ュ叕寮忚绠椾负鍑嗭級
- productForm.value.perimeter =
- row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0;
-
- // 鍚庣鐩存帴杩斿洖 thickness
- productForm.value.thickness = row?.thickness;
-
- productForm.value.salesProductProcessList = normalizeOtherAmountsFromRow(row);
- productIndex.value = index;
- // 缂栬緫鏃舵牴鎹骇鍝佸ぇ绫诲悕绉板弽鏌� tree 鑺傜偣 id锛屽苟鍔犺浇瑙勬牸鍨嬪彿鍒楄〃
- try {
- const options = productOptions.value && productOptions.value.length > 0
- ? productOptions.value
- : await getProductOptions();
- const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
- if (categoryId) {
- const models = await modelList({ id: categoryId });
- modelOptions.value = models || [];
- // 鏍规嵁褰撳墠瑙勬牸鍨嬪彿鍚嶇О鍙嶆煡骞惰缃� productModelId锛屼究浜庝笅鎷夋鏄剧ず宸查�夊��
- const currentModel = (modelOptions.value || []).find(
- (m) => m.model === productForm.value.specificationModel
- );
- if (currentModel) {
- productForm.value.productModelId = currentModel.id;
- }
- }
- } catch (e) {
- // 鍔犺浇澶辫触鏃朵繚鎸佸彲缂栬緫锛屼笉涓柇寮圭獥
- console.error("鍔犺浇浜у搧瑙勬牸鍨嬪彿澶辫触", e);
- }
-
- // 鏍规嵁褰撳墠瀹介珮閲嶆柊璁$畻鍛ㄩ暱涓庨潰绉�
- recalcPerimeterFromWidthHeight();
- recalcAreaFromWidthHeight();
-
- // 鍥炴樉鈥滃叾浠栭噾棰濃�濆閫夛細鍏堟媺鍙栦笅鎷夐�夐」锛屽啀琛ラ綈 processName
- await fetchOtherAmountSelectOptions(true);
- mergeOtherAmountOptionsBySelection(productForm.value.salesProductProcessList);
- productForm.value.salesProductProcessList = fillOtherAmountProcessName(productForm.value.salesProductProcessList);
- } else {
- getProductOptions()
- // 鏂板鏃朵笅鎷夐�夐」鍔犺浇涓�娆″嵆鍙�
- fetchOtherAmountSelectOptions(true);
- }
- productFormVisible.value = true;
-};
-// 鎻愪氦浜у搧琛ㄥ崟
-const submitProduct = () => {
- proxy.$refs["productFormRef"].validate((valid) => {
- if (valid) {
- // 鍘氬害淇濈暀 15 浣嶅皬鏁帮紝閬垮厤鐢变簬娴偣璁$畻/杈撳叆瀵艰嚧绮惧害鍋忓樊
- if (productForm.value.thickness !== null && productForm.value.thickness !== undefined) {
- productForm.value.thickness = Number(Number(productForm.value.thickness).toFixed(15));
- }
-
- // 闈㈢Н/鎬昏瀛楁鍦ㄦ彁浜ゅ墠鍏滃簳璁$畻涓�娆�
- recalcAreaTotals();
- // 鎻愪氦鍏滃簳锛氱◣鐜�/鏁伴噺鏈~鏃舵寜鏁板瓧 0 浼犻��
- productForm.value.taxRate = Number(productForm.value.taxRate ?? 0) || 0;
- productForm.value.quantity = Number(productForm.value.quantity ?? 0) || 0;
- // 鍏朵粬閲戦鍙彁浜� {id, processName, quantity}锛堝悗绔瓧娈碉細salesProductProcessList锛�
- productForm.value.salesProductProcessList = (Array.isArray(productForm.value.salesProductProcessList)
- ? productForm.value.salesProductProcessList
- : []
- )
- .map((it) => ({
- id: it?.id,
- processName: it?.processName ?? "",
- unitPrice: Number(it?.unitPrice ?? 0) || 0,
- quantity: Number(it?.quantity ?? 0) || 0,
- }))
- .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
-
- if (operationType.value === "edit") {
- submitProductEdit();
- } else {
- if(productOperationType.value === "add"){
- productData.value.push({ ...productForm.value });
- }else{
- productData.value[productIndex.value] = { ...productForm.value }
- }
- closeProductDia();
- }
- }
- });
-};
-const submitProductEdit = () => {
- productForm.value.salesLedgerId = currentId.value;
- productForm.value.type = 1
- addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeProductDia();
- getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
- productData.value = res.productData;
- });
- });
-};
-// 鍒犻櫎浜у搧
-const deleteProduct = () => {
- if (productSelectedRows.value.length === 0) {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
-
- // 妫�鏌ユ槸鍚︽湁宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝�
- const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
- if (shippedProducts.length > 0) {
- proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳藉垹闄�");
- return;
- }
-
- if (operationType.value === "add") {
- productSelectedRows.value.forEach((selectedRow) => {
- const index = productData.value.findIndex((product) => {
- if (!product || !selectedRow) return false;
- // 鏂板琛� id 涓虹┖鏃讹紝鐢ㄤ复鏃� key 瀹氫綅
- if (product.id != null && selectedRow.id != null) {
- return String(product.id) === String(selectedRow.id);
- }
- return (
- product.__tempKey &&
- selectedRow.__tempKey &&
- String(product.__tempKey) === String(selectedRow.__tempKey)
- );
- });
- if (index !== -1) {
- productData.value.splice(index, 1);
- }
- });
- } else {
- let ids = [];
- if (productSelectedRows.value.length > 0) {
- ids = productSelectedRows.value.map((item) => item.id);
- }
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- delProduct(ids).then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
- closeProductDia();
- getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
- (res) => {
- productData.value = res.productData;
- }
- );
- });
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
- }
-};
-// 鍏抽棴浜у搧寮规
-const closeProductDia = () => {
- proxy.resetForm("productFormRef");
- productFormVisible.value = false;
- otherAmountAddDialogVisible.value = false;
- otherAmountAddId.value = null;
-};
-// 瀵煎叆
-const handleImport = () => {
- importUpload.title = "瀵煎叆閿�鍞彴璐�";
- importUpload.open = true;
- if (importUploadRef.value) {
- importUploadRef.value.clearFiles();
- }
-};
-
-// 涓嬭浇瀵煎叆妯℃澘
-const downloadTemplate = () => {
- proxy.download("/sales/ledger/exportTemplate", {}, "閿�鍞彴璐﹀鍏ユā鏉�.xlsx");
-};
-
-// 鎻愪氦瀵煎叆鏂囦欢
-const submitImportFile = () => {
- importUpload.isUploading = true;
- proxy.$refs["importUploadRef"].submit();
-};
-
-// 瀵煎嚭
-const handleOut = () => {
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- proxy.download("/sales/ledger/export", {}, "閿�鍞彴璐�.xlsx");
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
-};
-/** 鍒ゆ柇鍗曚釜浜у搧鏄惁宸插彂璐э紙鏍规嵁shippingStatus鍒ゆ柇锛屽凡鍙戣揣鎴栧鏍搁�氳繃涓嶅彲缂栬緫鍜屽垹闄わ級 */
-const isProductShipped = (product) => {
- if (!product) return false;
- const status = String(product.shippingStatus || "").trim();
- // 濡傛灉鍙戣揣鐘舵�佹槸"宸插彂璐�"鎴�"瀹℃牳閫氳繃"锛屽垯涓嶅彲缂栬緫鍜屽垹闄�
- return status === "宸插彂璐�" || status === "瀹℃牳閫氳繃";
-};
-
-/** 鍒ゆ柇閿�鍞鍗曚笅鏄惁瀛樺湪宸插彂璐�/鍙戣揣瀹屾垚鐨勪骇鍝侊紙涓嶅彲鍒犻櫎锛� */
-const hasShippedProducts = (products) => {
- if (!products || !products.length) return false;
- return products.some((p) => {
- const status = String(p.shippingStatus || "").trim();
- // 鏈夊彂璐ф棩鏈熸垨杞︾墝鍙疯涓哄凡鍙戣揣
- if (p.shippingDate || p.shippingCarNumber) return true;
- // 宸茶繘琛屽彂璐с�佸彂璐у畬鎴愩�佸凡鍙戣揣 鍧囦笉鍙垹闄�
- return status === "宸茶繘琛屽彂璐�" || status === "鍙戣揣瀹屾垚" || status === "宸插彂璐�";
- });
-};
-
-// 鍒犻櫎
-const handleDelete = async () => {
- if (selectedRows.value.length === 0) {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
- const ids = selectedRows.value.map((item) => item.id);
-
- // 妫�鏌ユ槸鍚︽湁宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曪紝鑻ユ湁鍒欎笉鍏佽鍒犻櫎
- const cannotDeleteNames = [];
- for (const row of selectedRows.value) {
- let products = row.children && row.children.length > 0 ? row.children : null;
- if (!products) {
- try {
- const res = await productList({ salesLedgerId: row.id, type: 1 });
- products = res.data || [];
- } catch {
- products = [];
- }
- }
- if (hasShippedProducts(products)) {
- cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
- }
- }
- if (cannotDeleteNames.length > 0) {
- proxy.$modal.msgWarning("宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曚笉鑳藉垹闄わ細" + cannotDeleteNames.join("銆�"));
- return;
- }
-
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- delLedger(ids).then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
- getList();
- });
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
-};
-
-const handlePrintCommand = async (command) => {
- if (command !== "finishedProcessCard" && command !== "salesOrder" && command !== "salesDeliveryNote") return;
- if (command === "salesDeliveryNote") {
- if (selectedRows.value.length === 0) {
- proxy.$modal.msgWarning("璇疯嚦灏戦�夋嫨涓�鏉¢攢鍞彴璐︽暟鎹繘琛屾墦鍗�");
- return;
- }
- const customerNames = Array.from(
- new Set(selectedRows.value.map((item) => String(item?.customerName ?? "").trim()))
- );
- if (customerNames.length > 1) {
- proxy.$modal.msgWarning("浠呮敮鎸佺浉鍚屽鎴峰悕绉扮殑閿�鍞彴璐﹀悎骞跺彂璐ф墦鍗�");
- return;
- }
- } else if (selectedRows.value.length !== 1) {
- proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉¢攢鍞彴璐︽暟鎹繘琛屾墦鍗�");
- return;
- }
-
- const selectedRow = selectedRows.value[0];
- const selectedId = selectedRow?.id;
- if (command === "salesDeliveryNote") {
- const selectedIds = selectedRows.value
- .map((item) => item?.id)
- .filter((id) => id !== null && id !== undefined && id !== "");
- if (selectedIds.length !== selectedRows.value.length) {
- proxy.$modal.msgWarning("褰撳墠閫夋嫨鏁版嵁瀛樺湪缂哄皯ID鐨勮褰曪紝鏃犳硶鎵撳嵃");
- return;
- }
- const loadingText =
- command === "salesOrder"
- ? "姝e湪鑾峰彇閿�鍞鍗曟暟鎹紝璇风◢鍊�..."
- : command === "salesDeliveryNote"
- ? "姝e湪鑾峰彇閿�鍞彂璐у崟鏁版嵁锛岃绋嶅��..."
- : "姝e湪鑾峰彇鐢熶骇娴佺▼鍗℃暟鎹紝璇风◢鍊�...";
- proxy.$modal.loading(loadingText);
- try {
- const res = await getSalesInvoices(selectedIds);
- const salesInvoiceData = res?.data ?? {};
- printSalesDeliveryNote(salesInvoiceData, selectedRow);
- } catch (error) {
- console.error("鎵撳嵃閿�鍞彂璐у崟澶辫触:", error);
- proxy.$modal.msgError("鎵撳嵃澶辫触锛岃绋嶅悗閲嶈瘯");
- } finally {
- proxy.$modal.closeLoading();
- }
- return;
- }
- if (!selectedId) {
- proxy.$modal.msgWarning("褰撳墠閫夋嫨鏁版嵁缂哄皯ID锛屾棤娉曟墦鍗�");
- return;
- }
-
- const loadingText =
- command === "salesOrder"
- ? "姝e湪鑾峰彇閿�鍞鍗曟暟鎹紝璇风◢鍊�..."
- : command === "salesDeliveryNote"
- ? "姝e湪鑾峰彇閿�鍞彂璐у崟鏁版嵁锛岃绋嶅��..."
- : "姝e湪鑾峰彇鐢熶骇娴佺▼鍗℃暟鎹紝璇风◢鍊�...";
- proxy.$modal.loading(loadingText);
- try {
- if (command === "salesOrder") {
- const res = await getSalesOrder(selectedId);
- const salesOrderData = res?.data ?? {};
- printSalesOrder(salesOrderData);
- } else {
- const res = await getProcessCard(selectedId);
- const processCardData = res?.data ?? {};
- printFinishedProcessCard(processCardData);
- }
- } catch (error) {
- console.error(
- command === "salesOrder"
- ? "鎵撳嵃閿�鍞鍗曞け璐�:"
- : command === "salesDeliveryNote"
- ? "鎵撳嵃閿�鍞彂璐у崟澶辫触:"
- : "鎵撳嵃鐢熶骇娴佺▼鍗″け璐�:",
- error
- );
- proxy.$modal.msgError("鎵撳嵃澶辫触锛岃绋嶅悗閲嶈瘯");
- } finally {
- proxy.$modal.closeLoading();
- }
-};
-
-const handlePrintLabel = async () => {
- if (selectedRows.value.length !== 1) {
- proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉¢攢鍞彴璐︽暟鎹繘琛屾爣绛炬墦鍗�");
- return;
- }
-
- const selectedId = selectedRows.value[0]?.id;
- if (!selectedId) {
- proxy.$modal.msgWarning("褰撳墠閫夋嫨鏁版嵁缂哄皯ID锛屾棤娉曟墦鍗版爣绛�");
- return;
- }
-
- proxy.$modal.loading("姝e湪鑾峰彇鏍囩鏁版嵁锛岃绋嶅��...");
- try {
- const res = await getSalesLabel(selectedId);
- const labelList = res?.data ?? [];
- if (!Array.isArray(labelList) || labelList.length === 0) {
- proxy.$modal.msgWarning("鏆傛棤鍙墦鍗版爣绛炬暟鎹�");
- return;
- }
- printSalesLabel(labelList);
- } catch (error) {
- console.error("鎵撳嵃鏍囩澶辫触:", error);
- proxy.$modal.msgError("鎵撳嵃鏍囩澶辫触锛岃绋嶅悗閲嶈瘯");
- } finally {
- proxy.$modal.closeLoading();
- }
-};
-
-const mathNum = () => {
- console.log("productForm.value", productForm.value);
- if (!productForm.value.taxInclusiveUnitPrice) {
- return;
- }
- if (!productForm.value.quantity) {
- return;
- }
- const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
- // 鍚◣鎬讳环璁$畻 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺 + 鍏朵粬閲戦鎬诲拰
- const basePrice = proxy.calculateTaxIncludeTotalPrice(
- productForm.value.taxInclusiveUnitPrice * settlePieceArea,
- productForm.value.quantity
- );
- const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
- return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
- }, 0);
- productForm.value.taxInclusiveTotalPrice = (parseFloat(basePrice) + otherAmountTotal).toFixed(2);
- if (productForm.value.taxRate) {
- // 涓嶅惈绋庢�讳环璁$畻
- productForm.value.taxExclusiveTotalPrice =
- proxy.calculateTaxExclusiveTotalPrice(
- productForm.value.taxInclusiveTotalPrice,
- productForm.value.taxRate
- );
- }
-};
-
-// 鏂板锛氬昂瀵�(瀹介珮)涓庨潰绉�(鍗曠墖/鎬昏)鑱斿姩
-const recalcAreaTotals = () => {
- const qty = Number(productForm.value.quantity ?? 0) || 0;
- const actualPiece = Number(productForm.value.actualPieceArea ?? 0) || 0;
- const settlePiece = Number(productForm.value.settlePieceArea ?? 0) || 0;
-
- productForm.value.actualTotalArea = Number((actualPiece * qty).toFixed(5));
- productForm.value.settleTotalArea = Number((settlePiece * qty).toFixed(5));
-};
-
-// 鏂板锛氬懆闀�(cm)锛堥噸绠県eavyBox鍛ㄩ暱锛�
-// width/height 鍗曚綅涓� mm锛屽洜姝ゅ懆闀�(cm)闇�瑕侀櫎浠� 10
-const recalcPerimeterFromWidthHeight = () => {
- const width = Number(productForm.value.width ?? 0) || 0;
- const height = Number(productForm.value.height ?? 0) || 0;
-
- if (width <= 0 || height <= 0) {
- productForm.value.perimeter = 0;
- return;
- }
-
- // 鍛ㄩ暱 = (瀹� + 楂�) * 2锛屽崟浣嶄粠 mm 杞负 cm锛�/10
- productForm.value.perimeter = Number((((width + height) * 2) / 10).toFixed(2));
-};
-
-const recalcAreaFromWidthHeight = () => {
- const width = Number(productForm.value.width ?? 0) || 0;
- const height = Number(productForm.value.height ?? 0) || 0;
-
- if (width <= 0 || height <= 0) {
- // 瀹介珮涓虹┖/涓�0鏃讹紝鎶婂崟鐗囬潰绉笌鎬婚潰绉疆涓�0
- productForm.value.actualPieceArea = 0;
- productForm.value.actualTotalArea = 0;
- productForm.value.perimeter = 0;
-
- // 鍙湁鍦ㄧ粨绠楀崟鐗囬潰绉篃涓虹┖/涓�0鏃讹紝鎵嶅悓姝ョ疆0锛岄伩鍏嶈鐩栫敤鎴锋墜鍔ㄥ~鍐�
- const settlePiece = Number(productForm.value.settlePieceArea ?? 0) || 0;
- if (!settlePiece) {
- productForm.value.settlePieceArea = 0;
- }
- productForm.value.settleTotalArea = Number(
- ((Number(productForm.value.settlePieceArea ?? 0) || 0) * (Number(productForm.value.quantity ?? 0) || 0)).toFixed(5)
- );
- return;
- }
-
- const computedPieceArea = (width * height) / 1e6; // mm*mm -> 銕�
- const computed = Number(computedPieceArea.toFixed(5));
-
- productForm.value.actualPieceArea = computed;
- productForm.value.settlePieceArea = computed;
-
- recalcPerimeterFromWidthHeight();
- recalcAreaTotals();
- // 闈㈢Н鏇存柊鍚庯紝閲嶆柊璁$畻鍚◣鎬讳环 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺
- calculateFromUnitPrice(true);
-};
-
-// 鏍规嵁鍚◣鎬讳环璁$畻鍚◣鍗曚环鍜屾暟閲�
-const calculateFromTotalPrice = () => {
- if (isCalculating.value) return;
-
- const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
- const quantity = parseFloat(productForm.value.quantity);
-
- if (!totalPrice || !quantity || quantity <= 0) {
- return;
- }
-
- isCalculating.value = true;
-
- // 璁$畻鍚◣鍗曚环 = (鍚◣鎬讳环 - 鍏朵粬閲戦鎬诲拰) / 鏁伴噺
- const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
- return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
- }, 0);
- const basePrice = totalPrice - otherAmountTotal;
- productForm.value.taxInclusiveUnitPrice = (basePrice / quantity).toFixed(2);
-
- // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
- if (productForm.value.taxRate) {
- productForm.value.taxExclusiveTotalPrice =
- proxy.calculateTaxExclusiveTotalPrice(
- totalPrice,
- productForm.value.taxRate
- );
- }
-
- isCalculating.value = false;
-};
-
-// 鏍规嵁涓嶅惈绋庢�讳环璁$畻鍚◣鍗曚环鍜屾暟閲�
-const calculateFromExclusiveTotalPrice = () => {
- // if (!productForm.value.taxRate) {
- // proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
- // return;
- // }
- if (isCalculating.value) return;
-
- const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
- const quantity = parseFloat(productForm.value.quantity);
- const taxRate = parseFloat(productForm.value.taxRate);
-
- if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
- return;
- }
-
- isCalculating.value = true;
-
- // 鍏堣绠楀惈绋庢�讳环 = 涓嶅惈绋庢�讳环 / (1 - 绋庣巼/100)
- const taxRateDecimal = taxRate / 100;
- const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
- productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
-
- // 璁$畻鍚◣鍗曚环 = (鍚◣鎬讳环 - 鍏朵粬閲戦鎬诲拰) / 鏁伴噺
- const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
- return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
- }, 0);
- const basePrice = inclusiveTotalPrice - otherAmountTotal;
- productForm.value.taxInclusiveUnitPrice = (basePrice / quantity).toFixed(2);
-
- isCalculating.value = false;
-};
-
-// 鏍规嵁鏁伴噺鍙樺寲璁$畻鎬讳环
-const calculateFromQuantity = () => {
- // if (!productForm.value.taxRate) {
- // proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
- // return;
- // }
- if (isCalculating.value) return;
-
- const quantity = parseFloat(productForm.value.quantity);
- const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
- const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
-
- if (!quantity || quantity <= 0 || !unitPrice) {
- return;
- }
-
- isCalculating.value = true;
-
- // 璁$畻鍚◣鎬讳环 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺 + 鍏朵粬閲戦鎬诲拰
- const basePrice = unitPrice * settlePieceArea * quantity;
- const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
- return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
- }, 0);
- productForm.value.taxInclusiveTotalPrice = (basePrice + otherAmountTotal).toFixed(2);
-
- // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
- if (productForm.value.taxRate) {
- productForm.value.taxExclusiveTotalPrice =
- proxy.calculateTaxExclusiveTotalPrice(
- productForm.value.taxInclusiveTotalPrice,
- productForm.value.taxRate
- );
- }
-
- isCalculating.value = false;
-};
-
-// 鏍规嵁鍚◣鍗曚环鍙樺寲璁$畻鎬讳环
-const calculateFromUnitPrice = (silent = false) => {
- // if (!productForm.value.taxRate) {
- // if (!silent) proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
- // return;
- // }
- if (isCalculating.value) return;
-
- const quantity = parseFloat(productForm.value.quantity);
- const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
- const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
-
- if (!quantity || quantity <= 0 || !unitPrice) {
- return;
- }
-
- isCalculating.value = true;
-
- // 璁$畻鍚◣鎬讳环 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺 + 鍏朵粬閲戦鎬诲拰
- const basePrice = unitPrice * settlePieceArea * quantity;
- const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
- return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
- }, 0);
- productForm.value.taxInclusiveTotalPrice = (basePrice + otherAmountTotal).toFixed(2);
-
- // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
- if (productForm.value.taxRate) {
- productForm.value.taxExclusiveTotalPrice =
- proxy.calculateTaxExclusiveTotalPrice(
- productForm.value.taxInclusiveTotalPrice,
- productForm.value.taxRate
- );
- }
-
- isCalculating.value = false;
-};
-
-// 鏍规嵁绋庣巼鍙樺寲璁$畻涓嶅惈绋庢�讳环
-const calculateFromTaxRate = () => {
- // if (!productForm.value.taxRate) {
- // proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
- // return;
- // }
- if (isCalculating.value) return;
-
- const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
- const taxRate = parseFloat(productForm.value.taxRate);
-
- if (!inclusiveTotalPrice || !taxRate) {
- return;
- }
-
- isCalculating.value = true;
-
- // 璁$畻涓嶅惈绋庢�讳环
- productForm.value.taxExclusiveTotalPrice =
- proxy.calculateTaxExclusiveTotalPrice(
- inclusiveTotalPrice,
- taxRate
- );
-
- isCalculating.value = false;
-};
-/**
- * 鑾峰彇鍙戣揣鐘舵�佹枃鏈�
- * @param row 琛屾暟鎹�
- */
-const getShippingStatusText = (row) => {
- // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀�"宸插彂璐�"
- if (row.shippingDate || row.shippingCarNumber) {
- return '宸插彂璐�';
- }
-
- // 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
- const status = row.shippingStatus;
-
- // 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓�"寰呭彂璐�"
- if (status === null || status === undefined || status === '') {
- return '寰呭彂璐�';
- }
-
- // 鐘舵�佹槸瀛楃涓�
- const statusStr = String(status).trim();
- const statusTextMap = {
- '寰呭彂璐�': '寰呭彂璐�',
- '寰呭鏍�': '寰呭鏍�',
- '瀹℃牳涓�': '瀹℃牳涓�',
- '瀹℃牳鎷掔粷': '瀹℃牳鎷掔粷',
- '瀹℃牳閫氳繃': '瀹℃牳閫氳繃',
- '宸插彂璐�': '宸插彂璐�'
- };
- return statusTextMap[statusStr] || '寰呭彂璐�';
-};
-
-/**
- * 鑾峰彇鍙戣揣鐘舵�佹爣绛剧被鍨嬶紙棰滆壊锛�
- * @param row 琛屾暟鎹�
- */
-const getShippingStatusType = (row) => {
- // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀虹豢鑹�
- if (row.shippingDate || row.shippingCarNumber) {
- return 'success';
- }
-
- // 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
- const status = row.shippingStatus;
-
- // 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓虹伆鑹诧紙寰呭彂璐э級
- if (status === null || status === undefined || status === '') {
- return 'info';
- }
-
- // 鐘舵�佹槸瀛楃涓�
- const statusStr = String(status).trim();
- const typeTextMap = {
- '寰呭彂璐�': 'info',
- '寰呭鏍�': 'info',
- '瀹℃牳涓�': 'warning',
- '瀹℃牳鎷掔粷': 'danger',
- '瀹℃牳閫氳繃': 'success',
- '宸插彂璐�': 'success'
- };
- return typeTextMap[statusStr] || 'info';
-};
-
-/**
- * 鍒ゆ柇鏄惁鍙互鍙戣揣
- * 鍙湁鍦ㄤ骇鍝佺姸鎬佹槸鍏呰冻锛屽彂璐х姸鎬佹槸寰呭彂璐у拰瀹℃牳鎷掔粷鐨勬椂鍊欐墠鍙互鍙戣揣
- * @param row 琛屾暟鎹�
- */
-const canShip = (row) => {
-
- // 浜у搧鐘舵�佸繀椤绘槸鍏呰冻锛坅pproveStatus === 1锛�
- if (row.approveStatus !== 1) {
- return false;
- }
-
- // 濡傛灉鍚庣杩斿洖浜嗗彴璐︾骇鍙戣揣鐘舵�侊紙deliveryStatus锛�
- // 1=宸插彂璐э紝鍒欑姝㈠啀娆″彂璐�
- const deliveryStatus = row.deliveryStatus;
- if (
- deliveryStatus !== null &&
- deliveryStatus !== undefined &&
- String(deliveryStatus).trim() !== ""
- ) {
- if (Number(deliveryStatus) === 1) return false;
- }
-
- // 鑾峰彇鍙戣揣鐘舵��
- const shippingStatus = row.shippingStatus;
-
- // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屼笉鑳藉啀娆″彂璐�
- if (row.shippingDate || row.shippingCarNumber) {
- return false;
- }
-
- // 鍙戣揣鐘舵�佸繀椤绘槸"寰呭彂璐�"鎴�"瀹℃牳鎷掔粷"
- const statusStr = shippingStatus ? String(shippingStatus).trim() : '';
- return statusStr === '寰呭彂璐�' || statusStr === '瀹℃牳鎷掔粷';
-};
-
-const handleBulkDelivery = async () => {
- if (selectedRows.value.length === 0) {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
-
- // 鍙厑璁搞�愭湭鍙戣揣/瀹℃壒澶辫触銆戣繘鍏ュ彂璐ф祦绋�
- const canDeliveryLedgers = selectedRows.value.filter((r) => {
- const status = Number(r.deliveryStatus);
- return status === 1 || status === 3;
- });
- if (canDeliveryLedgers.length === 0) {
- proxy.$modal.msgWarning("浠呮湭鍙戣揣鎴栧鎵瑰け璐ョ殑鍙拌处鍙互鍙戣揣");
- return;
- }
-
- // 宸插彂璐у彴璐︼細寮圭獥鎻愰啋锛屼笉鑳藉啀娆″彂璐э紙4 瑙嗕负宸插彂璐э級
- const shippedLedgers = selectedRows.value.filter((r) => Number(r.deliveryStatus) === 4);
- if (shippedLedgers.length === selectedRows.value.length) {
- try {
- await ElMessageBox.alert("鎵�閫夐攢鍞彴璐﹀潎宸插彂璐э紝涓嶈兘鍐嶆鍙戣揣銆�", "鎻愮ず", {
- type: "warning",
- confirmButtonText: "鐭ラ亾浜�",
- });
- } catch {
- /* 鍏抽棴寮圭獥 */
- }
- return;
- }
- if (shippedLedgers.length > 0) {
- try {
- await ElMessageBox.alert(
- "閫変腑鐨勯攢鍞彴璐︿腑鍖呭惈宸插彂璐ц褰曪紝宸插彂璐х殑涓嶈兘鍐嶆鍙戣揣锛岀郴缁熷皢浠呬负鏈彂璐у彴璐﹀鐞嗐��",
- "鎻愮ず",
- {
- type: "warning",
- confirmButtonText: "鐭ラ亾浜�",
- }
- );
- } catch {
- return;
- }
- }
-
- const customerNames = selectedRows.value.map((r) => String(r.customerName || "").trim());
- const uniqueCustomers = Array.from(new Set(customerNames));
-
- // 瀹㈡埛鍚嶇О涓嶄竴鑷翠笉鍏佽鍙戣揣
- if (uniqueCustomers.length > 1) {
- proxy.$modal.msgWarning("瀹㈡埛鍚嶇О涓嶄竴鑷达紝涓嶅厑璁稿彂璐�");
- return;
- }
-
- // 澶氭潯涓斿鎴蜂竴鑷达細浜屾纭
- if (selectedRows.value.length > 1) {
- try {
- await ElMessageBox.confirm("鏄惁纭鍚堝苟鍙戣揣锛�", "鍚堝苟鍙戣揣", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- });
- } catch (e) {
- proxy.$modal.msg("宸插彇娑�");
- return;
- }
- }
-
- proxy.$modal.loading("姝e湪鑾峰彇浜у搧鏁版嵁锛岃绋嶅��...");
- try {
- const targets = [];
- for (const ledger of selectedRows.value) {
-
- //濡傛灉宸茬粡鏄�滃鎵逛腑(2)鈥濇垨鈥滃凡鍙戣揣(4)鈥濓紝鍒欒烦杩囷紝涓嶅厑璁搁噸澶嶆搷浣�
- const status = Number(ledger.deliveryStatus);
- if (status === 2 || status === 4) {
- console.warn(`鍙拌处缂栧彿 ${ledger.salesContractNo} 鐘舵�佷负 ${status}锛岃烦杩囧彂璐);
- continue;
- }
-
- let products = [];
- try {
- const res = await productList({ salesLedgerId: ledger.id, type: 1 });
- products = res?.data || [];
- } catch (error) {
- products = [];
- console.error('璇锋眰鍙戠敓寮傚父', error);
- }
- for (const product of products) {
- if (!canShip(product)) continue;
- targets.push({
- ...product,
- salesLedgerId: product.salesLedgerId || ledger.id,
- });
- }
- }
- if (targets.length === 0) {
- proxy.$modal.msgWarning("娌℃湁鍙彂璐х殑鏁版嵁");
- return;
- }
-
- currentDeliveryRows.value = targets;
- deliveryForm.value = { type: "璐ц溅" };
- // 閲嶇疆瀹℃壒浜鸿妭鐐癸紙榛樿涓�涓┖鑺傜偣锛�
- approverNodes.value = [{ id: 1, userId: null }];
- nextApproverId = 2;
- deliveryFormVisible.value = true;
- } finally {
- proxy.$modal.closeLoading();
- }
-};
-
-/**
- * 涓嬭浇鏂囦欢
- *
- * @param row 涓嬭浇鏂囦欢鐨勭浉鍏充俊鎭璞�
- */
-const fileListRef = ref(null)
-const fileListDialogVisible = ref(false)
-const downLoadFile = (row) => {
- getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
- if (fileListRef.value) {
- fileListRef.value.open(res.salesLedgerFiles)
- }
- });
-}
-
-// 鎵撳紑鍙戣揣寮规锛堝崟鏉★級
-const openDeliveryForm = (row) => {
- // 鍙厑璁搞�愭湭鍙戣揣/瀹℃壒澶辫触銆戝彂璐э紱宸插彂璐�/瀹℃壒涓笉鍏佽
- const status = Number(row.deliveryStatus);
- if (status !== 1 && status !== 3) {
- proxy.$modal.msgWarning("鍙湁鍙戣揣鐘舵�佷负鏈彂璐ф垨瀹℃壒澶辫触鐨勮褰曟墠鍙互鍙戣揣");
- return;
- }
-
- currentDeliveryRows.value = [row];
- deliveryForm.value = {
- type: "璐ц溅",
+ const ensureProductRowDefaults = row => {
+ if (!row || typeof row !== "object") return;
+ if (!Array.isArray(row.salesProductProcessList))
+ row.salesProductProcessList = [];
+ if (
+ row.__otherAmountPopoverVisible === undefined ||
+ row.__otherAmountPopoverVisible === null
+ )
+ row.__otherAmountPopoverVisible = false;
+ if (
+ row.__inlineOtherAmountAdding === undefined ||
+ row.__inlineOtherAmountAdding === null
+ )
+ row.__inlineOtherAmountAdding = false;
+ if (row.__inlineOtherAmountAddId === undefined)
+ row.__inlineOtherAmountAddId = null;
+ if (row.width === undefined || row.width === null) row.width = 0;
+ if (row.height === undefined || row.height === null) row.height = 0;
+ if (row.perimeter === undefined || row.perimeter === null) row.perimeter = 0;
+ if (row.actualPieceArea === undefined || row.actualPieceArea === null)
+ row.actualPieceArea = 0;
+ if (row.actualTotalArea === undefined || row.actualTotalArea === null)
+ row.actualTotalArea = 0;
+ if (row.settlePieceArea === undefined || row.settlePieceArea === null)
+ row.settlePieceArea = 0;
+ if (row.settleTotalArea === undefined || row.settleTotalArea === null)
+ row.settleTotalArea = 0;
+ if (row.processRequirement === undefined || row.processRequirement === null)
+ row.processRequirement = "";
+ if (row.remark === undefined || row.remark === null) row.remark = "";
+ if (row.floorCode === undefined || row.floorCode === null) row.floorCode = "";
+ if (row.invoiceType === undefined || row.invoiceType === null)
+ row.invoiceType = "";
+ if (row.taxRate === undefined || row.taxRate === null) row.taxRate = "";
+ if (row.quantity === undefined || row.quantity === null) row.quantity = 0;
+ if (
+ row.taxInclusiveUnitPrice === undefined ||
+ row.taxInclusiveUnitPrice === null
+ )
+ row.taxInclusiveUnitPrice = 0;
+ if (
+ row.taxInclusiveTotalPrice === undefined ||
+ row.taxInclusiveTotalPrice === null
+ )
+ row.taxInclusiveTotalPrice = 0;
+ if (
+ row.taxExclusiveTotalPrice === undefined ||
+ row.taxExclusiveTotalPrice === null
+ )
+ row.taxExclusiveTotalPrice = 0;
};
- // 閲嶇疆瀹℃壒浜鸿妭鐐癸紙榛樿涓�涓┖鑺傜偣锛�
- approverNodes.value = [{ id: 1, userId: null }];
- nextApproverId = 2;
- deliveryFormVisible.value = true;
-};
-// 鎻愪氦鍙戣揣琛ㄥ崟
-const submitDelivery = () => {
- proxy.$refs["deliveryFormRef"].validate((valid) => {
- if (valid) {
- // 瀹℃壒浜哄繀濉牎楠岋紙鎵�鏈夎妭鐐归兘瑕侀�変汉锛�
- const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
- if (hasEmptyApprover) {
- proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
+ const stopOtherEditingRows = () => {
+ (productData.value || []).forEach(r => {
+ if (r && r.__editing) r.__editing = false;
+ });
+ editingProductRow.value = null;
+ };
+
+ const hasEditingProductRow = () => {
+ return (productData.value || []).some(r => r && r.__editing);
+ };
+
+ const addProductInline = async () => {
+ if (operationType.value === "view") return;
+ if (hasEditingProductRow()) {
+ proxy.$modal.msgWarning("璇峰厛淇濆瓨鎴栧彇娑堝綋鍓嶇紪杈戣");
+ return;
+ }
+ await getProductOptions();
+ await fetchOtherAmountSelectOptions(true);
+ const row = {
+ id: null,
+ __tempKey: `__temp_${Date.now()}_${Math.random().toString(16).slice(2)}`,
+ __editing: true,
+ __isNew: true,
+ __productCategoryId: null,
+ productCategory: "",
+ productModelId: null,
+ specificationModel: "",
+ thickness: null,
+ quantity: 0,
+ taxInclusiveUnitPrice: 0,
+ taxRate: "",
+ taxInclusiveTotalPrice: 0,
+ taxExclusiveTotalPrice: 0,
+ invoiceType: "",
+ width: 0,
+ height: 0,
+ perimeter: 0,
+ actualPieceArea: 0,
+ actualTotalArea: 0,
+ settlePieceArea: 0,
+ settleTotalArea: 0,
+ processRequirement: "",
+ remark: "",
+ salesProductProcessList: [],
+ processFlowConfigId: null,
+ floorCode: "",
+ heavyBox: "",
+ };
+ productData.value.push(row);
+ editingProductRow.value = row;
+ // 璁╃幇鏈夌殑璁$畻/鍏朵粬閲戦閫昏緫澶嶇敤褰撳墠琛�
+ productForm.value = row;
+ };
+
+ const editProductInline = async (row, index) => {
+ if (operationType.value === "view") return;
+ if (!row) return;
+ if (isProductShipped(row)) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+ return;
+ }
+ stopOtherEditingRows();
+ await getProductOptions();
+ await fetchOtherAmountSelectOptions(true);
+ ensureProductRowDefaults(row);
+ // 浜у搧澶х被 tree-select 鍥炴樉锛氬悕绉� -> id
+ row.__productCategoryId = findNodeIdByLabel(
+ productOptions.value,
+ row.productCategory
+ );
+
+ // 鍏煎鍚庣瀛楁鍛藉悕锛堜繚鎸佸師閫昏緫锛�
+ row.actualPieceArea = row?.actualPieceArea ?? row?.actual_piece_area ?? 0;
+ row.actualTotalArea = row?.actualTotalArea ?? row?.actual_total_area ?? 0;
+ row.settlePieceArea = row?.settlePieceArea ?? row?.settle_piece_area ?? 0;
+ row.settleTotalArea = row?.settleTotalArea ?? row?.settle_total_area ?? 0;
+ row.processRequirement =
+ row?.processRequirement ?? row?.process_requirement ?? "";
+ row.remark = row?.remark ?? row?.remarks ?? "";
+ row.floorCode = row?.floorCode ?? row?.floor_code ?? "";
+ row.processFlowConfigId =
+ row?.processFlowConfigId ?? row?.process_flow_config_id ?? null;
+ row.perimeter =
+ row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0;
+ row.thickness = row?.thickness;
+
+ row.salesProductProcessList = normalizeOtherAmountsFromRow(row);
+ mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
+ row.salesProductProcessList = fillOtherAmountProcessName(
+ row.salesProductProcessList
+ );
+
+ // 澶囦唤鐢ㄤ簬鍙栨秷
+ row.__backup = JSON.parse(JSON.stringify(row));
+ row.__editing = true;
+ editingProductRow.value = row;
+ productForm.value = row;
+
+ // 鏍规嵁浜у搧澶х被鍚嶇О鍙嶆煡 tree 鑺傜偣 id锛屽苟鍔犺浇瑙勬牸鍨嬪彿鍒楄〃
+ try {
+ const options =
+ productOptions.value && productOptions.value.length > 0
+ ? productOptions.value
+ : await getProductOptions();
+ const categoryId = findNodeIdByLabel(options, row.productCategory);
+ if (categoryId) {
+ const models = await modelList({ id: categoryId });
+ modelOptions.value = models || [];
+ const currentModel = (modelOptions.value || []).find(
+ m => m.model === row.specificationModel
+ );
+ if (currentModel) row.productModelId = currentModel.id;
+ }
+ } catch (e) {
+ console.error("鍔犺浇浜у搧瑙勬牸鍨嬪彿澶辫触", e);
+ }
+
+ // 鍚屾璁$畻涓�娆�
+ recalcPerimeterFromWidthHeight();
+ recalcAreaFromWidthHeight();
+ };
+
+ const validateInlineProductRow = row => {
+ if (!row) return false;
+ if (!row.productCategory) {
+ proxy.$modal.msgWarning("璇烽�夋嫨浜у搧澶х被");
+ return false;
+ }
+ if (!row.productModelId) {
+ proxy.$modal.msgWarning("璇烽�夋嫨瑙勬牸鍨嬪彿");
+ return false;
+ }
+ return true;
+ };
+
+ const saveProductInline = async (row, index) => {
+ if (operationType.value === "view") return;
+ if (!row) return;
+ if (isProductShipped(row)) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+ return;
+ }
+ // 纭繚 productForm 鎸囧悜褰撳墠琛岋紝浠ュ鐢ㄨ绠楅�昏緫
+ productForm.value = row;
+ ensureProductRowDefaults(row);
+
+ if (!validateInlineProductRow(row)) return;
+
+ // 鍘氬害绮惧害澶勭悊
+ if (
+ row.thickness !== null &&
+ row.thickness !== undefined &&
+ row.thickness !== ""
+ ) {
+ row.thickness = Number(Number(row.thickness).toFixed(15));
+ }
+
+ // 鎻愪氦鍓嶅厹搴曡绠椾竴娆★紙娌跨敤鍘熼�昏緫锛�
+ recalcAreaTotals();
+ // 鎻愪氦鍏滃簳锛氱◣鐜�/鏁伴噺鏈~鏃舵寜鏁板瓧 0 浼犻��
+ row.taxRate = Number(row.taxRate ?? 0) || 0;
+ row.quantity = Number(row.quantity ?? 0) || 0;
+
+ // 瑙勮寖鍖栧叾浠栭噾棰濇彁浜ょ粨鏋�
+ row.salesProductProcessList = (
+ Array.isArray(row.salesProductProcessList)
+ ? row.salesProductProcessList
+ : []
+ )
+ .map(it => ({
+ id: it?.id,
+ processName: it?.processName ?? "",
+ unitPrice: Number(it?.unitPrice ?? 0) || 0,
+ quantity: Number(it?.quantity ?? 0) || 0,
+ }))
+ .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
+
+ // 瑙勬牸鍨嬪彿锛氭牴鎹� productModelId 鍥炲~鍚嶇О
+ const model = (modelOptions.value || []).find(
+ m => String(m.id) === String(row.productModelId)
+ );
+ if (model?.model) row.specificationModel = model.model;
+
+ if (operationType.value === "edit") {
+ // 鍙拌处宸插瓨鍦細璧板師鎺ュ彛淇濆瓨鍒板悗绔紝鍐嶅洖鎷夊埛鏂�
+ const payload = { ...row, salesLedgerId: currentId.value, type: 1 };
+ delete payload.__backup;
+ delete payload.__editing;
+ delete payload.__isNew;
+ delete payload.__productCategoryId;
+ delete payload.__tempKey;
+ await addOrUpdateSalesLedgerProduct(payload);
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ await getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
+ res => {
+ productData.value = res.productData;
+ }
+ );
+ } else {
+ // 鏂板鍙拌处锛氫粎鍦ㄦ湰鍦� productData 鐢熸晥锛屾渶缁堥殢鍙拌处涓�璧锋彁浜�
+ row.__isNew = false;
+ row.__editing = false;
+ delete row.__backup;
+ }
+
+ stopOtherEditingRows();
+ };
+
+ const cancelProductInline = (row, index) => {
+ if (!row) return;
+ if (row.__isNew) {
+ productData.value.splice(index, 1);
+ } else if (row.__backup) {
+ const restored = JSON.parse(JSON.stringify(row.__backup));
+ // 淇濈暀 id 涓庣姸鎬佸瓧娈�
+ const keepId = row.id;
+ Object.keys(row).forEach(k => delete row[k]);
+ Object.assign(row, restored);
+ row.id = keepId;
+ row.__editing = false;
+ delete row.__backup;
+ }
+ stopOtherEditingRows();
+ };
+
+ const openOtherAmountInline = async row => {
+ if (!row) return;
+ if (operationType.value === "view") return;
+ if (isProductShipped(row)) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+ return;
+ }
+ ensureProductRowDefaults(row);
+ productForm.value = row;
+ otherAmountAddTargetRow.value = row;
+ await fetchOtherAmountSelectOptions(true);
+ mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
+ row.salesProductProcessList = fillOtherAmountProcessName(
+ row.salesProductProcessList
+ );
+ // 鍙仛鏁版嵁鍑嗗涓庢墦寮�娴眰锛堟柊澧炵敱娴眰鍐呮寜閽Е鍙戯級
+ row.__otherAmountPopoverVisible = true;
+ };
+
+ const keepOtherAmountPopoverOpenKey = ref(null);
+ const keepOtherAmountPopoverOpenUntil = ref(0);
+
+ const getOtherAmountRowKey = row => String(row?.__tempKey ?? row?.id ?? "");
+
+ const lockOtherAmountPopoverOpen = (row, durationMs = 1200) => {
+ const key = getOtherAmountRowKey(row);
+ if (!key) return;
+ keepOtherAmountPopoverOpenKey.value = key;
+ keepOtherAmountPopoverOpenUntil.value = Date.now() + durationMs;
+ };
+
+ const handleOtherAmountPopoverVisibleChange = (row, visible) => {
+ if (!row) return;
+ if (visible) {
+ row.__otherAmountPopoverVisible = true;
+ return;
+ }
+ if (row.__inlineOtherAmountAdding) {
+ row.__otherAmountPopoverVisible = true;
+ return;
+ }
+ const key = getOtherAmountRowKey(row);
+ const shouldKeepOpen = Boolean(
+ key &&
+ keepOtherAmountPopoverOpenKey.value === key &&
+ Date.now() < keepOtherAmountPopoverOpenUntil.value
+ );
+ row.__otherAmountPopoverVisible = shouldKeepOpen;
+ };
+
+ const startAddOtherAmountForRow = async row => {
+ if (!row) return;
+ if (operationType.value === "view") return;
+ if (isProductShipped(row)) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+ return;
+ }
+ ensureProductRowDefaults(row);
+ productForm.value = row;
+ await fetchOtherAmountSelectOptions(true);
+ mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
+ row.salesProductProcessList = fillOtherAmountProcessName(
+ row.salesProductProcessList
+ );
+ row.__inlineOtherAmountAddId = null;
+ row.__inlineOtherAmountAdding = true;
+ row.__otherAmountPopoverVisible = true;
+ };
+
+ const confirmAddOtherAmountForRow = row => {
+ if (!row) return;
+ ensureProductRowDefaults(row);
+ productForm.value = row;
+ const selectedId = row.__inlineOtherAmountAddId;
+ if (selectedId === null || selectedId === undefined || selectedId === "")
+ return;
+ const opt = otherAmountSelectOptions.value.find(
+ o => String(o.id) === String(selectedId)
+ );
+ if (!opt) return;
+ const exists = (row.salesProductProcessList ?? []).some(
+ it => String(it?.id) === String(opt.id)
+ );
+ if (exists) {
+ proxy.$modal.msgWarning("璇ュ叾浠栭噾棰濋」鐩凡娣诲姞");
+ return;
+ }
+ row.salesProductProcessList.push({
+ id: opt.id,
+ processName: opt.processName,
+ unitPrice: opt.unitPrice ?? 0,
+ quantity: 0,
+ });
+ row.__inlineOtherAmountAddId = null;
+ row.__inlineOtherAmountAdding = false;
+ row.__otherAmountPopoverVisible = true;
+ calculateFromUnitPrice(true);
+ };
+
+ const removeOtherAmountAtForRow = (row, index) => {
+ if (!row) return;
+ if (operationType.value === "view") return;
+ if (isProductShipped(row)) return;
+ productForm.value = row;
+ removeOtherAmountAt(index);
+ };
+
+ const handleOtherAmountQuantityChange = row => {
+ if (!row) return;
+ productForm.value = row;
+ calculateFromUnitPrice(true);
+ };
+
+ const handleInlineProductCategoryChange = async (row, val) => {
+ if (!row) return;
+ productForm.value = row;
+ // 澶嶇敤鍘熸湁閫昏緫锛氫細鍐欏叆 productCategory(鍚嶇О)銆侀噸缃鏍�/鍘氬害骞舵媺鍙栧瀷鍙�
+ await getModels(val);
+ // 琛屽唴缂栬緫鏃舵妸閫変腑鐨� id 璁板綍涓嬫潵锛屼究浜庡洖鏄�
+ row.__productCategoryId = val;
+ };
+
+ const handleInlineProductModelChange = (row, val) => {
+ if (!row) return;
+ productForm.value = row;
+ // 澶嶇敤鍘熸湁閫昏緫锛氫細鍐欏叆 specificationModel銆佸帤搴�
+ getProductModel(val);
+ };
+
+ const handleInlineSizeChange = row => {
+ if (!row) return;
+ productForm.value = row;
+ recalcPerimeterFromWidthHeight();
+ recalcAreaFromWidthHeight();
+ recalcAreaTotals();
+ };
+
+ const handleInlineUnitPriceChange = row => {
+ if (!row) return;
+ productForm.value = row;
+ calculateFromUnitPrice();
+ recalcAreaTotals();
+ };
+
+ const handleInlineQuantityChange = row => {
+ if (!row) return;
+ productForm.value = row;
+ calculateFromQuantity();
+ recalcAreaTotals();
+ };
+
+ const handleInlineTaxRateChange = row => {
+ if (!row) return;
+ productForm.value = row;
+ calculateFromTaxRate();
+ };
+
+ const handleInlineSettleAreaChange = row => {
+ if (!row) return;
+ productForm.value = row;
+ recalcAreaTotals();
+ calculateFromUnitPrice(true);
+ };
+ const upload = reactive({
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+ });
+ // 鎶ヤ环鍗曞鍏ョ浉鍏�
+ const quotationDialogVisible = ref(false);
+ const quotationLoading = ref(false);
+ const quotationList = ref([]);
+ const quotationSearchForm = reactive({
+ quotationNo: "",
+ customer: "",
+ });
+ // 鎶ヤ环鍗曞脊妗嗗垎椤�
+ const quotationPage = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+ const selectedQuotation = ref(null);
+
+ // 鍙戣揣鐩稿叧
+ const deliveryFormVisible = ref(false);
+ const currentDeliveryRows = ref([]);
+ const deliveryFormData = reactive({
+ deliveryForm: {
+ type: "璐ц溅", // 璐ц溅, 蹇��
+ },
+ deliveryRules: {
+ type: [{ required: true, message: "璇烽�夋嫨鍙戣揣绫诲瀷", trigger: "change" }],
+ },
+ });
+ const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
+
+ // 浜у搧寮规锛氬叾浠栭噾棰濆閫変笅鎷夛紙鍩轰簬鈥滃叾浠栭噾棰濈淮鎶も�濇煡璇㈡帴鍙o級
+ const otherAmountSelectOptions = ref([]); // [{id, processName}]
+ const otherAmountSelectOptionsLoading = ref(false);
+
+ const fetchOtherAmountSelectOptions = async (force = false) => {
+ if (!force && otherAmountSelectOptions.value.length > 0) return;
+ otherAmountSelectOptionsLoading.value = true;
+ try {
+ const params = {
+ current: 1,
+ // 涓嬫媺妗嗗敖閲忎竴娆℃�ф媺鍏紝閬垮厤澶氭鍒嗛〉褰卞搷閫夋嫨浣撻獙
+ size: 1000,
+ };
+ const res = await salesLedgerProductProcessList(params);
+ const records = res?.records ?? res?.data?.records ?? [];
+
+ otherAmountSelectOptions.value = records.map(item => ({
+ id: item.id,
+ processName: item.processName ?? "",
+ unitPrice: item.unitPrice ?? 0,
+ }));
+ } finally {
+ otherAmountSelectOptionsLoading.value = false;
+ }
+ };
+
+ const normalizeOtherAmountsFromRow = row => {
+ if (!row) return [];
+ const raw =
+ row.other_amounts ??
+ row.otherAmounts ??
+ row.otherAmountProjects ??
+ row.otherAmountList ??
+ row.salesProductProcessList ??
+ [];
+
+ if (!Array.isArray(raw)) return [];
+ if (raw.length === 0) return [];
+
+ // 鎯呭喌1锛氬悗绔洿鎺ヨ繑鍥� [{id, processName}...]
+ if (typeof raw[0] === "object") {
+ return raw
+ .map(it => {
+ const id = it?.id ?? it?.processId ?? it?.otherAmountId ?? null;
+ const quantity =
+ Number(
+ it?.quantity ??
+ it?.processQuantity ??
+ it?.otherQuantity ??
+ it?.process_quantity ??
+ it?.other_amount_quantity
+ ) || 0;
+ return {
+ id,
+ processName: it?.processName ?? "",
+ quantity,
+ };
+ })
+ .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
+ }
+
+ // 鎯呭喌2锛氬悗绔彧杩斿洖 ids: [1,2,3]
+ return raw
+ .map(id => ({
+ id,
+ processName: "",
+ quantity: 0,
+ }))
+ .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
+ };
+
+ const mergeOtherAmountOptionsBySelection = selected => {
+ const list = Array.isArray(selected) ? selected : [];
+ for (const s of list) {
+ const exists = otherAmountSelectOptions.value.some(
+ o => String(o.id) === String(s.id)
+ );
+ if (!exists) {
+ otherAmountSelectOptions.value.push({
+ id: s.id,
+ processName: s.processName ?? "",
+ });
+ }
+ }
+ };
+
+ const fillOtherAmountProcessName = selected => {
+ const list = Array.isArray(selected) ? selected : [];
+ return list.map(s => {
+ const opt = otherAmountSelectOptions.value.find(
+ o => String(o.id) === String(s.id)
+ );
+ return {
+ id: s.id,
+ processName: opt?.processName ?? s.processName ?? "",
+ unitPrice: opt?.unitPrice ?? s.unitPrice ?? 0,
+ quantity: Number(s.quantity ?? 0) || 0,
+ };
+ });
+ };
+
+ // 鈥滃叾浠栭噾棰濃�濆崱鐗囧竷灞�锛氬彧鏈変竴鏉℃椂鍗犳弧鏁磋锛�>=2鏃朵袱鍒楁帓甯�
+ const getOtherAmountCardFlexStyle = () => {
+ const list = productForm.value?.salesProductProcessList;
+ const len = Array.isArray(list) ? list.length : 0;
+ if (len === 1) {
+ return { flex: "0 0 100%", maxWidth: "100%", width: "100%" };
+ }
+ return {
+ flex: "0 0 calc(50% - 6px)",
+ maxWidth: "calc(50% - 6px)",
+ width: "calc(50% - 6px)",
+ };
+ };
+
+ // 鍏朵粬閲戦锛氱偣鍑烩�滄柊澧炩�濆悗鍦ㄥ脊绐楅噷閫夋嫨涓�涓」鐩�
+ const otherAmountAddDialogVisible = ref(false);
+ const otherAmountAddId = ref(null);
+ const otherAmountAddTargetRow = ref(null);
+ const otherAmountAddTargetRowKey = ref(null);
+
+ const startAddOtherAmount = () => {
+ if (operationType.value === "view") return;
+ otherAmountAddDialogVisible.value = true;
+ otherAmountAddId.value = null;
+ // 閫氬父 openProductForm 宸茬粡鎷夎繃 options锛岃繖閲屽厹搴�
+ if (otherAmountSelectOptions.value.length === 0) {
+ fetchOtherAmountSelectOptions(true);
+ }
+ };
+
+ const cancelAddOtherAmount = () => {
+ otherAmountAddDialogVisible.value = false;
+ otherAmountAddId.value = null;
+ otherAmountAddTargetRow.value = null;
+ otherAmountAddTargetRowKey.value = null;
+ keepOtherAmountPopoverOpenKey.value = null;
+ keepOtherAmountPopoverOpenUntil.value = 0;
+ };
+
+ const handleOtherAmountSelected = id => {
+ const selectedId = id ?? otherAmountAddId.value;
+ if (selectedId === null || selectedId === undefined || selectedId === "")
+ return;
+ const opt = otherAmountSelectOptions.value.find(
+ o => String(o.id) === String(selectedId)
+ );
+ if (!opt) return;
+
+ const exists = (productForm.value?.salesProductProcessList ?? []).some(
+ it => String(it?.id) === String(opt.id)
+ );
+ if (exists) {
+ proxy.$modal.msgWarning("璇ュ叾浠栭噾棰濋」鐩凡娣诲姞");
+ return;
+ }
+
+ productForm.value.salesProductProcessList.push({
+ id: opt.id,
+ processName: opt.processName,
+ unitPrice: opt.unitPrice ?? 0,
+ quantity: 0,
+ });
+ calculateFromUnitPrice(true);
+
+ // 閫夋嫨瀹屾垚鍚庡叧闂�滄柊澧炲叾浠栭噾棰濃�濆脊绐楋紝骞朵繚鎸佽鍐呪�滃叾浠栭噾棰濃�濆脊灞傚紑鍚紝渚夸簬鐩存帴濉啓鏁伴噺
+ otherAmountAddDialogVisible.value = false;
+ otherAmountAddId.value = null;
+ const reopenOtherAmountPopover = () => {
+ let targetRow = otherAmountAddTargetRow.value;
+ const rowKey = otherAmountAddTargetRowKey.value;
+ if (rowKey) {
+ const matchedRow = (productData.value || []).find(
+ it => String(it?.__tempKey ?? it?.id ?? "") === rowKey
+ );
+ if (matchedRow) targetRow = matchedRow;
+ }
+ if (targetRow && typeof targetRow === "object") {
+ lockOtherAmountPopoverOpen(targetRow, 1500);
+ targetRow.__otherAmountPopoverVisible = true;
+ }
+ };
+ nextTick(() => {
+ reopenOtherAmountPopover();
+ setTimeout(reopenOtherAmountPopover, 0);
+ setTimeout(reopenOtherAmountPopover, 80);
+ });
+ otherAmountAddTargetRow.value = null;
+ otherAmountAddTargetRowKey.value = null;
+ };
+
+ const confirmAddOtherAmount = () => {
+ handleOtherAmountSelected(otherAmountAddId.value);
+ };
+
+ const removeOtherAmountAt = index => {
+ if (operationType.value === "view") return;
+ if (!Array.isArray(productForm.value?.salesProductProcessList)) return;
+ productForm.value.salesProductProcessList.splice(index, 1);
+ calculateFromUnitPrice(true);
+ };
+
+ // 鍙戣揣瀹℃壒浜鸿妭鐐癸紙浠垮崗鍚屽鎵� infoFormDia.vue锛�
+ const approverNodes = ref([{ id: 1, userId: null }]);
+ let nextApproverId = 2;
+ const addApproverNode = () => {
+ approverNodes.value.push({ id: nextApproverId++, userId: null });
+ };
+ const removeApproverNode = index => {
+ approverNodes.value.splice(index, 1);
+ };
+
+ // 瀵煎叆鐩稿叧
+ const importUploadRef = ref(null);
+ const importUpload = reactive({
+ title: "瀵煎叆閿�鍞彴璐�",
+ open: false,
+ url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
+ headers: { Authorization: "Bearer " + getToken() },
+ isUploading: false,
+ beforeUpload: file => {
+ const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
+ const isLt10M = file.size / 1024 / 1024 < 10;
+ if (!isExcel) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢鍙兘鏄� xlsx/xls 鏍煎紡!");
+ return false;
+ }
+ if (!isLt10M) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!");
+ return false;
+ }
+ return true;
+ },
+ onChange: (file, fileList) => {
+ console.log("鏂囦欢鐘舵�佹敼鍙�", file, fileList);
+ },
+ onProgress: (event, file, fileList) => {
+ console.log("涓婁紶涓�...", event.percent);
+ },
+ onSuccess: (response, file, fileList) => {
+ console.log("涓婁紶鎴愬姛", response, file, fileList);
+ importUpload.isUploading = false;
+ if (response.code === 200) {
+ proxy.$modal.msgSuccess("瀵煎叆鎴愬姛");
+ importUpload.open = false;
+ if (importUploadRef.value) {
+ importUploadRef.value.clearFiles();
+ }
+ getList();
+ } else {
+ proxy.$modal.msgError(response.msg || "瀵煎叆澶辫触");
+ }
+ },
+ onError: (error, file, fileList) => {
+ console.error("涓婁紶澶辫触", error, file, fileList);
+ importUpload.isUploading = false;
+ proxy.$modal.msgError("瀵煎叆澶辫触锛岃閲嶈瘯");
+ },
+ });
+
+ const changeDaterange = value => {
+ if (value) {
+ searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+ searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+ } else {
+ searchForm.entryDateStart = undefined;
+ searchForm.entryDateEnd = undefined;
+ }
+ handleQuery();
+ };
+
+ // 鏌ヨ鍒楄〃
+ /** 鎼滅储鎸夐挳鎿嶄綔 */
+ const handleQuery = () => {
+ // 鍙湁鍦ㄧ偣鍑绘悳绱㈡寜閽椂鎵嶉噸缃〉鐮佸埌绗竴椤�
+ // 閬垮厤琛ㄥ崟瀛楁change浜嬩欢骞叉壈鍒嗛〉
+ if (arguments.length === 0) {
+ page.current = 1;
+ }
+ expandedRowKeys.value = [];
+ getList();
+ };
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+ const getList = () => {
+ tableLoading.value = true;
+ const { entryDate, ...rest } = searchForm;
+ // 灏嗚寖鍥存棩鏈熷瓧娈典紶閫掔粰鍚庣
+ const params = { ...rest, ...page };
+ // 绉婚櫎褰曞叆鏃ユ湡鐨勯粯璁ゅ�艰缃紝鍙繚鐣欒寖鍥存棩鏈熷瓧娈�
+ delete params.entryDate;
+ // 鏌ヨ瀹㈡埛鍚嶇О涓庢柊澧炰繚鎸佷竴鑷达細鍏堥�� customerId锛屽啀鏄犲皠涓� customerName 鏌ヨ
+ const selectedCustomer = (customerOption.value || []).find(
+ item => String(item?.id ?? "") === String(params.customerId ?? "")
+ );
+ if (selectedCustomer?.customerName) {
+ params.customerName = String(selectedCustomer.customerName).trim();
+ } else {
+ const cn =
+ params.customerName != null ? String(params.customerName).trim() : "";
+ if (cn) {
+ params.customerName = cn;
+ } else {
+ delete params.customerName;
+ }
+ }
+ delete params.customerId;
+ return ledgerListPage(params)
+ .then(res => {
+ tableLoading.value = false;
+ tableData.value = res.records;
+ tableData.value.map(item => {
+ item.children = [];
+ });
+ total.value = res.total;
+ return res;
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ // 鍏ュ簱锛堥攢鍞彴璐� -> 鍏ュ簱鐘舵�侊級
+ const handleSalesStock = async () => {
+ if (selectedRows.value.length !== 1) {
+ ElMessage.warning("璇峰嬀閫変竴鏉″彴璐︽暟鎹繘琛屽叆搴�");
+ return;
+ }
+ const row = selectedRows.value[0] || {};
+ const id = row?.id;
+ if (!id) {
+ ElMessage.warning("鎵�閫夋暟鎹己灏慽d锛屾棤娉曞叆搴�");
+ return;
+ }
+ if (Number(row.stockStatus) === 1) {
+ ElMessage.info("璇ュ彴璐﹀凡鍏ュ簱锛屾棤闇�閲嶅鎿嶄綔");
+ return;
+ }
+ try {
+ await ElMessageBox.confirm("纭瀵规墍閫夊彴璐︽墽琛屽叆搴擄紵", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ });
+ } catch {
+ return;
+ }
+ proxy?.$modal?.loading?.("姝e湪鍏ュ簱锛岃绋嶅��...");
+ try {
+ await salesStock({ id });
+ proxy?.$modal?.msgSuccess?.("鍏ュ簱鎴愬姛");
+ await getList();
+ } catch (e) {
+ proxy?.$modal?.msgError?.("鍏ュ簱澶辫触锛岃绋嶅悗閲嶈瘯");
+ } finally {
+ proxy?.$modal?.closeLoading?.();
+ }
+ };
+
+ // 鎵撳紑鈥滃伐鑹鸿矾绾块厤缃�濋�夋嫨寮圭獥锛堝繀椤绘樉寮忛�夋嫨锛�
+ const openProcessFlowSelect = async ledgerRow => {
+ if (!ledgerRow) return;
+ if (!ledgerRow.isEdit) return;
+
+ processFlowSelectLedgerRow.value = ledgerRow;
+ processFlowSelectDefaultRouteId.value = null;
+ processFlowSelectBoundRouteId.value = null;
+ processFlowSelectBoundRouteName.value = "";
+
+ try {
+ const res = await getSaleProcessBindInfo(ledgerRow.id);
+ const info = res?.data ?? res ?? {};
+ const boundId = info?.processRouteId ?? info?.routeId ?? info?.id ?? null;
+ const boundName =
+ info?.processRouteName ?? info?.routeName ?? info?.name ?? "";
+ processFlowSelectBoundRouteId.value = boundId;
+ processFlowSelectBoundRouteName.value = boundName;
+ processFlowSelectDefaultRouteId.value = boundId;
+ } catch (e) {
+ // 鏌ヨ澶辫触鏃舵寜鏈粦瀹氬鐞嗭紝涓嶉樆濉炲脊绐�
+ processFlowSelectBoundRouteId.value = null;
+ processFlowSelectBoundRouteName.value = "";
+ processFlowSelectDefaultRouteId.value = null;
+ }
+
+ processFlowSelectDialogVisible.value = true;
+ };
+
+ // 缁戝畾宸ヨ壓璺嚎鍒板綋鍓嶅彴璐︽暟鎹�
+ const handleProcessFlowSelectConfirm = async routeId => {
+ const ledgerRow = processFlowSelectLedgerRow.value;
+ if (!ledgerRow?.id) return;
+
+ const finalRouteId = routeId ?? null;
+ if (!finalRouteId) return;
+
+ const oldRouteId = processFlowSelectBoundRouteId.value;
+ if (
+ oldRouteId !== null &&
+ oldRouteId !== undefined &&
+ oldRouteId !== "" &&
+ String(oldRouteId) !== String(finalRouteId)
+ ) {
+ try {
+ await ElMessageBox.confirm(
+ "璇ヨ鍗曞凡缁戝畾宸ヨ壓璺嚎锛屾槸鍚︾‘瀹氭洿鎹紵",
+ "鎻愮ず",
+ {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }
+ );
+ } catch {
return;
}
- const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
- // 淇濆瓨褰撳墠灞曞紑鐨勮ID锛屼互渚垮彂璐у悗閲嶆柊鍔犺浇瀛愯〃鏍兼暟鎹�
- const currentExpandedKeys = [...expandedRowKeys.value];
+ }
- const targets = currentDeliveryRows.value || [];
- if (targets.length === 0) {
- proxy.$modal.msgWarning("鏈�夋嫨鍙彂璐х殑鏁版嵁");
- return;
+ proxy?.$modal?.loading?.("姝e湪缁戝畾宸ヨ壓璺嚎锛岃绋嶅��...");
+ try {
+ await saleProcessBind({
+ salesLedgerId: ledgerRow.id,
+ processRouteId: finalRouteId,
+ });
+
+ proxy?.$modal?.msgSuccess?.("宸ヨ壓璺嚎缁戝畾鎴愬姛");
+ processFlowSelectDialogVisible.value = false;
+ // 缁戝畾鍚庡埛鏂板垪琛紝纭繚鎿嶄綔鍒楀啀娆$偣鍑昏兘鍥炴樉缁戝畾
+ await getList();
+ } catch (e) {
+ proxy?.$modal?.msgError?.("缁戝畾澶辫触锛岃绋嶅悗閲嶈瘯");
+ } finally {
+ proxy?.$modal?.closeLoading?.();
+ }
+ };
+
+ // 鑾峰彇浜у搧澶х被tree鏁版嵁
+ const getProductOptions = () => {
+ // 杩斿洖 Promise锛屼究浜庡湪缂栬緫浜у搧鏃剁瓑寰呭姞杞藉畬鎴�
+ return productTreeList().then(res => {
+ productOptions.value = convertIdToValue(res);
+ return productOptions.value;
+ });
+ };
+ const formattedNumber = (row, column, cellValue) => {
+ return parseFloat(cellValue).toFixed(2);
+ };
+ // 鑾峰彇tree瀛愭暟鎹�
+ const getModels = value => {
+ // 浜у搧澶х被鍙樺寲鏃讹紝閲嶇疆瑙勬牸鍨嬪彿涓庡帤搴︼紝閬垮厤鏃у�兼畫鐣�
+ productForm.value.productModelId = null;
+ productForm.value.specificationModel = "";
+ productForm.value.thickness = null;
+
+ if (!value) {
+ productForm.value.productCategory = "";
+ modelOptions.value = [];
+ return;
+ }
+
+ productForm.value.productCategory = findNodeById(productOptions.value, value);
+ modelList({ id: value }).then(res => {
+ modelOptions.value = res || [];
+ });
+ };
+ const getProductModel = value => {
+ const index = modelOptions.value.findIndex(item => item.id === value);
+ if (index !== -1) {
+ productForm.value.specificationModel = modelOptions.value[index].model;
+ const selectedModel = modelOptions.value[index];
+ const modelThickness =
+ selectedModel?.thickness ??
+ selectedModel?.modelThickness ??
+ selectedModel?.thick ??
+ null;
+ productForm.value.thickness =
+ modelThickness === null ||
+ modelThickness === undefined ||
+ modelThickness === ""
+ ? null
+ : Number(modelThickness);
+ } else {
+ productForm.value.specificationModel = null;
+ productForm.value.thickness = null;
+ }
+ };
+ const filterProductCategoryNode = (value, data) => {
+ if (!value) return true;
+ return String(data?.label || "")
+ .toLowerCase()
+ .includes(String(value).toLowerCase());
+ };
+ const findNodeById = (nodes, productId) => {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].value === productId) {
+ return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣
+ }
+ if (nodes[i].children && nodes[i].children.length > 0) {
+ const foundNode = findNodeById(nodes[i].children, productId);
+ if (foundNode) {
+ return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖璇ヨ妭鐐�
+ }
+ }
+ }
+ return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+ };
+ function convertIdToValue(data, level = 0) {
+ return data.map(item => {
+ const { id, children, ...rest } = item;
+ const hasChildren = Array.isArray(children) && children.length > 0;
+ const newItem = {
+ ...rest,
+ value: id, // 灏� id 鏀逛负 value
+ // 浠呭厑璁稿彾瀛愯妭鐐硅閫夋嫨锛堟湁瀛愯妭鐐圭殑鍒嗙被鑺傜偣缁熶竴绂佺敤锛�
+ disabled: Boolean(rest?.disabled) || hasChildren,
+ };
+ if (hasChildren) {
+ newItem.children = convertIdToValue(children, level + 1);
}
- // 鎸夊彴璐︾淮搴﹀幓閲嶏紝姣忎釜 salesLedgerId 鍙皟鐢ㄤ竴娆″彂璐ф帴鍙�
- const uniqueLedgerIds = [...new Set(targets.map((item) => item.salesLedgerId).filter(Boolean))];
+ return newItem;
+ });
+ }
+ // 鏍规嵁鍚嶇О鍙嶆煡浜у搧澶х被 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
+ function 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 handleSelectionChange = selection => {
+ // 杩囨护鎺夊瓙鏁版嵁
+ selectedRows.value = selection.filter(item => item.children !== undefined);
+ console.log("selection", selectedRows.value);
+ };
+ const productSelected = selectedRows => {
+ productSelectedRows.value = selectedRows;
+ };
+ const expandedRowKeys = ref([]);
+ // 灞曞紑琛�
+ const expandChange = (row, expandedRows) => {
+ if (expandedRows.length > 0) {
+ expandedRowKeys.value = [];
+ try {
+ productList({ salesLedgerId: row.id, type: 1 }).then(res => {
+ const index = tableData.value.findIndex(item => item.id === row.id);
+ if (index > -1) {
+ tableData.value[index].children = res.data;
+ }
+ expandedRowKeys.value.push(row.id);
+ });
+ } catch (error) {
+ console.log(error);
+ }
+ } else {
+ expandedRowKeys.value = [];
+ }
+ };
- const run = async () => {
- for (const salesLedgerId of uniqueLedgerIds) {
- await addShippingInfo({
- salesLedgerId,
- type: deliveryForm.value.type,
- approveUserIds,
- });
- }
- };
+ // 娣诲姞琛ㄨ绫诲悕鏂规硶
+ const tableRowClassName = ({ row }) => {
+ if (!row.deliveryDate) return "";
+ if (row.isFh) return "";
- run()
+ const diff = row.deliveryDaysDiff;
+ if (diff === 15) {
+ return "yellow";
+ } else if (diff === 10) {
+ return "pink";
+ } else if (diff === 2) {
+ return "purple";
+ } else if (diff < 2) {
+ return "red";
+ }
+ };
+ // 涓昏〃鍚堣鏂规硶
+ const summarizeMainTable = param => {
+ return proxy.summarizeTable(param, [
+ "contractAmount",
+ "taxInclusiveTotalPrice",
+ "taxExclusiveTotalPrice",
+ ]);
+ };
+ // 瀛愯〃鍚堣鏂规硶
+ const summarizeChildrenTable = param => {
+ return proxy.summarizeTable(param, [
+ "taxInclusiveUnitPrice",
+ "taxInclusiveTotalPrice",
+ "taxExclusiveTotalPrice",
+ ]);
+ };
+ // 鎵撳紑寮规
+ const openForm = async (type, row) => {
+ operationType.value = type;
+ form.value = {};
+ productData.value = [];
+ selectedQuotation.value = null;
+ let userLists = await userListNoPage();
+ userList.value = userLists.data;
+ customerList().then(res => {
+ customerOption.value = res;
+ });
+ form.value.entryPerson = userStore.id;
+ if (type === "add") {
+ // 鏂板鏃惰缃綍鍏ユ棩鏈熶负褰撳ぉ
+ form.value.entryDate = getCurrentDate();
+ // 绛捐鏃ユ湡榛樿涓哄綋澶�
+ form.value.executionDate = getCurrentDate();
+ form.value.customerRemarks = "";
+ } else {
+ currentId.value = row.id;
+ getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
+ form.value = { ...res };
+ form.value.entryPerson = Number(res.entryPerson);
+ // 瀛楁鍚嶅吋瀹癸細鍚庣鍙兘杩斿洖 customer_remarks
+ form.value.customerRemarks =
+ res?.customerRemarks ?? res?.customer_remarks ?? "";
+ productData.value = form.value.productData;
+ fileList.value = form.value.salesLedgerFiles;
+ });
+ }
+ // let userAll = await userStore.getInfo()
+ // userList.value.forEach(element => {
+ // if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
+ // form.value.entryPerson = userAll.user.userId // 璁剧疆榛樿涓氬姟鍛樹负褰撳墠鐢ㄦ埛
+ // }
+ // });
+ form.value.entryDate = getCurrentDate(); // 璁剧疆榛樿褰曞叆鏃ユ湡涓哄綋鍓嶆棩鏈�
+ if (type === "add") {
+ form.value.deliveryDate = dayjs(form.value.entryDate)
+ .add(7, "day")
+ .format("YYYY-MM-DD");
+ }
+ dialogFormVisible.value = true;
+ };
+
+ // 鎵撳紑鎶ヤ环鍗曢�夋嫨寮圭獥锛堜粎瀹℃壒閫氳繃锛�
+ const openQuotationDialog = async () => {
+ if (operationType.value === "view") return;
+ quotationDialogVisible.value = true;
+ // 鎵撳紑寮圭獥鏃堕噸缃垎椤靛埌绗竴椤�
+ quotationPage.current = 1;
+ // 鍏堢‘淇濆鎴峰垪琛ㄥ凡鍔犺浇锛屼究浜庡悗缁洖濉� customerId
+ if (!customerOption.value || customerOption.value.length === 0) {
+ try {
+ const res = await customerList();
+ customerOption.value = res;
+ } catch (e) {
+ // ignore锛屽厑璁哥敤鎴峰悗缁墜鍔ㄩ�夋嫨瀹㈡埛
+ }
+ }
+ await fetchQuotationList();
+ };
+
+ const fetchQuotationList = async () => {
+ quotationLoading.value = true;
+ try {
+ const params = {
+ // 鍚庣鍒嗛〉瀛楁锛歝urrent / size
+ current: quotationPage.current,
+ size: quotationPage.size,
+ ...quotationSearchForm,
+ status: "閫氳繃",
+ };
+ const res = await getQuotationList(params);
+ quotationList.value = res?.data?.records || [];
+ quotationPage.total = res?.data?.total || 0;
+ } finally {
+ quotationLoading.value = false;
+ }
+ };
+
+ const resetQuotationSearch = async () => {
+ quotationSearchForm.quotationNo = "";
+ quotationSearchForm.customer = "";
+ quotationPage.current = 1;
+ await fetchQuotationList();
+ };
+
+ // 鎶ヤ环鍗曞脊妗嗗垎椤靛垏鎹�
+ const quotationPaginationChange = obj => {
+ quotationPage.current = obj.page;
+ quotationPage.size = obj.limit;
+ fetchQuotationList();
+ };
+
+ // 閫変腑鎶ヤ环鍗曞悗鍥炲~鍒板彴璐﹁〃鍗�
+ const applyQuotation = row => {
+ if (!row) return;
+ selectedQuotation.value = row;
+
+ // 涓氬姟鍛�
+ form.value.salesman = (row.salesperson || "").trim();
+
+ // 瀹㈡埛鍚嶇О -> customerId
+ const qCustomerName = String(row.customer || "").trim();
+ const customer = (customerOption.value || []).find(c => {
+ const name = String(c.customerName || "").trim();
+ return (
+ name === qCustomerName ||
+ name.includes(qCustomerName) ||
+ qCustomerName.includes(name)
+ );
+ });
+ if (customer?.id) {
+ form.value.customerId = customer.id;
+ } else {
+ // 濡傛灉鎵句笉鍒帮紝淇濈暀鍘熷�硷紙鍏佽鐢ㄦ埛鎵嬪姩閫夋嫨/涓嶆墦鏂凡鏈夎緭鍏ワ級
+ form.value.customerId = form.value.customerId || "";
+ }
+
+ // 浜у搧淇℃伅鏄犲皠锛氭姤浠� products -> 鍙拌处 productData
+ const products = Array.isArray(row.products) ? row.products : [];
+ productData.value = products.map(p => {
+ const quantity = Number(p.quantity ?? 0) || 0;
+ const unitPrice = Number(p.unitPrice ?? 0) || 0;
+ const settlePieceArea = Number(p.settlePieceArea ?? 0) || 1;
+ const taxRate = "13"; // 榛樿 13%锛屼究浜庣洿鎺ユ彁浜わ紙濡傞渶鍙湪浜у搧涓嚜琛屼慨鏀癸級
+ const taxInclusiveTotalPrice = (
+ unitPrice *
+ settlePieceArea *
+ quantity
+ ).toFixed(2);
+ const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
+ taxInclusiveTotalPrice,
+ taxRate
+ );
+ return {
+ // 鍙拌处瀛楁
+ productCategory: p.product || p.productName || "",
+ specificationModel: p.specification || "",
+ thickness: p.thickness,
+ quantity: quantity,
+ taxRate: taxRate,
+ taxInclusiveUnitPrice: unitPrice.toFixed(2),
+ taxInclusiveTotalPrice: taxInclusiveTotalPrice,
+ taxExclusiveTotalPrice: taxExclusiveTotalPrice,
+ invoiceType: "澧炴櫘绁�",
+ // 鏂板锛氶粯璁ゅ�硷紙閬垮厤鍚庣画鎻愪氦鏃跺瓧娈电己澶憋級
+ width: 0,
+ height: 0,
+ actualPieceArea: 0,
+ actualTotalArea: 0,
+ settlePieceArea: 0,
+ settleTotalArea: 0,
+ processRequirement: "",
+ floorCode: "",
+ remark: "",
+ salesProductProcessList: [],
+ };
+ });
+
+ quotationDialogVisible.value = false;
+ };
+ function changs(val) {
+ console.log(val);
+ }
+ // 涓婁紶鍓嶆牎妫�
+ function handleBeforeUpload(file) {
+ // 鏍℃鏂囦欢澶у皬
+ // if (file.size > 1024 * 1024 * 10) {
+ // proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
+ // return false;
+ // }
+ proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+ return true;
+ }
+ // 涓婁紶澶辫触
+ function handleUploadError(err) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+ proxy.$modal.closeLoading();
+ }
+ // 涓婁紶鎴愬姛鍥炶皟
+ function handleUploadSuccess(res, file, uploadFiles) {
+ proxy.$modal.closeLoading();
+ if (res.code === 200) {
+ file.tempId = res.data.tempId;
+ proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+ } else {
+ proxy.$modal.msgError(res.msg);
+ proxy.$refs.fileUpload.handleRemove(file);
+ }
+ }
+ // 绉婚櫎鏂囦欢
+ function handleRemove(file) {
+ if (operationType.value === "edit") {
+ let ids = [];
+ ids.push(file.id);
+ delLedgerFile(ids).then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ });
+ }
+ }
+ // 鎻愪氦琛ㄥ崟
+ const submitForm = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ console.log("productData.value--", productData.value);
+ // 琛屽唴缂栬緫鏈繚瀛樻椂涓嶅厑璁告彁浜わ紝閬垮厤鑴忔暟鎹�/涓存椂瀛楁杩涘叆鍚庣
+ const hasEditingRow = (productData.value || []).some(
+ r => r && r.__editing
+ );
+ if (hasEditingRow) {
+ proxy.$modal.msgWarning("浜у搧淇℃伅瀛樺湪鏈繚瀛樼殑缂栬緫琛岋紝璇峰厛淇濆瓨鎴栧彇娑�");
+ return;
+ }
+ if (productData.value !== null && productData.value.length > 0) {
+ const cleanedProducts = (productData.value || []).map(p => {
+ if (!p || typeof p !== "object") return p;
+ const {
+ __editing,
+ __isNew,
+ __backup,
+ __productCategoryId,
+ __tempKey,
+ __otherAmountPopoverVisible,
+ ...rest
+ } = p;
+ rest.taxRate = Number(rest.taxRate ?? 0) || 0;
+ rest.quantity = Number(rest.quantity ?? 0) || 0;
+ return rest;
+ });
+ form.value.productData = proxy.HaveJson(cleanedProducts);
+ } else {
+ proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
+ return;
+ }
+ let tempFileIds = [];
+ if (fileList.value !== null && fileList.value.length > 0) {
+ tempFileIds = fileList.value.map(item => item.tempId);
+ }
+ form.value.tempFileIds = tempFileIds;
+ form.value.type = 1;
+ const submitPayload = { ...form.value };
+ delete submitPayload.paymentMethod;
+ addOrUpdateSalesLedger(submitPayload).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ getList();
+ });
+ }
+ });
+ };
+ // 鍏抽棴寮规
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ };
+
+ const productIndex = ref(0);
+ // 鎵撳紑浜у搧寮规
+ const openProductForm = async (type, row, index) => {
+ // 缂栬緫鏃舵鏌ヤ骇鍝佹槸鍚﹀凡鍙戣揣鎴栧鏍搁�氳繃
+ if (type === "edit" && isProductShipped(row)) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+ return;
+ }
+
+ productOperationType.value = type;
+ productForm.value = {};
+ proxy.resetForm("productFormRef");
+ // 纭繚澶氶�夐」榛樿鏄暟缁勶紝閬垮厤 el-select multiple 鎶ラ敊
+ productForm.value.salesProductProcessList = [];
+ otherAmountAddDialogVisible.value = false;
+ otherAmountAddId.value = null;
+ if (type === "edit") {
+ productForm.value = { ...row };
+
+ // 瀛楁鍛藉悕鍏煎锛氫紭鍏堥┘宄帮紙濡� actualPieceArea锛夛紝鍏煎鍚庣鍙兘杩斿洖涓嬪垝绾匡紙濡� actual_piece_area锛�
+ productForm.value.actualPieceArea =
+ row?.actualPieceArea ?? row?.actual_piece_area ?? 0;
+ productForm.value.actualTotalArea =
+ row?.actualTotalArea ?? row?.actual_total_area ?? 0;
+ productForm.value.settlePieceArea =
+ row?.settlePieceArea ?? row?.settle_piece_area ?? 0;
+ productForm.value.settleTotalArea =
+ row?.settleTotalArea ?? row?.settle_total_area ?? 0;
+
+ // 鍔犲伐瑕佹眰/澶囨敞鍏煎锛氬悗绔彲鑳戒娇鐢ㄥ叾瀹冨懡鍚�
+ productForm.value.processRequirement =
+ row?.processRequirement ?? row?.process_requirement ?? "";
+ productForm.value.remark = row?.remark ?? row?.remarks ?? "";
+ productForm.value.floorCode = row?.floorCode ?? row?.floor_code ?? "";
+ // 宸ヨ壓娴佺▼閰嶇疆缁戝畾瀛楁锛堝悗缁敱鍚庣纭瀛楁鍚嶏級
+ productForm.value.processFlowConfigId =
+ row?.processFlowConfigId ?? row?.process_flow_config_id ?? null;
+
+ // 鍛ㄩ暱鍥炴樉锛堝鍚庣杩斿洖锛涙渶缁堜粛浠ュ叕寮忚绠椾负鍑嗭級
+ productForm.value.perimeter =
+ row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0;
+
+ // 鍚庣鐩存帴杩斿洖 thickness
+ productForm.value.thickness = row?.thickness;
+
+ productForm.value.salesProductProcessList =
+ normalizeOtherAmountsFromRow(row);
+ productIndex.value = index;
+ // 缂栬緫鏃舵牴鎹骇鍝佸ぇ绫诲悕绉板弽鏌� tree 鑺傜偣 id锛屽苟鍔犺浇瑙勬牸鍨嬪彿鍒楄〃
+ try {
+ const options =
+ productOptions.value && productOptions.value.length > 0
+ ? productOptions.value
+ : await getProductOptions();
+ const categoryId = findNodeIdByLabel(
+ options,
+ productForm.value.productCategory
+ );
+ if (categoryId) {
+ const models = await modelList({ id: categoryId });
+ modelOptions.value = models || [];
+ // 鏍规嵁褰撳墠瑙勬牸鍨嬪彿鍚嶇О鍙嶆煡骞惰缃� productModelId锛屼究浜庝笅鎷夋鏄剧ず宸查�夊��
+ const currentModel = (modelOptions.value || []).find(
+ m => m.model === productForm.value.specificationModel
+ );
+ if (currentModel) {
+ productForm.value.productModelId = currentModel.id;
+ }
+ }
+ } catch (e) {
+ // 鍔犺浇澶辫触鏃朵繚鎸佸彲缂栬緫锛屼笉涓柇寮圭獥
+ console.error("鍔犺浇浜у搧瑙勬牸鍨嬪彿澶辫触", e);
+ }
+
+ // 鏍规嵁褰撳墠瀹介珮閲嶆柊璁$畻鍛ㄩ暱涓庨潰绉�
+ recalcPerimeterFromWidthHeight();
+ recalcAreaFromWidthHeight();
+
+ // 鍥炴樉鈥滃叾浠栭噾棰濃�濆閫夛細鍏堟媺鍙栦笅鎷夐�夐」锛屽啀琛ラ綈 processName
+ await fetchOtherAmountSelectOptions(true);
+ mergeOtherAmountOptionsBySelection(
+ productForm.value.salesProductProcessList
+ );
+ productForm.value.salesProductProcessList = fillOtherAmountProcessName(
+ productForm.value.salesProductProcessList
+ );
+ } else {
+ getProductOptions();
+ // 鏂板鏃朵笅鎷夐�夐」鍔犺浇涓�娆″嵆鍙�
+ fetchOtherAmountSelectOptions(true);
+ }
+ productFormVisible.value = true;
+ };
+ // 鎻愪氦浜у搧琛ㄥ崟
+ const submitProduct = () => {
+ proxy.$refs["productFormRef"].validate(valid => {
+ if (valid) {
+ // 鍘氬害淇濈暀 15 浣嶅皬鏁帮紝閬垮厤鐢变簬娴偣璁$畻/杈撳叆瀵艰嚧绮惧害鍋忓樊
+ if (
+ productForm.value.thickness !== null &&
+ productForm.value.thickness !== undefined
+ ) {
+ productForm.value.thickness = Number(
+ Number(productForm.value.thickness).toFixed(15)
+ );
+ }
+
+ // 闈㈢Н/鎬昏瀛楁鍦ㄦ彁浜ゅ墠鍏滃簳璁$畻涓�娆�
+ recalcAreaTotals();
+ // 鎻愪氦鍏滃簳锛氱◣鐜�/鏁伴噺鏈~鏃舵寜鏁板瓧 0 浼犻��
+ productForm.value.taxRate = Number(productForm.value.taxRate ?? 0) || 0;
+ productForm.value.quantity = Number(productForm.value.quantity ?? 0) || 0;
+ // 鍏朵粬閲戦鍙彁浜� {id, processName, quantity}锛堝悗绔瓧娈碉細salesProductProcessList锛�
+ productForm.value.salesProductProcessList = (
+ Array.isArray(productForm.value.salesProductProcessList)
+ ? productForm.value.salesProductProcessList
+ : []
+ )
+ .map(it => ({
+ id: it?.id,
+ processName: it?.processName ?? "",
+ unitPrice: Number(it?.unitPrice ?? 0) || 0,
+ quantity: Number(it?.quantity ?? 0) || 0,
+ }))
+ .filter(it => it.id !== null && it.id !== undefined && it.id !== "");
+
+ if (operationType.value === "edit") {
+ submitProductEdit();
+ } else {
+ if (productOperationType.value === "add") {
+ productData.value.push({ ...productForm.value });
+ } else {
+ productData.value[productIndex.value] = { ...productForm.value };
+ }
+ closeProductDia();
+ }
+ }
+ });
+ };
+ const submitProductEdit = () => {
+ productForm.value.salesLedgerId = currentId.value;
+ productForm.value.type = 1;
+ addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeProductDia();
+ getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(res => {
+ productData.value = res.productData;
+ });
+ });
+ };
+ // 鍒犻櫎浜у搧
+ const deleteProduct = () => {
+ if (productSelectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+
+ // 妫�鏌ユ槸鍚︽湁宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝�
+ const shippedProducts = productSelectedRows.value.filter(row =>
+ isProductShipped(row)
+ );
+ if (shippedProducts.length > 0) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳藉垹闄�");
+ return;
+ }
+
+ if (operationType.value === "add") {
+ productSelectedRows.value.forEach(selectedRow => {
+ const index = productData.value.findIndex(product => {
+ if (!product || !selectedRow) return false;
+ // 鏂板琛� id 涓虹┖鏃讹紝鐢ㄤ复鏃� key 瀹氫綅
+ if (product.id != null && selectedRow.id != null) {
+ return String(product.id) === String(selectedRow.id);
+ }
+ return (
+ product.__tempKey &&
+ selectedRow.__tempKey &&
+ String(product.__tempKey) === String(selectedRow.__tempKey)
+ );
+ });
+ if (index !== -1) {
+ productData.value.splice(index, 1);
+ }
+ });
+ } else {
+ let ids = [];
+ if (productSelectedRows.value.length > 0) {
+ ids = productSelectedRows.value.map(item => item.id);
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
.then(() => {
- proxy.$modal.msgSuccess("鍙戣揣鎴愬姛");
- closeDeliveryDia();
- // 鍒锋柊涓昏〃鏁版嵁
- getList().then(() => {
- // 濡傛灉涔嬪墠鏈夊睍寮�鐨勮锛岄噸鏂板姞杞借繖浜涜鐨勫瓙琛ㄦ牸鏁版嵁
- if (currentExpandedKeys.length > 0) {
- const loadPromises = currentExpandedKeys.map((ledgerId) => {
- return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => {
- const index = tableData.value.findIndex((item) => item.id === ledgerId);
- if (index > -1) {
- tableData.value[index].children = res.data;
- }
- });
- });
- Promise.all(loadPromises).then(() => {
- expandedRowKeys.value = currentExpandedKeys;
- });
- }
+ delProduct(ids).then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ closeProductDia();
+ getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
+ res => {
+ productData.value = res.productData;
+ }
+ );
});
})
.catch(() => {
- proxy.$modal.msgError("鍙戣揣澶辫触锛岃绋嶅悗閲嶈瘯");
+ proxy.$modal.msg("宸插彇娑�");
});
}
- });
-};
+ };
+ // 鍏抽棴浜у搧寮规
+ const closeProductDia = () => {
+ proxy.resetForm("productFormRef");
+ productFormVisible.value = false;
+ otherAmountAddDialogVisible.value = false;
+ otherAmountAddId.value = null;
+ };
+ // 瀵煎叆
+ const handleImport = () => {
+ importUpload.title = "瀵煎叆閿�鍞彴璐�";
+ importUpload.open = true;
+ if (importUploadRef.value) {
+ importUploadRef.value.clearFiles();
+ }
+ };
-// 鍏抽棴鍙戣揣寮规
-const closeDeliveryDia = () => {
- proxy.resetForm("deliveryFormRef");
- deliveryFormVisible.value = false;
- currentDeliveryRows.value = [];
-};
-const currentFactoryName = ref("");
-const getCurrentFactoryName = async () => {
- let res = await userStore.getInfo();
- currentFactoryName.value = res.user.currentFactoryName;
-};
-onMounted(() => {
- getList();
- customerList().then((res) => {
- customerOption.value = res;
- });
- userListNoPage().then(res => {
- userList.value = res.data;
- })
- getCurrentFactoryName();
-});
+ // 涓嬭浇瀵煎叆妯℃澘
+ const downloadTemplate = () => {
+ proxy.download("/sales/ledger/exportTemplate", {}, "閿�鍞彴璐﹀鍏ユā鏉�.xlsx");
+ };
+
+ // 鎻愪氦瀵煎叆鏂囦欢
+ const submitImportFile = () => {
+ importUpload.isUploading = true;
+ proxy.$refs["importUploadRef"].submit();
+ };
+
+ // 瀵煎嚭
+ const handleOut = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/sales/ledger/export", {}, "閿�鍞彴璐�.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+ /** 鍒ゆ柇鍗曚釜浜у搧鏄惁宸插彂璐э紙鏍规嵁shippingStatus鍒ゆ柇锛屽凡鍙戣揣鎴栧鏍搁�氳繃涓嶅彲缂栬緫鍜屽垹闄わ級 */
+ const isProductShipped = product => {
+ if (!product) return false;
+ const status = String(product.shippingStatus || "").trim();
+ // 濡傛灉鍙戣揣鐘舵�佹槸"宸插彂璐�"鎴�"瀹℃牳閫氳繃"锛屽垯涓嶅彲缂栬緫鍜屽垹闄�
+ return status === "宸插彂璐�" || status === "瀹℃牳閫氳繃";
+ };
+
+ /** 鍒ゆ柇閿�鍞鍗曚笅鏄惁瀛樺湪宸插彂璐�/鍙戣揣瀹屾垚鐨勪骇鍝侊紙涓嶅彲鍒犻櫎锛� */
+ const hasShippedProducts = products => {
+ if (!products || !products.length) return false;
+ return products.some(p => {
+ const status = String(p.shippingStatus || "").trim();
+ // 鏈夊彂璐ф棩鏈熸垨杞︾墝鍙疯涓哄凡鍙戣揣
+ if (p.shippingDate || p.shippingCarNumber) return true;
+ // 宸茶繘琛屽彂璐с�佸彂璐у畬鎴愩�佸凡鍙戣揣 鍧囦笉鍙垹闄�
+ return (
+ status === "宸茶繘琛屽彂璐�" || status === "鍙戣揣瀹屾垚" || status === "宸插彂璐�"
+ );
+ });
+ };
+
+ // 鍒犻櫎
+ const handleDelete = async () => {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ const ids = selectedRows.value.map(item => item.id);
+
+ // 妫�鏌ユ槸鍚︽湁宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曪紝鑻ユ湁鍒欎笉鍏佽鍒犻櫎
+ const cannotDeleteNames = [];
+ for (const row of selectedRows.value) {
+ let products =
+ row.children && row.children.length > 0 ? row.children : null;
+ if (!products) {
+ try {
+ const res = await productList({ salesLedgerId: row.id, type: 1 });
+ products = res.data || [];
+ } catch {
+ products = [];
+ }
+ }
+ if (hasShippedProducts(products)) {
+ cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
+ }
+ }
+ if (cannotDeleteNames.length > 0) {
+ proxy.$modal.msgWarning(
+ "宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曚笉鑳藉垹闄わ細" + cannotDeleteNames.join("銆�")
+ );
+ return;
+ }
+
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ delLedger(ids).then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ const handlePrintCommand = async command => {
+ if (
+ command !== "finishedProcessCard" &&
+ command !== "salesOrder" &&
+ command !== "salesDeliveryNote"
+ )
+ return;
+ if (command === "salesDeliveryNote") {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇疯嚦灏戦�夋嫨涓�鏉¢攢鍞彴璐︽暟鎹繘琛屾墦鍗�");
+ return;
+ }
+ const customerNames = Array.from(
+ new Set(
+ selectedRows.value.map(item => String(item?.customerName ?? "").trim())
+ )
+ );
+ if (customerNames.length > 1) {
+ proxy.$modal.msgWarning("浠呮敮鎸佺浉鍚屽鎴峰悕绉扮殑閿�鍞彴璐﹀悎骞跺彂璐ф墦鍗�");
+ return;
+ }
+ } else if (selectedRows.value.length !== 1) {
+ proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉¢攢鍞彴璐︽暟鎹繘琛屾墦鍗�");
+ return;
+ }
+
+ const selectedRow = selectedRows.value[0];
+ const selectedId = selectedRow?.id;
+ if (command === "salesDeliveryNote") {
+ const selectedIds = selectedRows.value
+ .map(item => item?.id)
+ .filter(id => id !== null && id !== undefined && id !== "");
+ if (selectedIds.length !== selectedRows.value.length) {
+ proxy.$modal.msgWarning("褰撳墠閫夋嫨鏁版嵁瀛樺湪缂哄皯ID鐨勮褰曪紝鏃犳硶鎵撳嵃");
+ return;
+ }
+ const loadingText =
+ command === "salesOrder"
+ ? "姝e湪鑾峰彇閿�鍞鍗曟暟鎹紝璇风◢鍊�..."
+ : command === "salesDeliveryNote"
+ ? "姝e湪鑾峰彇閿�鍞彂璐у崟鏁版嵁锛岃绋嶅��..."
+ : "姝e湪鑾峰彇鐢熶骇娴佺▼鍗℃暟鎹紝璇风◢鍊�...";
+ proxy.$modal.loading(loadingText);
+ try {
+ const res = await getSalesInvoices(selectedIds);
+ const salesInvoiceData = res?.data ?? {};
+ printSalesDeliveryNote(salesInvoiceData, selectedRow);
+ } catch (error) {
+ console.error("鎵撳嵃閿�鍞彂璐у崟澶辫触:", error);
+ proxy.$modal.msgError("鎵撳嵃澶辫触锛岃绋嶅悗閲嶈瘯");
+ } finally {
+ proxy.$modal.closeLoading();
+ }
+ return;
+ }
+ if (!selectedId) {
+ proxy.$modal.msgWarning("褰撳墠閫夋嫨鏁版嵁缂哄皯ID锛屾棤娉曟墦鍗�");
+ return;
+ }
+
+ const loadingText =
+ command === "salesOrder"
+ ? "姝e湪鑾峰彇閿�鍞鍗曟暟鎹紝璇风◢鍊�..."
+ : command === "salesDeliveryNote"
+ ? "姝e湪鑾峰彇閿�鍞彂璐у崟鏁版嵁锛岃绋嶅��..."
+ : "姝e湪鑾峰彇鐢熶骇娴佺▼鍗℃暟鎹紝璇风◢鍊�...";
+ proxy.$modal.loading(loadingText);
+ try {
+ if (command === "salesOrder") {
+ const res = await getSalesOrder(selectedId);
+ const salesOrderData = res?.data ?? {};
+ printSalesOrder(salesOrderData);
+ } else {
+ const res = await getProcessCard(selectedId);
+ const processCardData = res?.data ?? {};
+ printFinishedProcessCard(processCardData);
+ }
+ } catch (error) {
+ console.error(
+ command === "salesOrder"
+ ? "鎵撳嵃閿�鍞鍗曞け璐�:"
+ : command === "salesDeliveryNote"
+ ? "鎵撳嵃閿�鍞彂璐у崟澶辫触:"
+ : "鎵撳嵃鐢熶骇娴佺▼鍗″け璐�:",
+ error
+ );
+ proxy.$modal.msgError("鎵撳嵃澶辫触锛岃绋嶅悗閲嶈瘯");
+ } finally {
+ proxy.$modal.closeLoading();
+ }
+ };
+
+ const handlePrintLabel = async () => {
+ if (selectedRows.value.length !== 1) {
+ proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉¢攢鍞彴璐︽暟鎹繘琛屾爣绛炬墦鍗�");
+ return;
+ }
+
+ const selectedId = selectedRows.value[0]?.id;
+ if (!selectedId) {
+ proxy.$modal.msgWarning("褰撳墠閫夋嫨鏁版嵁缂哄皯ID锛屾棤娉曟墦鍗版爣绛�");
+ return;
+ }
+
+ proxy.$modal.loading("姝e湪鑾峰彇鏍囩鏁版嵁锛岃绋嶅��...");
+ try {
+ const res = await getSalesLabel(selectedId);
+ const labelList = res?.data ?? [];
+ if (!Array.isArray(labelList) || labelList.length === 0) {
+ proxy.$modal.msgWarning("鏆傛棤鍙墦鍗版爣绛炬暟鎹�");
+ return;
+ }
+ printSalesLabel(labelList);
+ } catch (error) {
+ console.error("鎵撳嵃鏍囩澶辫触:", error);
+ proxy.$modal.msgError("鎵撳嵃鏍囩澶辫触锛岃绋嶅悗閲嶈瘯");
+ } finally {
+ proxy.$modal.closeLoading();
+ }
+ };
+
+ const mathNum = () => {
+ console.log("productForm.value", productForm.value);
+ if (!productForm.value.taxInclusiveUnitPrice) {
+ return;
+ }
+ if (!productForm.value.quantity) {
+ return;
+ }
+ const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
+ // 鍚◣鎬讳环璁$畻 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺 + 鍏朵粬閲戦鎬诲拰
+ const basePrice = proxy.calculateTaxIncludeTotalPrice(
+ productForm.value.taxInclusiveUnitPrice * settlePieceArea,
+ productForm.value.quantity
+ );
+ const otherAmountTotal = (
+ productForm.value.salesProductProcessList || []
+ ).reduce((total, item) => {
+ return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
+ }, 0);
+ productForm.value.taxInclusiveTotalPrice = (
+ parseFloat(basePrice) + otherAmountTotal
+ ).toFixed(2);
+ if (productForm.value.taxRate) {
+ // 涓嶅惈绋庢�讳环璁$畻
+ productForm.value.taxExclusiveTotalPrice =
+ proxy.calculateTaxExclusiveTotalPrice(
+ productForm.value.taxInclusiveTotalPrice,
+ productForm.value.taxRate
+ );
+ }
+ };
+
+ // 鏂板锛氬昂瀵�(瀹介珮)涓庨潰绉�(鍗曠墖/鎬昏)鑱斿姩
+ const recalcAreaTotals = () => {
+ const qty = Number(productForm.value.quantity ?? 0) || 0;
+ const actualPiece = Number(productForm.value.actualPieceArea ?? 0) || 0;
+ const settlePiece = Number(productForm.value.settlePieceArea ?? 0) || 0;
+
+ productForm.value.actualTotalArea = Number((actualPiece * qty).toFixed(5));
+ productForm.value.settleTotalArea = Number((settlePiece * qty).toFixed(5));
+ };
+
+ // 鏂板锛氬懆闀�(cm)锛堥噸绠県eavyBox鍛ㄩ暱锛�
+ // width/height 鍗曚綅涓� mm锛屽洜姝ゅ懆闀�(cm)闇�瑕侀櫎浠� 10
+ const recalcPerimeterFromWidthHeight = () => {
+ const width = Number(productForm.value.width ?? 0) || 0;
+ const height = Number(productForm.value.height ?? 0) || 0;
+
+ if (width <= 0 || height <= 0) {
+ productForm.value.perimeter = 0;
+ return;
+ }
+
+ // 鍛ㄩ暱 = (瀹� + 楂�) * 2锛屽崟浣嶄粠 mm 杞负 cm锛�/10
+ productForm.value.perimeter = Number(
+ (((width + height) * 2) / 10).toFixed(2)
+ );
+ };
+
+ const recalcAreaFromWidthHeight = () => {
+ const width = Number(productForm.value.width ?? 0) || 0;
+ const height = Number(productForm.value.height ?? 0) || 0;
+
+ if (width <= 0 || height <= 0) {
+ // 瀹介珮涓虹┖/涓�0鏃讹紝鎶婂崟鐗囬潰绉笌鎬婚潰绉疆涓�0
+ productForm.value.actualPieceArea = 0;
+ productForm.value.actualTotalArea = 0;
+ productForm.value.perimeter = 0;
+
+ // 鍙湁鍦ㄧ粨绠楀崟鐗囬潰绉篃涓虹┖/涓�0鏃讹紝鎵嶅悓姝ョ疆0锛岄伩鍏嶈鐩栫敤鎴锋墜鍔ㄥ~鍐�
+ const settlePiece = Number(productForm.value.settlePieceArea ?? 0) || 0;
+ if (!settlePiece) {
+ productForm.value.settlePieceArea = 0;
+ }
+ productForm.value.settleTotalArea = Number(
+ (
+ (Number(productForm.value.settlePieceArea ?? 0) || 0) *
+ (Number(productForm.value.quantity ?? 0) || 0)
+ ).toFixed(5)
+ );
+ return;
+ }
+
+ const computedPieceArea = (width * height) / 1e6; // mm*mm -> 銕�
+ const computed = Number(computedPieceArea.toFixed(5));
+
+ productForm.value.actualPieceArea = computed;
+ productForm.value.settlePieceArea = computed;
+
+ recalcPerimeterFromWidthHeight();
+ recalcAreaTotals();
+ // 闈㈢Н鏇存柊鍚庯紝閲嶆柊璁$畻鍚◣鎬讳环 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺
+ calculateFromUnitPrice(true);
+ };
+
+ // 鏍规嵁鍚◣鎬讳环璁$畻鍚◣鍗曚环鍜屾暟閲�
+ const calculateFromTotalPrice = () => {
+ if (isCalculating.value) return;
+
+ const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
+ const quantity = parseFloat(productForm.value.quantity);
+
+ if (!totalPrice || !quantity || quantity <= 0) {
+ return;
+ }
+
+ isCalculating.value = true;
+
+ // 璁$畻鍚◣鍗曚环 = (鍚◣鎬讳环 - 鍏朵粬閲戦鎬诲拰) / 鏁伴噺
+ const otherAmountTotal = (
+ productForm.value.salesProductProcessList || []
+ ).reduce((total, item) => {
+ return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
+ }, 0);
+ const basePrice = totalPrice - otherAmountTotal;
+ productForm.value.taxInclusiveUnitPrice = (basePrice / quantity).toFixed(2);
+
+ // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
+ if (productForm.value.taxRate) {
+ productForm.value.taxExclusiveTotalPrice =
+ proxy.calculateTaxExclusiveTotalPrice(
+ totalPrice,
+ productForm.value.taxRate
+ );
+ }
+
+ isCalculating.value = false;
+ };
+
+ // 鏍规嵁涓嶅惈绋庢�讳环璁$畻鍚◣鍗曚环鍜屾暟閲�
+ const calculateFromExclusiveTotalPrice = () => {
+ // if (!productForm.value.taxRate) {
+ // proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ // return;
+ // }
+ if (isCalculating.value) return;
+
+ const exclusiveTotalPrice = parseFloat(
+ productForm.value.taxExclusiveTotalPrice
+ );
+ const quantity = parseFloat(productForm.value.quantity);
+ const taxRate = parseFloat(productForm.value.taxRate);
+
+ if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
+ return;
+ }
+
+ isCalculating.value = true;
+
+ // 鍏堣绠楀惈绋庢�讳环 = 涓嶅惈绋庢�讳环 / (1 - 绋庣巼/100)
+ const taxRateDecimal = taxRate / 100;
+ const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
+ productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
+
+ // 璁$畻鍚◣鍗曚环 = (鍚◣鎬讳环 - 鍏朵粬閲戦鎬诲拰) / 鏁伴噺
+ const otherAmountTotal = (
+ productForm.value.salesProductProcessList || []
+ ).reduce((total, item) => {
+ return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
+ }, 0);
+ const basePrice = inclusiveTotalPrice - otherAmountTotal;
+ productForm.value.taxInclusiveUnitPrice = (basePrice / quantity).toFixed(2);
+
+ isCalculating.value = false;
+ };
+
+ // 鏍规嵁鏁伴噺鍙樺寲璁$畻鎬讳环
+ const calculateFromQuantity = () => {
+ // if (!productForm.value.taxRate) {
+ // proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ // return;
+ // }
+ if (isCalculating.value) return;
+
+ const quantity = parseFloat(productForm.value.quantity);
+ const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
+ const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
+
+ if (!quantity || quantity <= 0 || !unitPrice) {
+ return;
+ }
+
+ isCalculating.value = true;
+
+ // 璁$畻鍚◣鎬讳环 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺 + 鍏朵粬閲戦鎬诲拰
+ const basePrice = unitPrice * settlePieceArea * quantity;
+ const otherAmountTotal = (
+ productForm.value.salesProductProcessList || []
+ ).reduce((total, item) => {
+ return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
+ }, 0);
+ productForm.value.taxInclusiveTotalPrice = (
+ basePrice + otherAmountTotal
+ ).toFixed(2);
+
+ // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
+ if (productForm.value.taxRate) {
+ productForm.value.taxExclusiveTotalPrice =
+ proxy.calculateTaxExclusiveTotalPrice(
+ productForm.value.taxInclusiveTotalPrice,
+ productForm.value.taxRate
+ );
+ }
+
+ isCalculating.value = false;
+ };
+
+ // 鏍规嵁鍚◣鍗曚环鍙樺寲璁$畻鎬讳环
+ const calculateFromUnitPrice = (silent = false) => {
+ // if (!productForm.value.taxRate) {
+ // if (!silent) proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ // return;
+ // }
+ if (isCalculating.value) return;
+
+ const quantity = parseFloat(productForm.value.quantity);
+ const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
+ const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
+
+ if (!quantity || quantity <= 0 || !unitPrice) {
+ return;
+ }
+
+ isCalculating.value = true;
+
+ // 璁$畻鍚◣鎬讳环 = 鍗曚环 * 缁撶畻闈㈢Н * 鏁伴噺 + 鍏朵粬閲戦鎬诲拰
+ const basePrice = unitPrice * settlePieceArea * quantity;
+ const otherAmountTotal = (
+ productForm.value.salesProductProcessList || []
+ ).reduce((total, item) => {
+ return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
+ }, 0);
+ productForm.value.taxInclusiveTotalPrice = (
+ basePrice + otherAmountTotal
+ ).toFixed(2);
+
+ // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
+ if (productForm.value.taxRate) {
+ productForm.value.taxExclusiveTotalPrice =
+ proxy.calculateTaxExclusiveTotalPrice(
+ productForm.value.taxInclusiveTotalPrice,
+ productForm.value.taxRate
+ );
+ }
+
+ isCalculating.value = false;
+ };
+
+ // 鏍规嵁绋庣巼鍙樺寲璁$畻涓嶅惈绋庢�讳环
+ const calculateFromTaxRate = () => {
+ // if (!productForm.value.taxRate) {
+ // proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ // return;
+ // }
+ if (isCalculating.value) return;
+
+ const inclusiveTotalPrice = parseFloat(
+ productForm.value.taxInclusiveTotalPrice
+ );
+ const taxRate = parseFloat(productForm.value.taxRate);
+
+ if (!inclusiveTotalPrice || !taxRate) {
+ return;
+ }
+
+ isCalculating.value = true;
+
+ // 璁$畻涓嶅惈绋庢�讳环
+ productForm.value.taxExclusiveTotalPrice =
+ proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate);
+
+ isCalculating.value = false;
+ };
+ /**
+ * 鑾峰彇鍙戣揣鐘舵�佹枃鏈�
+ * @param row 琛屾暟鎹�
+ */
+ const getShippingStatusText = row => {
+ // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀�"宸插彂璐�"
+ if (row.shippingDate || row.shippingCarNumber) {
+ return "宸插彂璐�";
+ }
+
+ // 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
+ const status = row.shippingStatus;
+
+ // 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓�"寰呭彂璐�"
+ if (status === null || status === undefined || status === "") {
+ return "寰呭彂璐�";
+ }
+
+ // 鐘舵�佹槸瀛楃涓�
+ const statusStr = String(status).trim();
+ const statusTextMap = {
+ 寰呭彂璐�: "寰呭彂璐�",
+ 寰呭鏍�: "寰呭鏍�",
+ 瀹℃牳涓�: "瀹℃牳涓�",
+ 瀹℃牳鎷掔粷: "瀹℃牳鎷掔粷",
+ 瀹℃牳閫氳繃: "瀹℃牳閫氳繃",
+ 宸插彂璐�: "宸插彂璐�",
+ };
+ return statusTextMap[statusStr] || "寰呭彂璐�";
+ };
+
+ /**
+ * 鑾峰彇鍙戣揣鐘舵�佹爣绛剧被鍨嬶紙棰滆壊锛�
+ * @param row 琛屾暟鎹�
+ */
+ const getShippingStatusType = row => {
+ // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀虹豢鑹�
+ if (row.shippingDate || row.shippingCarNumber) {
+ return "success";
+ }
+
+ // 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
+ const status = row.shippingStatus;
+
+ // 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓虹伆鑹诧紙寰呭彂璐э級
+ if (status === null || status === undefined || status === "") {
+ return "info";
+ }
+
+ // 鐘舵�佹槸瀛楃涓�
+ const statusStr = String(status).trim();
+ const typeTextMap = {
+ 寰呭彂璐�: "info",
+ 寰呭鏍�: "info",
+ 瀹℃牳涓�: "warning",
+ 瀹℃牳鎷掔粷: "danger",
+ 瀹℃牳閫氳繃: "success",
+ 宸插彂璐�: "success",
+ };
+ return typeTextMap[statusStr] || "info";
+ };
+
+ /**
+ * 鍒ゆ柇鏄惁鍙互鍙戣揣
+ * 鍙湁鍦ㄤ骇鍝佺姸鎬佹槸鍏呰冻锛屽彂璐х姸鎬佹槸寰呭彂璐у拰瀹℃牳鎷掔粷鐨勬椂鍊欐墠鍙互鍙戣揣
+ * @param row 琛屾暟鎹�
+ */
+ const canShip = row => {
+ // 浜у搧鐘舵�佸繀椤绘槸鍏呰冻锛坅pproveStatus === 1锛�
+ if (row.approveStatus !== 1) {
+ return false;
+ }
+
+ // 濡傛灉鍚庣杩斿洖浜嗗彴璐︾骇鍙戣揣鐘舵�侊紙deliveryStatus锛�
+ // 1=宸插彂璐э紝鍒欑姝㈠啀娆″彂璐�
+ const deliveryStatus = row.deliveryStatus;
+ if (
+ deliveryStatus !== null &&
+ deliveryStatus !== undefined &&
+ String(deliveryStatus).trim() !== ""
+ ) {
+ if (Number(deliveryStatus) === 1) return false;
+ }
+
+ // 鑾峰彇鍙戣揣鐘舵��
+ const shippingStatus = row.shippingStatus;
+
+ // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屼笉鑳藉啀娆″彂璐�
+ if (row.shippingDate || row.shippingCarNumber) {
+ return false;
+ }
+
+ // 鍙戣揣鐘舵�佸繀椤绘槸"寰呭彂璐�"鎴�"瀹℃牳鎷掔粷"
+ const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
+ return statusStr === "寰呭彂璐�" || statusStr === "瀹℃牳鎷掔粷";
+ };
+
+ const handleBulkDelivery = async () => {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+
+ // 鍙厑璁搞�愭湭鍙戣揣/瀹℃壒澶辫触銆戣繘鍏ュ彂璐ф祦绋�
+ const canDeliveryLedgers = selectedRows.value.filter(r => {
+ const status = Number(r.deliveryStatus);
+ return status === 1 || status === 3;
+ });
+ if (canDeliveryLedgers.length === 0) {
+ proxy.$modal.msgWarning("浠呮湭鍙戣揣鎴栧鎵瑰け璐ョ殑鍙拌处鍙互鍙戣揣");
+ return;
+ }
+
+ // 宸插彂璐у彴璐︼細寮圭獥鎻愰啋锛屼笉鑳藉啀娆″彂璐э紙4 瑙嗕负宸插彂璐э級
+ const shippedLedgers = selectedRows.value.filter(
+ r => Number(r.deliveryStatus) === 4
+ );
+ if (shippedLedgers.length === selectedRows.value.length) {
+ try {
+ await ElMessageBox.alert("鎵�閫夐攢鍞彴璐﹀潎宸插彂璐э紝涓嶈兘鍐嶆鍙戣揣銆�", "鎻愮ず", {
+ type: "warning",
+ confirmButtonText: "鐭ラ亾浜�",
+ });
+ } catch {
+ /* 鍏抽棴寮圭獥 */
+ }
+ return;
+ }
+ if (shippedLedgers.length > 0) {
+ try {
+ await ElMessageBox.alert(
+ "閫変腑鐨勯攢鍞彴璐︿腑鍖呭惈宸插彂璐ц褰曪紝宸插彂璐х殑涓嶈兘鍐嶆鍙戣揣锛岀郴缁熷皢浠呬负鏈彂璐у彴璐﹀鐞嗐��",
+ "鎻愮ず",
+ {
+ type: "warning",
+ confirmButtonText: "鐭ラ亾浜�",
+ }
+ );
+ } catch {
+ return;
+ }
+ }
+
+ const customerNames = selectedRows.value.map(r =>
+ String(r.customerName || "").trim()
+ );
+ const uniqueCustomers = Array.from(new Set(customerNames));
+
+ // 瀹㈡埛鍚嶇О涓嶄竴鑷翠笉鍏佽鍙戣揣
+ if (uniqueCustomers.length > 1) {
+ proxy.$modal.msgWarning("瀹㈡埛鍚嶇О涓嶄竴鑷达紝涓嶅厑璁稿彂璐�");
+ return;
+ }
+
+ // 澶氭潯涓斿鎴蜂竴鑷达細浜屾纭
+ if (selectedRows.value.length > 1) {
+ try {
+ await ElMessageBox.confirm("鏄惁纭鍚堝苟鍙戣揣锛�", "鍚堝苟鍙戣揣", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ });
+ } catch (e) {
+ proxy.$modal.msg("宸插彇娑�");
+ return;
+ }
+ }
+
+ proxy.$modal.loading("姝e湪鑾峰彇浜у搧鏁版嵁锛岃绋嶅��...");
+ try {
+ const targets = [];
+ for (const ledger of selectedRows.value) {
+ //濡傛灉宸茬粡鏄�滃鎵逛腑(2)鈥濇垨鈥滃凡鍙戣揣(4)鈥濓紝鍒欒烦杩囷紝涓嶅厑璁搁噸澶嶆搷浣�
+ const status = Number(ledger.deliveryStatus);
+ if (status === 2 || status === 4) {
+ console.warn(
+ `鍙拌处缂栧彿 ${ledger.salesContractNo} 鐘舵�佷负 ${status}锛岃烦杩囧彂璐
+ );
+ continue;
+ }
+
+ let products = [];
+ try {
+ const res = await productList({ salesLedgerId: ledger.id, type: 1 });
+ products = res?.data || [];
+ } catch (error) {
+ products = [];
+ console.error("璇锋眰鍙戠敓寮傚父", error);
+ }
+ for (const product of products) {
+ if (!canShip(product)) continue;
+ targets.push({
+ ...product,
+ salesLedgerId: product.salesLedgerId || ledger.id,
+ });
+ }
+ }
+ if (targets.length === 0) {
+ proxy.$modal.msgWarning("娌℃湁鍙彂璐х殑鏁版嵁");
+ return;
+ }
+
+ currentDeliveryRows.value = targets;
+ deliveryForm.value = { type: "璐ц溅" };
+ // 閲嶇疆瀹℃壒浜鸿妭鐐癸紙榛樿涓�涓┖鑺傜偣锛�
+ approverNodes.value = [{ id: 1, userId: null }];
+ nextApproverId = 2;
+ deliveryFormVisible.value = true;
+ } finally {
+ proxy.$modal.closeLoading();
+ }
+ };
+
+ /**
+ * 涓嬭浇鏂囦欢
+ *
+ * @param row 涓嬭浇鏂囦欢鐨勭浉鍏充俊鎭璞�
+ */
+ const fileListRef = ref(null);
+ const fileListDialogVisible = ref(false);
+ const downLoadFile = row => {
+ getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
+ if (fileListRef.value) {
+ fileListRef.value.open(res.salesLedgerFiles);
+ }
+ });
+ };
+
+ // 鎵撳紑鍙戣揣寮规锛堝崟鏉★級
+ const openDeliveryForm = row => {
+ // 鍙厑璁搞�愭湭鍙戣揣/瀹℃壒澶辫触銆戝彂璐э紱宸插彂璐�/瀹℃壒涓笉鍏佽
+ const status = Number(row.deliveryStatus);
+ if (status !== 1 && status !== 3) {
+ proxy.$modal.msgWarning("鍙湁鍙戣揣鐘舵�佷负鏈彂璐ф垨瀹℃壒澶辫触鐨勮褰曟墠鍙互鍙戣揣");
+ return;
+ }
+
+ currentDeliveryRows.value = [row];
+ deliveryForm.value = {
+ type: "璐ц溅",
+ };
+ // 閲嶇疆瀹℃壒浜鸿妭鐐癸紙榛樿涓�涓┖鑺傜偣锛�
+ approverNodes.value = [{ id: 1, userId: null }];
+ nextApproverId = 2;
+ deliveryFormVisible.value = true;
+ };
+
+ // 鎻愪氦鍙戣揣琛ㄥ崟
+ const submitDelivery = () => {
+ proxy.$refs["deliveryFormRef"].validate(valid => {
+ if (valid) {
+ // 瀹℃壒浜哄繀濉牎楠岋紙鎵�鏈夎妭鐐归兘瑕侀�変汉锛�
+ const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
+ if (hasEmptyApprover) {
+ proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
+ return;
+ }
+ const approveUserIds = approverNodes.value
+ .map(node => node.userId)
+ .join(",");
+ // 淇濆瓨褰撳墠灞曞紑鐨勮ID锛屼互渚垮彂璐у悗閲嶆柊鍔犺浇瀛愯〃鏍兼暟鎹�
+ const currentExpandedKeys = [...expandedRowKeys.value];
+
+ const targets = currentDeliveryRows.value || [];
+ if (targets.length === 0) {
+ proxy.$modal.msgWarning("鏈�夋嫨鍙彂璐х殑鏁版嵁");
+ return;
+ }
+
+ // 鎸夊彴璐︾淮搴﹀幓閲嶏紝姣忎釜 salesLedgerId 鍙皟鐢ㄤ竴娆″彂璐ф帴鍙�
+ const uniqueLedgerIds = [
+ ...new Set(targets.map(item => item.salesLedgerId).filter(Boolean)),
+ ];
+
+ const run = async () => {
+ for (const salesLedgerId of uniqueLedgerIds) {
+ await addShippingInfo({
+ salesLedgerId,
+ type: deliveryForm.value.type,
+ approveUserIds,
+ });
+ }
+ };
+
+ run()
+ .then(() => {
+ proxy.$modal.msgSuccess("鍙戣揣鎴愬姛");
+ closeDeliveryDia();
+ // 鍒锋柊涓昏〃鏁版嵁
+ getList().then(() => {
+ // 濡傛灉涔嬪墠鏈夊睍寮�鐨勮锛岄噸鏂板姞杞借繖浜涜鐨勫瓙琛ㄦ牸鏁版嵁
+ if (currentExpandedKeys.length > 0) {
+ const loadPromises = currentExpandedKeys.map(ledgerId => {
+ return productList({ salesLedgerId: ledgerId, type: 1 }).then(
+ res => {
+ const index = tableData.value.findIndex(
+ item => item.id === ledgerId
+ );
+ if (index > -1) {
+ tableData.value[index].children = res.data;
+ }
+ }
+ );
+ });
+ Promise.all(loadPromises).then(() => {
+ expandedRowKeys.value = currentExpandedKeys;
+ });
+ }
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msgError("鍙戣揣澶辫触锛岃绋嶅悗閲嶈瘯");
+ });
+ }
+ });
+ };
+
+ // 鍏抽棴鍙戣揣寮规
+ const closeDeliveryDia = () => {
+ proxy.resetForm("deliveryFormRef");
+ deliveryFormVisible.value = false;
+ currentDeliveryRows.value = [];
+ };
+ const currentFactoryName = ref("");
+ const getCurrentFactoryName = async () => {
+ let res = await userStore.getInfo();
+ currentFactoryName.value = res.user.currentFactoryName;
+ };
+ onMounted(() => {
+ getList();
+ customerList().then(res => {
+ customerOption.value = res;
+ });
+ userListNoPage().then(res => {
+ userList.value = res.data;
+ });
+ approveUserList({ approveType: 7 }).then(res => {
+ userListApprove.value = res.data;
+ });
+ getCurrentFactoryName();
+ });
</script>
<style scoped lang="scss">
-.ml-10 {
- margin-left: 10px;
-}
+ .ml-10 {
+ margin-left: 10px;
+ }
-::v-deep .yellow {
- background-color: #FAF0DE;
-}
+ ::v-deep .yellow {
+ background-color: #faf0de;
+ }
-::v-deep .pink {
- background-color: #FAE1DE;
-}
+ ::v-deep .pink {
+ background-color: #fae1de;
+ }
-::v-deep .red {
- background-color: #FAE1DE;
-}
+ ::v-deep .red {
+ background-color: #fae1de;
+ }
-::v-deep .purple{
- background-color: #F4DEFA;
-}
+ ::v-deep .purple {
+ background-color: #f4defa;
+ }
-.other-amount-select {
- /* 澶氶�夋爣绛惧尯鍩熷己鍒跺崟琛岋紝涓嶈杈撳叆妗嗛殢鏍囩鎹㈣鑰屾媺楂橀珮搴� */
- :deep .el-select__tags {
- display: flex;
- flex-wrap: nowrap !important;
- white-space: nowrap;
- overflow: hidden;
- max-height: 32px;
- }
-}
+ .other-amount-select {
+ /* 澶氶�夋爣绛惧尯鍩熷己鍒跺崟琛岋紝涓嶈杈撳叆妗嗛殢鏍囩鎹㈣鑰屾媺楂橀珮搴� */
+ :deep .el-select__tags {
+ display: flex;
+ flex-wrap: nowrap !important;
+ white-space: nowrap;
+ overflow: hidden;
+ max-height: 32px;
+ }
+ }
-.table_list {
- margin-top: unset;
-}
+ .table_list {
+ margin-top: unset;
+ }
-.actions {
- display: flex;
- justify-content: space-between;
- margin-bottom: 10px;
-}
+ .actions {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ }
</style>
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index 5574bce..549e5ae 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -2,350 +2,444 @@
<div class="app-container">
<el-card class="box-card">
<!-- 鎼滅储鍖哄煙 -->
- <el-row :gutter="20" class="search-row">
+ <el-row :gutter="20"
+ class="search-row">
<el-col :span="8">
- <el-input
- v-model="searchForm.quotationNo"
- placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
- clearable
- @keyup.enter="handleSearch"
- >
+ <el-input v-model="searchForm.quotationNo"
+ placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
+ clearable
+ @keyup.enter="handleSearch">
<template #prefix>
- <el-icon><Search /></el-icon>
+ <el-icon>
+ <Search />
+ </el-icon>
</template>
</el-input>
</el-col>
<el-col :span="8">
- <el-select v-model="searchForm.customer" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
- <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
- {{
+ <el-select v-model="searchForm.customer"
+ placeholder="璇烽�夋嫨瀹㈡埛"
+ clearable>
+ <el-option v-for="item in customerOption"
+ :key="item.id"
+ :label="item.customerName"
+ :value="item.customerName">
+ {{
item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
}}
- </el-option>
+ </el-option>
</el-select>
</el-col>
-<!-- <el-col :span="6">-->
-<!-- <el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鎶ヤ环鐘舵��" clearable>-->
-<!-- <el-option label="鑽夌" value="鑽夌"></el-option>-->
-<!-- <el-option label="宸插彂閫�" value="宸插彂閫�"></el-option>-->
-<!-- <el-option label="瀹㈡埛纭" value="瀹㈡埛纭"></el-option>-->
-<!-- <el-option label="宸茶繃鏈�" value="宸茶繃鏈�"></el-option>-->
-<!-- </el-select>-->
-<!-- </el-col>-->
+ <!-- <el-col :span="6">-->
+ <!-- <el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鎶ヤ环鐘舵��" clearable>-->
+ <!-- <el-option label="鑽夌" value="鑽夌"></el-option>-->
+ <!-- <el-option label="宸插彂閫�" value="宸插彂閫�"></el-option>-->
+ <!-- <el-option label="瀹㈡埛纭" value="瀹㈡埛纭"></el-option>-->
+ <!-- <el-option label="宸茶繃鏈�" value="宸茶繃鏈�"></el-option>-->
+ <!-- </el-select>-->
+ <!-- </el-col>-->
<el-col :span="8">
- <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button type="primary"
+ @click="handleSearch">鎼滅储</el-button>
<el-button @click="resetSearch">閲嶇疆</el-button>
- <el-button style="float: right;" type="primary" @click="handleAdd">
+ <el-button style="float: right;"
+ type="primary"
+ @click="handleAdd">
鏂板鎶ヤ环
</el-button>
</el-col>
</el-row>
-
<!-- 鎶ヤ环鍒楄〃 -->
- <el-table
- :data="filteredList"
- style="width: 100%"
- v-loading="loading"
- border
- stripe
- height="calc(100vh - 22em)"
- >
- <el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" />
- <el-table-column prop="customer" label="瀹㈡埛鍚嶇О" />
- <el-table-column prop="salesperson" label="涓氬姟鍛�" width="100" />
- <el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="120" />
- <el-table-column prop="validDate" label="鏈夋晥鏈熻嚦" width="120" />
- <el-table-column prop="status" label="瀹℃壒鐘舵��" width="120" align="center">
+ <el-table :data="filteredList"
+ style="width: 100%"
+ v-loading="loading"
+ border
+ stripe
+ height="calc(100vh - 22em)">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column prop="quotationNo"
+ label="鎶ヤ环鍗曞彿" />
+ <el-table-column prop="customer"
+ label="瀹㈡埛鍚嶇О" />
+ <el-table-column prop="salesperson"
+ label="涓氬姟鍛�"
+ width="100" />
+ <el-table-column prop="quotationDate"
+ label="鎶ヤ环鏃ユ湡"
+ width="120" />
+ <el-table-column prop="validDate"
+ label="鏈夋晥鏈熻嚦"
+ width="120" />
+ <el-table-column prop="status"
+ label="瀹℃壒鐘舵��"
+ width="120"
+ align="center">
<template #default="{ row }">
- <el-tag :type="getStatusType(row.status)" disable-transitions>
+ <el-tag :type="getStatusType(row.status)"
+ disable-transitions>
{{ row.status || '--' }}
</el-tag>
</template>
</el-table-column>
- <el-table-column prop="totalAmount" label="鎶ヤ环閲戦" width="120">
+ <el-table-column prop="totalAmount"
+ label="鎶ヤ环閲戦"
+ width="120">
<template #default="scope">
楼{{ scope.row.totalAmount.toFixed(2) }}
</template>
</el-table-column>
- <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
+ <el-table-column label="鎿嶄綔"
+ width="200"
+ fixed="right"
+ align="center">
<template #default="scope">
- <el-button link type="primary" @click="handleEdit(scope.row)" :disabled="!['寰呭鎵�','鎷掔粷'].includes(scope.row.status)">缂栬緫</el-button>
- <el-button link type="primary" @click="handleView(scope.row)" style="color: #67C23A">鏌ョ湅</el-button>
- <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ <el-button link
+ type="primary"
+ @click="handleEdit(scope.row)"
+ :disabled="!['寰呭鎵�','鎷掔粷'].includes(scope.row.status)">缂栬緫</el-button>
+ <el-button link
+ type="primary"
+ @click="handleView(scope.row)"
+ style="color: #67C23A">鏌ョ湅</el-button>
+ <el-button link
+ type="danger"
+ @click="handleDelete(scope.row)">鍒犻櫎</el-button>
</template>
</el-table-column>
</el-table>
-
<!-- 鍒嗛〉 -->
- <pagination
- :total="pagination.total"
- layout="total, sizes, prev, pager, next, jumper"
- :page="pagination.currentPage"
- :limit="pagination.pageSize"
- @pagination="handleCurrentChange"
- />
+ <pagination :total="pagination.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="pagination.currentPage"
+ :limit="pagination.pageSize"
+ @pagination="handleCurrentChange" />
</el-card>
-
<!-- 鏂板/缂栬緫瀵硅瘽妗� -->
- <FormDialog v-model="dialogVisible" :title="dialogTitle" width="85%" :close-on-click-modal="false" @close="dialogVisible = false" @confirm="handleSubmit" @cancel="dialogVisible = false">
+ <FormDialog v-model="dialogVisible"
+ :title="dialogTitle"
+ width="85%"
+ :close-on-click-modal="false"
+ @close="dialogVisible = false"
+ @confirm="handleSubmit"
+ @cancel="dialogVisible = false">
<div class="quotation-form-container">
- <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="quotation-form">
- <!-- 鍩烘湰淇℃伅 -->
- <el-card class="form-card" shadow="hover">
- <template #header>
- <div class="card-header-wrapper">
- <el-icon class="card-icon"><Document /></el-icon>
- <span class="card-title">鍩烘湰淇℃伅</span>
- </div>
- </template>
- <div class="form-content">
- <el-row :gutter="24">
- <el-col :span="12">
- <el-form-item label="瀹㈡埛鍚嶇О" prop="customer">
- <el-select v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" @change="handleCustomerChange" clearable>
- <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
- {{
+ <el-form :model="form"
+ :rules="rules"
+ ref="formRef"
+ label-width="120px"
+ class="quotation-form">
+ <!-- 鍩烘湰淇℃伅 -->
+ <el-card class="form-card"
+ shadow="hover">
+ <template #header>
+ <div class="card-header-wrapper">
+ <el-icon class="card-icon">
+ <Document />
+ </el-icon>
+ <span class="card-title">鍩烘湰淇℃伅</span>
+ </div>
+ </template>
+ <div class="form-content">
+ <el-row :gutter="24">
+ <el-col :span="12">
+ <el-form-item label="瀹㈡埛鍚嶇О"
+ prop="customer">
+ <el-select v-model="form.customer"
+ placeholder="璇烽�夋嫨瀹㈡埛"
+ style="width: 100%"
+ @change="handleCustomerChange"
+ clearable>
+ <el-option v-for="item in customerOption"
+ :key="item.id"
+ :label="item.customerName"
+ :value="item.customerName">
+ {{
item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
}}
- </el-option>
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="涓氬姟鍛�" prop="salesperson">
- <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable>
- <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
- :value="item.nickName" />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="24">
- <el-col :span="12">
- <el-form-item label="鎶ヤ环鏃ユ湡" prop="quotationDate">
- <el-date-picker
- v-model="form.quotationDate"
- type="date"
- placeholder="閫夋嫨鎶ヤ环鏃ユ湡"
- style="width: 100%"
- format="YYYY-MM-DD"
- value-format="YYYY-MM-DD"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鏈夋晥鏈熻嚦" prop="validDate">
- <el-date-picker
- v-model="form.validDate"
- type="date"
- placeholder="閫夋嫨鏈夋晥鏈�"
- style="width: 100%"
- format="YYYY-MM-DD"
- value-format="YYYY-MM-DD"
- clearable
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="24">
- <el-col :span="12">
- <el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
- <el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏ヤ粯娆炬柟寮�" clearable />
- </el-form-item>
- </el-col>
- </el-row>
- </div>
- </el-card>
-
- <!-- 瀹℃壒浜轰俊鎭� -->
- <el-card class="form-card" shadow="hover">
- <template #header>
- <div class="card-header-wrapper">
- <el-icon class="card-icon"><UserFilled /></el-icon>
- <span class="card-title">瀹℃壒浜洪�夋嫨</span>
- <el-button type="primary" size="small" @click="addApproverNode" class="header-btn">
- <el-icon><Plus /></el-icon>
- 鏂板鑺傜偣
- </el-button>
+ </el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="涓氬姟鍛�"
+ prop="salesperson">
+ <el-select v-model="form.salesperson"
+ placeholder="璇烽�夋嫨涓氬姟鍛�"
+ style="width: 100%"
+ clearable>
+ <el-option v-for="item in userList"
+ :key="item.nickName"
+ :label="item.nickName"
+ :value="item.nickName" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="24">
+ <el-col :span="12">
+ <el-form-item label="鎶ヤ环鏃ユ湡"
+ prop="quotationDate">
+ <el-date-picker v-model="form.quotationDate"
+ type="date"
+ placeholder="閫夋嫨鎶ヤ环鏃ユ湡"
+ style="width: 100%"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏈夋晥鏈熻嚦"
+ prop="validDate">
+ <el-date-picker v-model="form.validDate"
+ type="date"
+ placeholder="閫夋嫨鏈夋晥鏈�"
+ style="width: 100%"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="24">
+ <el-col :span="12">
+ <el-form-item label="浠樻鏂瑰紡"
+ prop="paymentMethod">
+ <el-input v-model="form.paymentMethod"
+ placeholder="璇疯緭鍏ヤ粯娆炬柟寮�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
</div>
- </template>
- <div class="form-content">
- <el-row>
- <el-col :span="24">
- <el-form-item>
- <div class="approver-nodes-container">
- <div
- v-for="(node, index) in approverNodes"
- :key="node.id"
- class="approver-node-item"
- >
- <div class="approver-node-label">
- <span class="node-step">{{ index + 1 }}</span>
- <span class="node-text">瀹℃壒浜�</span>
- <el-icon class="arrow-icon"><ArrowRight /></el-icon>
+ </el-card>
+ <!-- 瀹℃壒浜轰俊鎭� -->
+ <el-card class="form-card"
+ shadow="hover">
+ <template #header>
+ <div class="card-header-wrapper">
+ <el-icon class="card-icon">
+ <UserFilled />
+ </el-icon>
+ <span class="card-title">瀹℃壒浜洪�夋嫨</span>
+ <el-button type="primary"
+ size="small"
+ @click="addApproverNode"
+ class="header-btn">
+ <el-icon>
+ <Plus />
+ </el-icon>
+ 鏂板鑺傜偣
+ </el-button>
+ </div>
+ </template>
+ <div class="form-content">
+ <el-row>
+ <el-col :span="24">
+ <el-form-item>
+ <div class="approver-nodes-container">
+ <div v-for="(node, index) in approverNodes"
+ :key="node.id"
+ class="approver-node-item">
+ <div class="approver-node-label">
+ <span class="node-step">{{ index + 1 }}</span>
+ <span class="node-text">瀹℃壒浜�</span>
+ <el-icon class="arrow-icon">
+ <ArrowRight />
+ </el-icon>
+ </div>
+ <el-select v-model="node.userId"
+ placeholder="閫夋嫨浜哄憳"
+ class="approver-select"
+ clearable>
+ <el-option v-for="user in userListApprove"
+ :key="user.userId"
+ :label="user.userName"
+ :value="user.userId" />
+ </el-select>
+ <el-button type="danger"
+ size="small"
+ :icon="Delete"
+ @click="removeApproverNode(index)"
+ v-if="approverNodes.length > 1"
+ class="remove-btn">鍒犻櫎</el-button>
</div>
- <el-select
- v-model="node.userId"
- placeholder="閫夋嫨浜哄憳"
- class="approver-select"
- clearable
- >
- <el-option
- v-for="user in userList"
- :key="user.userId"
- :label="user.nickName"
- :value="user.userId"
- />
- </el-select>
- <el-button
- type="danger"
- size="small"
- :icon="Delete"
- @click="removeApproverNode(index)"
- v-if="approverNodes.length > 1"
- class="remove-btn"
- >鍒犻櫎</el-button>
</div>
- </div>
- </el-form-item>
- </el-col>
- </el-row>
- </div>
- </el-card>
-
- <!-- 浜у搧淇℃伅 -->
- <el-card class="form-card" shadow="hover">
- <template #header>
- <div class="card-header-wrapper">
- <el-icon class="card-icon"><Box /></el-icon>
- <span class="card-title">浜у搧淇℃伅</span>
- <el-button type="primary" size="small" @click="addProduct" class="header-btn">
- <el-icon><Plus /></el-icon>
- 娣诲姞浜у搧
- </el-button>
+ </el-form-item>
+ </el-col>
+ </el-row>
</div>
- </template>
- <div class="form-content">
- <el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0">
- <el-table-column prop="product" label="浜у搧鍚嶇О" width="200">
- <template #default="scope">
- <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item">
- <el-tree-select
- v-model="scope.row.productId"
- placeholder="璇烽�夋嫨"
- clearable
- check-strictly
- @change="getModels($event, scope.row)"
- :data="productOptions"
- :render-after-expand="false"
- style="width: 100%"
- />
- </el-form-item>
- </template>
- </el-table-column>
- <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" width="200">
- <template #default="scope">
- <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
- <el-select
- v-model="scope.row.specificationId"
- placeholder="璇烽�夋嫨"
- clearable
- @change="getProductModel($event, scope.row)"
- style="width: 100%"
- >
- <el-option
- v-for="item in scope.row.modelOptions || []"
- :key="item.id"
- :label="item.model"
- :value="item.id"
- />
- </el-select>
- </el-form-item>
- </template>
- </el-table-column>
- <el-table-column prop="unit" label="鍗曚綅">
- <template #default="scope">
- <el-form-item :prop="`products.${scope.$index}.unit`" class="product-table-form-item">
- <el-input v-model="scope.row.unit" placeholder="鍗曚綅" clearable/>
- </el-form-item>
- </template>
- </el-table-column>
- <el-table-column prop="unitPrice" label="鍗曚环">
- <template #default="scope">
- <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
- <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
- </el-form-item>
- </template>
- </el-table-column>
- <el-table-column label="鎿嶄綔" width="80" align="center">
- <template #default="scope">
- <el-button link type="danger" @click="removeProduct(scope.$index)">鍒犻櫎</el-button>
- </template>
- </el-table-column>
- </el-table>
- <el-empty v-else description="鏆傛棤浜у搧锛岃鐐瑰嚮娣诲姞浜у搧" :image-size="80" />
- </div>
- </el-card>
-
- <!-- 澶囨敞淇℃伅 -->
- <el-card class="form-card" shadow="hover">
- <template #header>
- <div class="card-header-wrapper">
- <el-icon class="card-icon"><EditPen /></el-icon>
- <span class="card-title">澶囨敞淇℃伅</span>
+ </el-card>
+ <!-- 浜у搧淇℃伅 -->
+ <el-card class="form-card"
+ shadow="hover">
+ <template #header>
+ <div class="card-header-wrapper">
+ <el-icon class="card-icon">
+ <Box />
+ </el-icon>
+ <span class="card-title">浜у搧淇℃伅</span>
+ <el-button type="primary"
+ size="small"
+ @click="addProduct"
+ class="header-btn">
+ <el-icon>
+ <Plus />
+ </el-icon>
+ 娣诲姞浜у搧
+ </el-button>
+ </div>
+ </template>
+ <div class="form-content">
+ <el-table :data="form.products"
+ border
+ style="width: 100%"
+ class="product-table"
+ v-if="form.products.length > 0">
+ <el-table-column prop="product"
+ label="浜у搧鍚嶇О"
+ width="200">
+ <template #default="scope">
+ <el-form-item :prop="`products.${scope.$index}.productId`"
+ class="product-table-form-item">
+ <el-tree-select v-model="scope.row.productId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ check-strictly
+ @change="getModels($event, scope.row)"
+ :data="productOptions"
+ :render-after-expand="false"
+ style="width: 100%" />
+ </el-form-item>
+ </template>
+ </el-table-column>
+ <el-table-column prop="specification"
+ label="瑙勬牸鍨嬪彿"
+ width="200">
+ <template #default="scope">
+ <el-form-item :prop="`products.${scope.$index}.specificationId`"
+ class="product-table-form-item">
+ <el-select v-model="scope.row.specificationId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="getProductModel($event, scope.row)"
+ style="width: 100%">
+ <el-option v-for="item in scope.row.modelOptions || []"
+ :key="item.id"
+ :label="item.model"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ </template>
+ </el-table-column>
+ <el-table-column prop="unit"
+ label="鍗曚綅">
+ <template #default="scope">
+ <el-form-item :prop="`products.${scope.$index}.unit`"
+ class="product-table-form-item">
+ <el-input v-model="scope.row.unit"
+ placeholder="鍗曚綅"
+ clearable />
+ </el-form-item>
+ </template>
+ </el-table-column>
+ <el-table-column prop="unitPrice"
+ label="鍗曚环">
+ <template #default="scope">
+ <el-form-item :prop="`products.${scope.$index}.unitPrice`"
+ class="product-table-form-item">
+ <el-input-number v-model="scope.row.unitPrice"
+ :min="0"
+ :precision="2"
+ style="width: 100%" />
+ </el-form-item>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔"
+ width="80"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="danger"
+ @click="removeProduct(scope.$index)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <el-empty v-else
+ description="鏆傛棤浜у搧锛岃鐐瑰嚮娣诲姞浜у搧"
+ :image-size="80" />
</div>
- </template>
- <div class="form-content">
- <el-form-item label="澶囨敞" prop="remark">
- <el-input
- type="textarea"
- v-model="form.remark"
- placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙閫夊~锛�"
- :rows="4"
- maxlength="500"
- show-word-limit
- ></el-input>
- </el-form-item>
- </div>
- </el-card>
- </el-form>
+ </el-card>
+ <!-- 澶囨敞淇℃伅 -->
+ <el-card class="form-card"
+ shadow="hover">
+ <template #header>
+ <div class="card-header-wrapper">
+ <el-icon class="card-icon">
+ <EditPen />
+ </el-icon>
+ <span class="card-title">澶囨敞淇℃伅</span>
+ </div>
+ </template>
+ <div class="form-content">
+ <el-form-item label="澶囨敞"
+ prop="remark">
+ <el-input type="textarea"
+ v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙閫夊~锛�"
+ :rows="4"
+ maxlength="500"
+ show-word-limit></el-input>
+ </el-form-item>
+ </div>
+ </el-card>
+ </el-form>
</div>
</FormDialog>
-
<!-- 鏌ョ湅璇︽儏瀵硅瘽妗� -->
- <el-dialog v-model="viewDialogVisible" title="鎶ヤ环璇︽儏" width="800px">
- <el-descriptions :column="2" border>
+ <el-dialog v-model="viewDialogVisible"
+ title="鎶ヤ环璇︽儏"
+ width="800px">
+ <el-descriptions :column="2"
+ border>
<el-descriptions-item label="鎶ヤ环鍗曞彿">{{ currentQuotation.quotationNo }}</el-descriptions-item>
<el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentQuotation.customer }}</el-descriptions-item>
<el-descriptions-item label="涓氬姟鍛�">{{ currentQuotation.salesperson }}</el-descriptions-item>
<el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
<el-descriptions-item label="鏈夋晥鏈熻嚦">{{ currentQuotation.validDate }}</el-descriptions-item>
<el-descriptions-item label="浠樻鏂瑰紡">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
-<!-- <el-descriptions-item label="鎶ヤ环鐘舵��">-->
-<!-- <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
-<!-- </el-descriptions-item>-->
- <el-descriptions-item label="鎶ヤ环鎬婚" :span="2">
+ <!-- <el-descriptions-item label="鎶ヤ环鐘舵��">-->
+ <!-- <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
+ <!-- </el-descriptions-item>-->
+ <el-descriptions-item label="鎶ヤ环鎬婚"
+ :span="2">
<span style="font-size: 18px; color: #e6a23c; font-weight: bold;">楼{{ currentQuotation.totalAmount?.toFixed(2) }}</span>
</el-descriptions-item>
</el-descriptions>
-
<div style="margin: 20px 0;">
<h4>浜у搧鏄庣粏</h4>
- <el-table :data="currentQuotation.products" border style="width: 100%">
- <el-table-column prop="product" label="浜у搧鍚嶇О" />
- <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" />
- <el-table-column prop="unit" label="鍗曚綅" />
- <el-table-column prop="unitPrice" label="鍗曚环">
+ <el-table :data="currentQuotation.products"
+ border
+ style="width: 100%">
+ <el-table-column prop="product"
+ label="浜у搧鍚嶇О" />
+ <el-table-column prop="specification"
+ label="瑙勬牸鍨嬪彿" />
+ <el-table-column prop="unit"
+ label="鍗曚綅" />
+ <el-table-column prop="unitPrice"
+ label="鍗曚环">
<template #default="scope">
楼{{ scope.row.unitPrice.toFixed(2) }}
</template>
</el-table-column>
</el-table>
</div>
-
- <div v-if="currentQuotation.remark" style="margin-top: 20px;">
+ <div v-if="currentQuotation.remark"
+ style="margin-top: 20px;">
<h4>澶囨敞</h4>
<p>{{ currentQuotation.remark }}</p>
</div>
@@ -354,742 +448,782 @@
</template>
<script setup>
-import { ref, reactive, computed, onMounted, markRaw, shallowRef } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue'
-import Pagination from '@/components/PIMTable/Pagination.vue'
-import FormDialog from '@/components/Dialog/FormDialog.vue'
-import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js'
-import {userListNoPage} from "@/api/system/user.js";
-import {customerList} from "@/api/salesManagement/salesLedger.js";
-import {modelList, productTreeList} from "@/api/basicData/product.js";
+ import { ref, reactive, computed, onMounted, markRaw, shallowRef } from "vue";
+ import { ElMessage, ElMessageBox } from "element-plus";
+ import {
+ Search,
+ Document,
+ UserFilled,
+ Box,
+ EditPen,
+ Plus,
+ ArrowRight,
+ Delete,
+ } from "@element-plus/icons-vue";
+ import Pagination from "@/components/PIMTable/Pagination.vue";
+ import FormDialog from "@/components/Dialog/FormDialog.vue";
+ import {
+ getQuotationList,
+ addQuotation,
+ updateQuotation,
+ deleteQuotation,
+ } from "@/api/salesManagement/salesQuotation.js";
+ import { userListNoPage } from "@/api/system/user.js";
+ import { approveUserList } from "@/api/collaborativeApproval/approvalProcess.js";
-// 鍝嶅簲寮忔暟鎹�
-const loading = ref(false)
-const searchForm = reactive({
- quotationNo: '',
- customer: '',
- status: ''
-})
+ import { customerList } from "@/api/salesManagement/salesLedger.js";
+ import { modelList, productTreeList } from "@/api/basicData/product.js";
-const quotationList = ref([])
-const productOptions = ref([]);
-const modelOptions = ref([]);
-const pagination = reactive({
- total: 3,
- currentPage: 1,
- pageSize: 100
-})
+ // 鍝嶅簲寮忔暟鎹�
+ const loading = ref(false);
+ const searchForm = reactive({
+ quotationNo: "",
+ customer: "",
+ status: "",
+ });
-const dialogVisible = ref(false)
-const viewDialogVisible = ref(false)
-const dialogTitle = ref('鏂板鎶ヤ环')
-const form = reactive({
- quotationNo: '',
- customer: '',
- salesperson: '',
- quotationDate: '',
- validDate: '',
- paymentMethod: '',
- status: '鑽夌',
- remark: '',
- products: [],
- subtotal: 0,
- freight: 0,
- otherFee: 0,
- discountRate: 0,
- discountAmount: 0,
- totalAmount: 0
-})
+ const quotationList = ref([]);
+ const productOptions = ref([]);
+ const modelOptions = ref([]);
+ const pagination = reactive({
+ total: 3,
+ currentPage: 1,
+ pageSize: 100,
+ });
-const baseRules = {
- 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 dialogVisible = ref(false);
+ const viewDialogVisible = ref(false);
+ const dialogTitle = ref("鏂板鎶ヤ环");
+ const form = reactive({
+ quotationNo: "",
+ customer: "",
+ salesperson: "",
+ quotationDate: "",
+ validDate: "",
+ paymentMethod: "",
+ status: "鑽夌",
+ remark: "",
+ products: [],
+ subtotal: 0,
+ freight: 0,
+ otherFee: 0,
+ discountRate: 0,
+ discountAmount: 0,
+ totalAmount: 0,
+ });
-const productRowRules = {
- productId: [{ required: true, message: '璇烽�夋嫨浜у搧鍚嶇О', trigger: 'change' }],
- specificationId: [{ required: true, message: '璇烽�夋嫨瑙勬牸鍨嬪彿', trigger: 'change' }],
- unit: [{ required: true, message: '璇峰~鍐欏崟浣�', trigger: 'blur' }],
- unitPrice: [{ required: true, message: '璇峰~鍐欏崟浠�', trigger: 'change' }]
-}
-const rules = computed(() => {
- const r = { ...baseRules }
- ;(form.products || []).forEach((_, i) => {
- r[`products.${i}.productId`] = productRowRules.productId
- r[`products.${i}.specificationId`] = productRowRules.specificationId
- r[`products.${i}.unit`] = productRowRules.unit
- r[`products.${i}.unitPrice`] = productRowRules.unitPrice
- })
- return r
-})
-const userList = ref([]);
-const customerOption = ref([]);
+ const baseRules = {
+ 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 approverNodes = ref([
- { id: 1, userId: null }
-])
-let nextApproverId = 2
+ const productRowRules = {
+ productId: [{ required: true, message: "璇烽�夋嫨浜у搧鍚嶇О", trigger: "change" }],
+ specificationId: [
+ { required: true, message: "璇烽�夋嫨瑙勬牸鍨嬪彿", trigger: "change" },
+ ],
+ unit: [{ required: true, message: "璇峰~鍐欏崟浣�", trigger: "blur" }],
+ unitPrice: [{ required: true, message: "璇峰~鍐欏崟浠�", trigger: "change" }],
+ };
+ const rules = computed(() => {
+ const r = { ...baseRules };
+ (form.products || []).forEach((_, i) => {
+ r[`products.${i}.productId`] = productRowRules.productId;
+ r[`products.${i}.specificationId`] = productRowRules.specificationId;
+ r[`products.${i}.unit`] = productRowRules.unit;
+ r[`products.${i}.unitPrice`] = productRowRules.unitPrice;
+ });
+ return r;
+ });
+ const userList = ref([]);
+ const userListApprove = ref([]);
+ const customerOption = ref([]);
-const isEdit = ref(false)
-const editId = ref(null)
-const currentQuotation = ref({})
-const formRef = ref()
+ // 瀹℃壒浜鸿妭鐐圭浉鍏�
+ const approverNodes = ref([{ id: 1, userId: null }]);
+ let nextApproverId = 2;
-// 娣诲姞瀹℃壒浜鸿妭鐐�
-function addApproverNode() {
- approverNodes.value.push({ id: nextApproverId++, userId: null })
-}
+ const isEdit = ref(false);
+ const editId = ref(null);
+ const currentQuotation = ref({});
+ const formRef = ref();
-// 鍒犻櫎瀹℃壒浜鸿妭鐐�
-function removeApproverNode(index) {
- approverNodes.value.splice(index, 1)
-}
-
-// 璁$畻灞炴��
-const filteredList = computed(() => {
- let list = quotationList.value
- return list
-})
-
-// 鏂规硶
-const getStatusType = (status) => {
- const statusMap = {
- '寰呭鎵�': 'info',
- '瀹℃牳涓�': 'primary',
- '閫氳繃': 'success',
- '鎷掔粷': 'danger'
+ // 娣诲姞瀹℃壒浜鸿妭鐐�
+ function addApproverNode() {
+ approverNodes.value.push({ id: nextApproverId++, userId: null });
}
- return statusMap[status] || 'info'
-}
-const resetSearch = () => {
- searchForm.quotationNo = ''
- searchForm.customer = ''
- searchForm.status = ''
- // 閲嶇疆鍒扮涓�椤靛苟閲嶆柊鏌ヨ
- pagination.currentPage = 1
- handleSearch()
-}
-
-const handleAdd = async () => {
- dialogTitle.value = '鏂板鎶ヤ环'
- isEdit.value = false
- resetForm()
- // 閲嶇疆瀹℃壒浜鸿妭鐐�
- approverNodes.value = [{ id: 1, userId: null }]
- nextApproverId = 2
- dialogVisible.value = true
- let userLists = await userListNoPage();
- // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
- userList.value = (userLists.data || []).map(item => ({
- userId: item.userId,
- nickName: item.nickName || '',
- userName: item.userName || ''
- }));
- getProductOptions();
- customerList().then((res) => {
- // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
- customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
- id: item.id,
- customerName: item.customerName || '',
- taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
- }))
- });
-}
-const getProductOptions = () => {
- // 杩斿洖 Promise锛屼究浜庣紪杈戞椂 await 纭繚鑳藉弽鏄�
- return productTreeList().then((res) => {
- productOptions.value = convertIdToValue(res);
- return productOptions.value
- });
-};
-function convertIdToValue(data) {
- return data.map((item) => {
- const { id, children, ...rest } = item;
- const newItem = {
- ...rest,
- value: id, // 灏� id 鏀逛负 value
- };
- if (children && children.length > 0) {
- newItem.children = convertIdToValue(children);
- }
-
- return newItem;
- });
-}
-// 鏍规嵁鍚嶇О鍙嶆煡鑺傜偣 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
-function 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 getModels = (value, row) => {
- if (!row) return;
- // 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
- if (!value) {
- row.productId = '';
- row.product = '';
- row.modelOptions = [];
- row.specificationId = '';
- row.specification = '';
- row.unit = '';
- return;
- }
- // 鏇存柊 productId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
- row.productId = value;
- // 鎵惧埌瀵瑰簲鐨� label 骞惰祴鍊肩粰 row.product
- const label = findNodeById(productOptions.value, value);
- if (label) {
- row.product = label;
- }
- // 鑾峰彇瑙勬牸鍨嬪彿鍒楄〃锛岃缃埌褰撳墠琛岀殑 modelOptions
- modelList({ id: value }).then((res) => {
- row.modelOptions = res || [];
- });
-};
-const getProductModel = (value, row) => {
- if (!row) return;
- // 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
- if (!value) {
- row.specificationId = '';
- row.specification = '';
- row.unit = '';
- return;
- }
- // 鏇存柊 specificationId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
- row.specificationId = value;
- const modelOptions = row.modelOptions || [];
- const index = modelOptions.findIndex((item) => item.id === value);
- if (index !== -1) {
- row.specification = modelOptions[index].model;
- row.unit = modelOptions[index].unit;
- } else {
- row.specification = '';
- row.unit = '';
- }
-};
-const findNodeById = (nodes, productId) => {
- for (let i = 0; i < nodes.length; i++) {
- if (nodes[i].value === productId) {
- return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥� label
- }
- if (nodes[i].children && nodes[i].children.length > 0) {
- const foundLabel = findNodeById(nodes[i].children, productId);
- if (foundLabel) {
- return foundLabel; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖 label
- }
- }
- }
- return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
-};
-const handleView = (row) => {
- // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
- currentQuotation.value = {
- quotationNo: row.quotationNo || '',
- customer: row.customer || '',
- salesperson: row.salesperson || '',
- quotationDate: row.quotationDate || '',
- validDate: row.validDate || '',
- paymentMethod: row.paymentMethod || '',
- status: row.status || '',
- remark: row.remark || '',
- products: row.products ? row.products.map(product => ({
- productId: product.productId || '',
- product: product.product || product.productName || '',
- specificationId: product.specificationId || '',
- specification: product.specification || '',
- quantity: product.quantity || 0,
- unit: product.unit || '',
- unitPrice: product.unitPrice || 0,
- amount: product.amount || 0
- })) : [],
- totalAmount: row.totalAmount || 0
+ // 鍒犻櫎瀹℃壒浜鸿妭鐐�
+ function removeApproverNode(index) {
+ approverNodes.value.splice(index, 1);
}
- viewDialogVisible.value = true
-}
-const handleEdit = async (row) => {
- dialogTitle.value = '缂栬緫鎶ヤ环'
- isEdit.value = true
- editId.value = row.id
- form.id = row.id || form.id || null
- // 鍏堝姞杞戒骇鍝佹爲鏁版嵁锛屽惁鍒� el-tree-select 鏃犳硶鍙嶆樉浜у搧鍚嶇О
- await getProductOptions()
+ // 璁$畻灞炴��
+ const filteredList = computed(() => {
+ let list = quotationList.value;
+ return list;
+ });
- // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
- form.quotationNo = row.quotationNo || ''
- form.customer = row.customer || ''
- form.salesperson = row.salesperson || ''
- form.quotationDate = row.quotationDate || ''
- form.validDate = row.validDate || ''
- form.paymentMethod = row.paymentMethod || ''
- form.status = row.status || '鑽夌'
- form.remark = row.remark || ''
- form.products = row.products ? await Promise.all(row.products.map(async (product) => {
- const productName = product.product || product.productName || ''
- // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究鏍戦�夋嫨鍣ㄥ弽鏄�
- const resolvedProductId = product.productId
- ? Number(product.productId)
- : findNodeIdByLabel(productOptions.value, productName) || ''
-
- // 濡傛灉鏈変骇鍝両D锛屽姞杞藉搴旂殑瑙勬牸鍨嬪彿鍒楄〃
- let modelOptions = [];
- let resolvedSpecificationId = product.specificationId || '';
-
- if (resolvedProductId) {
- try {
- const res = await modelList({ id: resolvedProductId });
- modelOptions = res || [];
-
- // 濡傛灉杩斿洖鐨勬暟鎹病鏈� specificationId锛屼絾鏈� specification 鍚嶇О锛屾牴鎹悕绉版煡鎵� ID
- if (!resolvedSpecificationId && product.specification) {
- const foundModel = modelOptions.find(item => item.model === product.specification);
- if (foundModel) {
- resolvedSpecificationId = foundModel.id;
- }
- }
- } catch (error) {
- console.error('鍔犺浇瑙勬牸鍨嬪彿澶辫触:', error);
- }
- }
-
- return {
- productId: resolvedProductId,
- product: productName,
- specificationId: resolvedSpecificationId,
- specification: product.specification || '',
- quantity: product.quantity || 0,
- unit: product.unit || '',
- unitPrice: product.unitPrice || 0,
- amount: product.amount || 0,
- modelOptions: modelOptions // 涓烘瘡琛屾坊鍔犵嫭绔嬬殑瑙勬牸鍨嬪彿鍒楄〃
- }
- })) : []
- form.subtotal = row.subtotal || 0
- form.freight = row.freight || 0
- form.otherFee = row.otherFee || 0
- form.discountRate = row.discountRate || 0
- form.discountAmount = row.discountAmount || 0
- form.totalAmount = row.totalAmount || 0
-
- // 鍙嶆樉瀹℃壒浜�
- if (row.approveUserIds) {
- const userIds = row.approveUserIds.split(',')
- approverNodes.value = userIds.map((userId, idx) => ({
- id: idx + 1,
- userId: parseInt(userId.trim())
- }))
- nextApproverId = userIds.length + 1
- } else {
- approverNodes.value = [{ id: 1, userId: null }]
- nextApproverId = 2
- }
-
- // 鍔犺浇鐢ㄦ埛鍒楄〃
- let userLists = await userListNoPage();
- userList.value = (userLists.data || []).map(item => ({
- userId: item.userId,
- nickName: item.nickName || '',
- userName: item.userName || ''
- }));
-
- dialogVisible.value = true
-}
+ // 鏂规硶
+ const getStatusType = status => {
+ const statusMap = {
+ 寰呭鎵�: "info",
+ 瀹℃牳涓�: "primary",
+ 閫氳繃: "success",
+ 鎷掔粷: "danger",
+ };
+ return statusMap[status] || "info";
+ };
+ const resetSearch = () => {
+ searchForm.quotationNo = "";
+ searchForm.customer = "";
+ searchForm.status = "";
+ // 閲嶇疆鍒扮涓�椤靛苟閲嶆柊鏌ヨ
+ pagination.currentPage = 1;
+ handleSearch();
+ };
-const handleDelete = (row) => {
- ElMessageBox.confirm('纭鍒犻櫎璇ユ姤浠峰崟鍚楋紵', '鎻愮ず', {
- confirmButtonText: '纭畾',
- cancelButtonText: '鍙栨秷',
- type: 'warning'
- }).then(() => {
- const index = quotationList.value.findIndex(item => item.id === row.id)
- if (index > -1) {
- deleteQuotation(row.id).then(res=>{
- // console.log(res)
- if(res.code===200){
- ElMessage.success('鍒犻櫎鎴愬姛')
- handleSearch()
- }
- })
- // quotationList.value.splice(index, 1)
- // pagination.total--
- // ElMessage.success('鍒犻櫎鎴愬姛')
- }
- })
-}
-
-const resetForm = () => {
- form.customer = ''
- form.salesperson = ''
- form.quotationDate = ''
- form.validDate = ''
- form.paymentMethod = ''
- form.status = '鑽夌'
- form.remark = ''
- form.products = []
- form.subtotal = 0
- form.freight = 0
- form.otherFee = 0
- form.discountRate = 0
- form.discountAmount = 0
- form.totalAmount = 0
-}
-
-const addProduct = () => {
- form.products.push({
- productId: '',
- product: '',
- productName: '',
- specificationId: '',
- specification: '',
- quantity: 1,
- unit: '',
- unitPrice: 0,
- amount: 0,
- modelOptions: [] // 涓烘瘡琛屾坊鍔犵嫭绔嬬殑瑙勬牸鍨嬪彿鍒楄〃
- })
-}
-
-const removeProduct = (index) => {
- form.products.splice(index, 1)
- calculateSubtotal()
-}
-
-const calculateAmount = (product) => {
- product.amount = product.quantity * product.unitPrice
- calculateSubtotal()
-}
-
-const calculateSubtotal = () => {
- form.subtotal = form.products.reduce((sum, product) => sum + product.amount, 0)
- calculateTotal()
-}
-
-const calculateTotal = () => {
- form.discountAmount = form.subtotal * (form.discountRate / 100)
- form.totalAmount = form.subtotal + form.freight + form.otherFee - form.discountAmount
-}
-
-const handleCustomerChange = () => {
- // 鍙互鏍规嵁瀹㈡埛淇℃伅鑷姩濉厖涓�浜涢粯璁ゅ��
-}
-
-const handleSubmit = () => {
- formRef.value.validate((valid) => {
- if (valid) {
- if (form.products.length === 0) {
- ElMessage.warning('璇疯嚦灏戞坊鍔犱竴涓骇鍝�')
- return
- }
-
- // 瀹℃壒浜哄繀濉牎楠�
- const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
- if (hasEmptyApprover) {
- ElMessage.error('璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒')
- return
- }
-
- // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
- form.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
-
- // 璁$畻鎵�鏈変骇鍝佺殑鍗曚环鎬诲拰
- form.totalAmount = form.products.reduce((sum, product) => {
- const price = Number(product.unitPrice) || 0
- return sum + price
- }, 0)
-
- if (isEdit.value) {
- // 缂栬緫
- const index = quotationList.value.findIndex(item => item.id === editId.value)
- if (index > -1) {
- updateQuotation(form).then(res=>{
- // console.log(res)
- if(res.code===200){
- ElMessage.success('缂栬緫鎴愬姛')
- dialogVisible.value = false
- handleSearch()
- }
- })
- }
- } else {
- // 鏂板
- addQuotation(form).then(res=>{
- if(res.code===200){
- ElMessage.success('鏂板鎴愬姛')
- dialogVisible.value = false
- handleSearch()
- }
- })
- }
-
- }
- })
-}
-
-const handleCurrentChange = (val) => {
- pagination.currentPage = val.page
- pagination.pageSize = val.limit
- // 鍒嗛〉鍙樺寲鏃堕噸鏂版煡璇㈠垪琛�
- handleSearch()
-}
-const handleSearch = ()=>{
- const params = {
- // 鍚庣鍒嗛〉鍙傛暟锛歝urrent / size
- current: pagination.currentPage,
- size: pagination.pageSize,
- ...searchForm
- }
- getQuotationList(params).then(res=>{
- // console.log(res)
- if(res.code===200){
- // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鎴栧叾浠栧璞℃斁鍏ュ搷搴斿紡瀵硅薄
- quotationList.value = (res.data.records || []).map(item => ({
+ const handleAdd = async () => {
+ dialogTitle.value = "鏂板鎶ヤ环";
+ isEdit.value = false;
+ resetForm();
+ // 閲嶇疆瀹℃壒浜鸿妭鐐�
+ approverNodes.value = [{ id: 1, userId: null }];
+ nextApproverId = 2;
+ dialogVisible.value = true;
+ let userLists = await userListNoPage();
+ // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+ userList.value = (userLists.data || []).map(item => ({
+ userId: item.userId,
+ nickName: item.nickName || "",
+ userName: item.userName || "",
+ }));
+ approveUserList({ approveType: 6 }).then(res => {
+ userListApprove.value = res.data;
+ });
+ getProductOptions();
+ customerList().then(res => {
+ // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+ customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
id: item.id,
- quotationNo: item.quotationNo || '',
- customer: item.customer || '',
- salesperson: item.salesperson || '',
- quotationDate: item.quotationDate || '',
- validDate: item.validDate || '',
- paymentMethod: item.paymentMethod || '',
- status: item.status || '鑽夌',
- // 瀹℃壒浜猴紙鐢ㄤ簬缂栬緫鏃跺弽鏄撅級
- approveUserIds: item.approveUserIds || '',
- remark: item.remark || '',
- products: item.products ? item.products.map(product => ({
- productId: product.productId || '',
- product: product.product || product.productName || '',
- specificationId: product.specificationId || '',
- specification: product.specification || '',
- quantity: product.quantity || 0,
- unit: product.unit || '',
- unitPrice: product.unitPrice || 0,
- amount: product.amount || 0
- })) : [],
- subtotal: item.subtotal || 0,
- freight: item.freight || 0,
- otherFee: item.otherFee || 0,
- discountRate: item.discountRate || 0,
- discountAmount: item.discountAmount || 0,
- totalAmount: item.totalAmount || 0
- }))
- pagination.total = res.data.total
- }
- })
- customerList().then((res) => {
- // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
- customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
- id: item.id,
- customerName: item.customerName || '',
- taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
- }))
- });
-}
+ customerName: item.customerName || "",
+ taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || "",
+ }));
+ });
+ };
+ const getProductOptions = () => {
+ // 杩斿洖 Promise锛屼究浜庣紪杈戞椂 await 纭繚鑳藉弽鏄�
+ return productTreeList().then(res => {
+ productOptions.value = convertIdToValue(res);
+ return productOptions.value;
+ });
+ };
+ function convertIdToValue(data) {
+ return data.map(item => {
+ const { id, children, ...rest } = item;
+ const newItem = {
+ ...rest,
+ value: id, // 灏� id 鏀逛负 value
+ };
+ if (children && children.length > 0) {
+ newItem.children = convertIdToValue(children);
+ }
-onMounted(()=>{
- handleSearch()
-})
+ return newItem;
+ });
+ }
+ // 鏍规嵁鍚嶇О鍙嶆煡鑺傜偣 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
+ function 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 getModels = (value, row) => {
+ if (!row) return;
+ // 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
+ if (!value) {
+ row.productId = "";
+ row.product = "";
+ row.modelOptions = [];
+ row.specificationId = "";
+ row.specification = "";
+ row.unit = "";
+ return;
+ }
+ // 鏇存柊 productId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
+ row.productId = value;
+ // 鎵惧埌瀵瑰簲鐨� label 骞惰祴鍊肩粰 row.product
+ const label = findNodeById(productOptions.value, value);
+ if (label) {
+ row.product = label;
+ }
+ // 鑾峰彇瑙勬牸鍨嬪彿鍒楄〃锛岃缃埌褰撳墠琛岀殑 modelOptions
+ modelList({ id: value }).then(res => {
+ row.modelOptions = res || [];
+ });
+ };
+ const getProductModel = (value, row) => {
+ if (!row) return;
+ // 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
+ if (!value) {
+ row.specificationId = "";
+ row.specification = "";
+ row.unit = "";
+ return;
+ }
+ // 鏇存柊 specificationId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
+ row.specificationId = value;
+ const modelOptions = row.modelOptions || [];
+ const index = modelOptions.findIndex(item => item.id === value);
+ if (index !== -1) {
+ row.specification = modelOptions[index].model;
+ row.unit = modelOptions[index].unit;
+ } else {
+ row.specification = "";
+ row.unit = "";
+ }
+ };
+ const findNodeById = (nodes, productId) => {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].value === productId) {
+ return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥� label
+ }
+ if (nodes[i].children && nodes[i].children.length > 0) {
+ const foundLabel = findNodeById(nodes[i].children, productId);
+ if (foundLabel) {
+ return foundLabel; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖 label
+ }
+ }
+ }
+ return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+ };
+ const handleView = row => {
+ // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+ currentQuotation.value = {
+ quotationNo: row.quotationNo || "",
+ customer: row.customer || "",
+ salesperson: row.salesperson || "",
+ quotationDate: row.quotationDate || "",
+ validDate: row.validDate || "",
+ paymentMethod: row.paymentMethod || "",
+ status: row.status || "",
+ remark: row.remark || "",
+ products: row.products
+ ? row.products.map(product => ({
+ productId: product.productId || "",
+ product: product.product || product.productName || "",
+ specificationId: product.specificationId || "",
+ specification: product.specification || "",
+ quantity: product.quantity || 0,
+ unit: product.unit || "",
+ unitPrice: product.unitPrice || 0,
+ amount: product.amount || 0,
+ }))
+ : [],
+ totalAmount: row.totalAmount || 0,
+ };
+ viewDialogVisible.value = true;
+ };
+
+ const handleEdit = async row => {
+ dialogTitle.value = "缂栬緫鎶ヤ环";
+ isEdit.value = true;
+ editId.value = row.id;
+ form.id = row.id || form.id || null;
+ // 鍏堝姞杞戒骇鍝佹爲鏁版嵁锛屽惁鍒� el-tree-select 鏃犳硶鍙嶆樉浜у搧鍚嶇О
+ await getProductOptions();
+
+ // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+ form.quotationNo = row.quotationNo || "";
+ form.customer = row.customer || "";
+ form.salesperson = row.salesperson || "";
+ form.quotationDate = row.quotationDate || "";
+ form.validDate = row.validDate || "";
+ form.paymentMethod = row.paymentMethod || "";
+ form.status = row.status || "鑽夌";
+ form.remark = row.remark || "";
+ form.products = row.products
+ ? await Promise.all(
+ row.products.map(async product => {
+ const productName = product.product || product.productName || "";
+ // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究鏍戦�夋嫨鍣ㄥ弽鏄�
+ const resolvedProductId = product.productId
+ ? Number(product.productId)
+ : findNodeIdByLabel(productOptions.value, productName) || "";
+
+ // 濡傛灉鏈変骇鍝両D锛屽姞杞藉搴旂殑瑙勬牸鍨嬪彿鍒楄〃
+ let modelOptions = [];
+ let resolvedSpecificationId = product.specificationId || "";
+
+ if (resolvedProductId) {
+ try {
+ const res = await modelList({ id: resolvedProductId });
+ modelOptions = res || [];
+
+ // 濡傛灉杩斿洖鐨勬暟鎹病鏈� specificationId锛屼絾鏈� specification 鍚嶇О锛屾牴鎹悕绉版煡鎵� ID
+ if (!resolvedSpecificationId && product.specification) {
+ const foundModel = modelOptions.find(
+ item => item.model === product.specification
+ );
+ if (foundModel) {
+ resolvedSpecificationId = foundModel.id;
+ }
+ }
+ } catch (error) {
+ console.error("鍔犺浇瑙勬牸鍨嬪彿澶辫触:", error);
+ }
+ }
+
+ return {
+ productId: resolvedProductId,
+ product: productName,
+ specificationId: resolvedSpecificationId,
+ specification: product.specification || "",
+ quantity: product.quantity || 0,
+ unit: product.unit || "",
+ unitPrice: product.unitPrice || 0,
+ amount: product.amount || 0,
+ modelOptions: modelOptions, // 涓烘瘡琛屾坊鍔犵嫭绔嬬殑瑙勬牸鍨嬪彿鍒楄〃
+ };
+ })
+ )
+ : [];
+ form.subtotal = row.subtotal || 0;
+ form.freight = row.freight || 0;
+ form.otherFee = row.otherFee || 0;
+ form.discountRate = row.discountRate || 0;
+ form.discountAmount = row.discountAmount || 0;
+ form.totalAmount = row.totalAmount || 0;
+
+ // 鍙嶆樉瀹℃壒浜�
+ if (row.approveUserIds) {
+ const userIds = row.approveUserIds.split(",");
+ approverNodes.value = userIds.map((userId, idx) => ({
+ id: idx + 1,
+ userId: parseInt(userId.trim()),
+ }));
+ nextApproverId = userIds.length + 1;
+ } else {
+ approverNodes.value = [{ id: 1, userId: null }];
+ nextApproverId = 2;
+ }
+
+ // 鍔犺浇鐢ㄦ埛鍒楄〃
+ let userLists = await userListNoPage();
+ userList.value = (userLists.data || []).map(item => ({
+ userId: item.userId,
+ nickName: item.nickName || "",
+ userName: item.userName || "",
+ }));
+
+ dialogVisible.value = true;
+ };
+
+ const handleDelete = row => {
+ ElMessageBox.confirm("纭鍒犻櫎璇ユ姤浠峰崟鍚楋紵", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ const index = quotationList.value.findIndex(item => item.id === row.id);
+ if (index > -1) {
+ deleteQuotation(row.id).then(res => {
+ // console.log(res)
+ if (res.code === 200) {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ handleSearch();
+ }
+ });
+ // quotationList.value.splice(index, 1)
+ // pagination.total--
+ // ElMessage.success('鍒犻櫎鎴愬姛')
+ }
+ });
+ };
+
+ const resetForm = () => {
+ form.customer = "";
+ form.salesperson = "";
+ form.quotationDate = "";
+ form.validDate = "";
+ form.paymentMethod = "";
+ form.status = "鑽夌";
+ form.remark = "";
+ form.products = [];
+ form.subtotal = 0;
+ form.freight = 0;
+ form.otherFee = 0;
+ form.discountRate = 0;
+ form.discountAmount = 0;
+ form.totalAmount = 0;
+ };
+
+ const addProduct = () => {
+ form.products.push({
+ productId: "",
+ product: "",
+ productName: "",
+ specificationId: "",
+ specification: "",
+ quantity: 1,
+ unit: "",
+ unitPrice: 0,
+ amount: 0,
+ modelOptions: [], // 涓烘瘡琛屾坊鍔犵嫭绔嬬殑瑙勬牸鍨嬪彿鍒楄〃
+ });
+ };
+
+ const removeProduct = index => {
+ form.products.splice(index, 1);
+ calculateSubtotal();
+ };
+
+ const calculateAmount = product => {
+ product.amount = product.quantity * product.unitPrice;
+ calculateSubtotal();
+ };
+
+ const calculateSubtotal = () => {
+ form.subtotal = form.products.reduce(
+ (sum, product) => sum + product.amount,
+ 0
+ );
+ calculateTotal();
+ };
+
+ const calculateTotal = () => {
+ form.discountAmount = form.subtotal * (form.discountRate / 100);
+ form.totalAmount =
+ form.subtotal + form.freight + form.otherFee - form.discountAmount;
+ };
+
+ const handleCustomerChange = () => {
+ // 鍙互鏍规嵁瀹㈡埛淇℃伅鑷姩濉厖涓�浜涢粯璁ゅ��
+ };
+
+ const handleSubmit = () => {
+ formRef.value.validate(valid => {
+ if (valid) {
+ if (form.products.length === 0) {
+ ElMessage.warning("璇疯嚦灏戞坊鍔犱竴涓骇鍝�");
+ return;
+ }
+
+ // 瀹℃壒浜哄繀濉牎楠�
+ const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
+ if (hasEmptyApprover) {
+ ElMessage.error("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
+ return;
+ }
+
+ // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
+ form.approveUserIds = approverNodes.value
+ .map(node => node.userId)
+ .join(",");
+
+ // 璁$畻鎵�鏈変骇鍝佺殑鍗曚环鎬诲拰
+ form.totalAmount = form.products.reduce((sum, product) => {
+ const price = Number(product.unitPrice) || 0;
+ return sum + price;
+ }, 0);
+
+ if (isEdit.value) {
+ // 缂栬緫
+ const index = quotationList.value.findIndex(
+ item => item.id === editId.value
+ );
+ if (index > -1) {
+ updateQuotation(form).then(res => {
+ // console.log(res)
+ if (res.code === 200) {
+ ElMessage.success("缂栬緫鎴愬姛");
+ dialogVisible.value = false;
+ handleSearch();
+ }
+ });
+ }
+ } else {
+ // 鏂板
+ addQuotation(form).then(res => {
+ if (res.code === 200) {
+ ElMessage.success("鏂板鎴愬姛");
+ dialogVisible.value = false;
+ handleSearch();
+ }
+ });
+ }
+ }
+ });
+ };
+
+ const handleCurrentChange = val => {
+ pagination.currentPage = val.page;
+ pagination.pageSize = val.limit;
+ // 鍒嗛〉鍙樺寲鏃堕噸鏂版煡璇㈠垪琛�
+ handleSearch();
+ };
+ const handleSearch = () => {
+ const params = {
+ // 鍚庣鍒嗛〉鍙傛暟锛歝urrent / size
+ current: pagination.currentPage,
+ size: pagination.pageSize,
+ ...searchForm,
+ };
+ getQuotationList(params).then(res => {
+ // console.log(res)
+ if (res.code === 200) {
+ // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鎴栧叾浠栧璞℃斁鍏ュ搷搴斿紡瀵硅薄
+ quotationList.value = (res.data.records || []).map(item => ({
+ id: item.id,
+ quotationNo: item.quotationNo || "",
+ customer: item.customer || "",
+ salesperson: item.salesperson || "",
+ quotationDate: item.quotationDate || "",
+ validDate: item.validDate || "",
+ paymentMethod: item.paymentMethod || "",
+ status: item.status || "鑽夌",
+ // 瀹℃壒浜猴紙鐢ㄤ簬缂栬緫鏃跺弽鏄撅級
+ approveUserIds: item.approveUserIds || "",
+ remark: item.remark || "",
+ products: item.products
+ ? item.products.map(product => ({
+ productId: product.productId || "",
+ product: product.product || product.productName || "",
+ specificationId: product.specificationId || "",
+ specification: product.specification || "",
+ quantity: product.quantity || 0,
+ unit: product.unit || "",
+ unitPrice: product.unitPrice || 0,
+ amount: product.amount || 0,
+ }))
+ : [],
+ subtotal: item.subtotal || 0,
+ freight: item.freight || 0,
+ otherFee: item.otherFee || 0,
+ discountRate: item.discountRate || 0,
+ discountAmount: item.discountAmount || 0,
+ totalAmount: item.totalAmount || 0,
+ }));
+ pagination.total = res.data.total;
+ }
+ });
+ customerList().then(res => {
+ // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+ customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
+ id: item.id,
+ customerName: item.customerName || "",
+ taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || "",
+ }));
+ });
+ };
+
+ onMounted(() => {
+ handleSearch();
+ });
</script>
<style scoped lang="scss">
-.search-row {
- margin-bottom: 20px;
-}
-
-.quotation-form-container {
- padding: 10px 0;
- max-height: calc(100vh - 200px);
- overflow-y: auto;
-
- &::-webkit-scrollbar {
- width: 6px;
- height: 6px;
+ .search-row {
+ margin-bottom: 20px;
}
-
- &::-webkit-scrollbar-thumb {
- background: #c1c1c1;
- border-radius: 3px;
-
- &:hover {
- background: #a8a8a8;
+
+ .quotation-form-container {
+ padding: 10px 0;
+ max-height: calc(100vh - 200px);
+ overflow-y: auto;
+
+ &::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #c1c1c1;
+ border-radius: 3px;
+
+ &:hover {
+ background: #a8a8a8;
+ }
}
}
-}
-.quotation-form {
- .el-form-item {
- margin-bottom: 22px;
+ .quotation-form {
+ .el-form-item {
+ margin-bottom: 22px;
+ }
}
-}
-.form-card {
- margin-bottom: 24px;
- border-radius: 8px;
- transition: all 0.3s ease;
-
- &:hover {
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
- }
-
- :deep(.el-card__header) {
- padding: 16px 20px;
- background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
- border-bottom: 1px solid #ebeef5;
- }
-
- :deep(.el-card__body) {
- padding: 20px;
- }
-}
+ .form-card {
+ margin-bottom: 24px;
+ border-radius: 8px;
+ transition: all 0.3s ease;
-.card-header-wrapper {
- display: flex;
- align-items: center;
- gap: 8px;
-
- .card-icon {
- font-size: 18px;
- color: #409eff;
- }
-
- .card-title {
- font-weight: 600;
- font-size: 16px;
- color: #303133;
- flex: 1;
- }
-
- .header-btn {
- margin-left: auto;
- }
-}
+ &:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
+ }
-.form-content {
- padding: 8px 0;
-}
+ :deep(.el-card__header) {
+ padding: 16px 20px;
+ background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
+ border-bottom: 1px solid #ebeef5;
+ }
-.product-table-form-item {
- margin-bottom: 0;
- :deep(.el-form-item__content) {
- margin-left: 0 !important;
+ :deep(.el-card__body) {
+ padding: 20px;
+ }
}
- :deep(.el-form-item__label) {
- width: auto;
- min-width: auto;
- }
-}
-.approver-nodes-container {
- display: flex;
- flex-wrap: wrap;
- gap: 24px;
- padding: 12px 0;
-}
-
-.approver-node-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 12px;
- padding: 16px;
- background: #f8f9fa;
- border-radius: 8px;
- border: 1px solid #e4e7ed;
- transition: all 0.3s ease;
- min-width: 180px;
-
- &:hover {
- border-color: #409eff;
- background: #f0f7ff;
- box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
- }
-}
-
-.approver-node-label {
- display: flex;
- align-items: center;
- gap: 8px;
- font-size: 14px;
- color: #606266;
-
- .node-step {
- display: inline-flex;
+ .card-header-wrapper {
+ display: flex;
align-items: center;
- justify-content: center;
- width: 24px;
- height: 24px;
- background: #409eff;
- color: #fff;
- border-radius: 50%;
- font-size: 12px;
- font-weight: 600;
- }
-
- .node-text {
- font-weight: 500;
- }
-
- .arrow-icon {
- color: #909399;
- font-size: 14px;
- }
-}
+ gap: 8px;
-.approver-select {
- width: 100%;
- min-width: 150px;
-}
+ .card-icon {
+ font-size: 18px;
+ color: #409eff;
+ }
-.remove-btn {
- margin-top: 4px;
-}
-
-.product-table {
- :deep(.el-table__header) {
- background-color: #f5f7fa;
-
- th {
- background-color: #f5f7fa !important;
- color: #606266;
+ .card-title {
font-weight: 600;
+ font-size: 16px;
+ color: #303133;
+ flex: 1;
+ }
+
+ .header-btn {
+ margin-left: auto;
}
}
-
- :deep(.el-table__row) {
- &:hover {
- background-color: #f5f7fa;
+
+ .form-content {
+ padding: 8px 0;
+ }
+
+ .product-table-form-item {
+ margin-bottom: 0;
+ :deep(.el-form-item__content) {
+ margin-left: 0 !important;
+ }
+ :deep(.el-form-item__label) {
+ width: auto;
+ min-width: auto;
}
}
-
- :deep(.el-table__cell) {
+
+ .approver-nodes-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 24px;
padding: 12px 0;
}
-}
-.dialog-footer {
- text-align: right;
-}
-
-// 鍝嶅簲寮忎紭鍖�
-@media (max-width: 1200px) {
- .approver-nodes-container {
- gap: 16px;
- }
-
.approver-node-item {
- min-width: 160px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ padding: 16px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ border: 1px solid #e4e7ed;
+ transition: all 0.3s ease;
+ min-width: 180px;
+
+ &:hover {
+ border-color: #409eff;
+ background: #f0f7ff;
+ box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
+ }
}
-}
+
+ .approver-node-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ color: #606266;
+
+ .node-step {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ background: #409eff;
+ color: #fff;
+ border-radius: 50%;
+ font-size: 12px;
+ font-weight: 600;
+ }
+
+ .node-text {
+ font-weight: 500;
+ }
+
+ .arrow-icon {
+ color: #909399;
+ font-size: 14px;
+ }
+ }
+
+ .approver-select {
+ width: 100%;
+ min-width: 150px;
+ }
+
+ .remove-btn {
+ margin-top: 4px;
+ }
+
+ .product-table {
+ :deep(.el-table__header) {
+ background-color: #f5f7fa;
+
+ th {
+ background-color: #f5f7fa !important;
+ color: #606266;
+ font-weight: 600;
+ }
+ }
+
+ :deep(.el-table__row) {
+ &:hover {
+ background-color: #f5f7fa;
+ }
+ }
+
+ :deep(.el-table__cell) {
+ padding: 12px 0;
+ }
+ }
+
+ .dialog-footer {
+ text-align: right;
+ }
+
+ // 鍝嶅簲寮忎紭鍖�
+ @media (max-width: 1200px) {
+ .approver-nodes-container {
+ gap: 16px;
+ }
+
+ .approver-node-item {
+ min-width: 160px;
+ }
+ }
</style>
--
Gitblit v1.9.3