From ace1b66fe094e791216f6c46f4bd628e1c23b2dd Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期四, 08 十月 2026 17:09:01 +0800
Subject: [PATCH] 协同办公系统模块优化

---
 src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue |  719 ++++++++++++++++++++++-------------------------------------
 1 files changed, 274 insertions(+), 445 deletions(-)

diff --git a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
index 4c9fce3..0f26e65 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -1,466 +1,295 @@
 <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-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">
-            <!-- 鐢宠閮ㄩ棬锛氭牎楠屼娇鐢ㄩ儴闂↖D锛屼究浜庝笅鎷夐�夋嫨鍚庣珛鍗抽�氳繃鏍¢獙 -->
-            <el-form-item label="鐢宠閮ㄩ棬锛�" prop="approveDeptId">
-<!--              <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>
-          </el-col>
-        </el-row>
-        <!-- 璇峰亣鏃堕棿锛堜粎褰� 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>
-          </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>
-          </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>
-          </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>
-          </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>
-              </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>
-                    <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>
-                  <div>
-                    <el-button
-                      type="danger"
-                      size="small"
-                      @click="removeApproverNode(index)"
-                      v-if="approverNodes.length > 1"
-                    >鍒犻櫎</el-button>
-                  </div>
-                </div>
-              </div>
-            </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="閫夋嫨浜哄憳"
-                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>
-          </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'">
-                  <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>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitForm">纭</el-button>
-          <el-button @click="closeDia">鍙栨秷</el-button>
-        </div>
+  <el-dialog v-model="visible" :title="operationType === 'add' ? `鏂板${typeName}` : `缂栬緫${typeName}`" width="760px" @close="closeDialog">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+      <el-form-item label="娴佺▼缂栧彿">
+        <el-input v-model="form.approveId" disabled placeholder="鎻愪氦鍚庤嚜鍔ㄧ敓鎴�" />
+      </el-form-item>
+
+      <el-form-item v-if="approveTypeNumber === 3" label="閿�鍞悎鍚�" prop="businessId">
+        <el-select v-model="form.businessId" filterable placeholder="璇烽�夋嫨閿�鍞悎鍚�" style="width: 100%" @change="handleContractChange">
+          <el-option v-for="item in contractOptions" :key="item.id" :value="item.id"
+                     :label="`${item.salesContractNo || '-'} / ${item.customerName || '-'} / 楼${item.contractAmount || 0}`" />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item v-if="approveTypeNumber === 3 && selectedContract" label="鍚堝悓淇℃伅">
+        <el-descriptions :column="2" border style="width: 100%">
+          <el-descriptions-item label="瀹㈡埛">{{ selectedContract.customerName || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="椤圭洰">{{ selectedContract.projectName || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="涓氬姟鍛�">{{ selectedContract.salesman || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="鍚堝悓閲戦">楼{{ selectedContract.contractAmount || 0 }}</el-descriptions-item>
+        </el-descriptions>
+      </el-form-item>
+
+      <el-form-item v-if="[1, 5].includes(approveTypeNumber)" label="瀹℃壒鏍囬" prop="businessTitle">
+        <el-input v-model="form.businessTitle" maxlength="200" show-word-limit />
+      </el-form-item>
+
+      <template v-if="approveTypeNumber === 1">
+        <el-form-item label="浜嬮」绫诲瀷" prop="category">
+          <el-select v-model="form.category" style="width: 100%">
+            <el-option label="涓存椂璐圭敤鎶ラ攢" value="涓存椂璐圭敤鎶ラ攢" />
+            <el-option label="閮ㄩ棬鍗忎綔" value="閮ㄩ棬鍗忎綔" />
+            <el-option label="鍏朵粬" value="鍏朵粬" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="娑夊強閲戦">
+          <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%" />
+        </el-form-item>
       </template>
-    </el-dialog>
-  </div>
+
+      <template v-if="approveTypeNumber === 5">
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="閲囪喘绫诲埆" prop="category"><el-input v-model="form.category" /></el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绱ф�ョ▼搴�" prop="urgency">
+              <el-select v-model="form.urgency" style="width: 100%">
+                <el-option label="鏅��" value="鏅��" /><el-option label="绱ф��" value="绱ф��" /><el-option label="鐗规��" value="鐗规��" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="閲囪喘鍐呭" prop="purchaseContent"><el-input v-model="form.purchaseContent" type="textarea" /></el-form-item>
+        <el-row :gutter="16">
+          <el-col :span="8"><el-form-item label="鏁伴噺" prop="quantity"><el-input-number v-model="form.quantity" :min="0.01" :precision="2" style="width: 100%" /></el-form-item></el-col>
+          <el-col :span="8"><el-form-item label="鍗曚綅" prop="unit"><el-input v-model="form.unit" /></el-form-item></el-col>
+          <el-col :span="8"><el-form-item label="棰勭畻閲戦" prop="amount"><el-input-number v-model="form.amount" :min="0.01" :precision="2" style="width: 100%" /></el-form-item></el-col>
+        </el-row>
+        <el-form-item label="鏈熸湜鍒拌揣鏃ユ湡" prop="expectedDate">
+          <el-date-picker v-model="form.expectedDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
+        </el-form-item>
+      </template>
+
+      <el-row v-if="approveTypeNumber === 2" :gutter="16">
+        <el-col :span="12"><el-form-item label="璇峰亣寮�濮嬫棩鏈�" prop="startDate"><el-date-picker v-model="form.startDate" type="date" value-format="YYYY-MM-DD" 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" value-format="YYYY-MM-DD" style="width: 100%" /></el-form-item></el-col>
+      </el-row>
+      <el-form-item v-if="approveTypeNumber === 4" label="鎶ラ攢閲戦" prop="amount">
+        <el-input-number v-model="form.amount" :min="0.01" :precision="2" style="width: 100%" />
+      </el-form-item>
+
+      <el-form-item label="鐢宠浜嬬敱" prop="approveReason">
+        <el-input v-model="form.approveReason" type="textarea" :rows="3" maxlength="1000" show-word-limit />
+      </el-form-item>
+
+      <el-form-item label="鐢宠閮ㄩ棬" prop="approveDeptId">
+        <el-select v-model="form.approveDeptId" style="width: 100%">
+          <el-option v-for="dept in departmentOptions" :key="dept.deptId" :label="dept.deptName" :value="dept.deptId" />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item v-if="[3, 5].includes(approveTypeNumber) && flowOptions.length" label="棰勮瀹℃壒娴佺▼">
+        <el-select v-model="form.flowDefinitionId" clearable placeholder="璇烽�夋嫨棰勮娴佺▼锛涚暀绌哄彲鎵嬪姩璁剧疆鑺傜偣" style="width: 100%">
+          <el-option v-for="flow in flowOptions" :key="flow.id" :label="flow.flowName" :value="flow.id" />
+        </el-select>
+      </el-form-item>
+
+      <el-divider content-position="left">瀹℃壒鑺傜偣</el-divider>
+      <el-alert v-if="form.flowDefinitionId && operationType === 'add'" title="鎻愪氦鍚庡皢鎸夐璁炬祦绋嬬敓鎴愬鎵硅妭鐐�" type="info" :closable="false" style="margin-bottom: 12px" />
+      <div v-for="(node, index) in approverNodes" v-show="!form.flowDefinitionId || operationType === 'edit'" :key="node.key" class="node-row">
+        <span class="node-order">{{ index + 1 }}</span>
+        <el-input v-model="node.nodeName" placeholder="鑺傜偣鍚嶇О" style="width: 150px" />
+        <el-select v-model="node.userId" filterable placeholder="瀹℃壒浜�" style="width: 150px">
+          <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" />
+        </el-select>
+        <el-select v-model="node.conditionOperator" style="width: 130px" @change="handleConditionChange(node)">
+          <el-option label="濮嬬粓鎵ц" value="ALWAYS" />
+          <el-option label="閲戦澶т簬" value="GT" />
+          <el-option label="閲戦澶т簬绛変簬" value="GTE" />
+          <el-option label="閲戦灏忎簬" value="LT" />
+          <el-option label="閲戦灏忎簬绛変簬" value="LTE" />
+        </el-select>
+        <el-input-number v-if="node.conditionOperator !== 'ALWAYS'" v-model="node.conditionValue" :min="0" :precision="2" style="width: 130px" />
+        <el-button type="danger" link :disabled="approverNodes.length === 1" @click="removeNode(index)">鍒犻櫎</el-button>
+        <el-button link :disabled="index === 0" @click="moveNode(index, -1)">涓婄Щ</el-button>
+        <el-button link :disabled="index === approverNodes.length - 1" @click="moveNode(index, 1)">涓嬬Щ</el-button>
+      </div>
+      <el-button v-if="!form.flowDefinitionId || operationType === 'edit'" type="primary" plain class="add-node" @click="addNode">鏂板瀹℃壒鑺傜偣</el-button>
+
+      <el-form-item label="鐢宠鏃ユ湡">
+        <el-date-picker v-model="form.approveTime" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
+      </el-form-item>
+      <el-form-item label="闄勪欢鏉愭枡">
+        <el-upload v-model:file-list="fileList" :action="upload.url" :headers="upload.headers" multiple
+                   :before-upload="beforeUpload" :on-success="uploadSuccess" :on-error="uploadError" :on-remove="removeFile">
+          <el-button type="primary">涓婁紶鏂囦欢</el-button>
+        </el-upload>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="closeDialog">鍙栨秷</el-button>
+      <el-button type="primary" :loading="submitting" @click="submit">鎻愪氦</el-button>
+    </template>
+  </el-dialog>
 </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()
+import { computed, getCurrentInstance, reactive, ref } from 'vue'
+import { approveFlowDefinitionList, approveProcessAdd, approveProcessDetails, approveProcessGetInfo, approveProcessUpdate, getDept } from '@/api/collaborativeApproval/approvalProcess'
+import { delLedgerFile, ledgerListNoPage } from '@/api/salesManagement/salesLedger'
+import { userListNoPageByTenantId } from '@/api/system/user'
+import { getToken } from '@/utils/auth'
+import { getCurrentDate } from '@/utils'
+import useUserStore from '@/store/modules/user'
+
+const props = defineProps({ approveType: { type: [Number, String], default: 1 } })
 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 { proxy } = getCurrentInstance()
+const userStore = useUserStore()
+const approveTypeNumber = computed(() => Number(props.approveType))
+const typeName = computed(() => ({ 1: '鑷敱鍗忓悓瀹℃壒', 3: '閿�鍞鎵�', 5: '閲囪喘瀹℃壒' }[approveTypeNumber.value] || '瀹℃壒娴佺▼'))
 
-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" }],
-    // 浣跨敤閮ㄩ棬ID鍋氬繀濉牎楠岋紝閬垮厤鍚嶇О鏈悓姝ュ鑷磋鎶�
-    approveDeptId: [{ required: true, message: "璇烽�夋嫨鐢宠閮ㄩ棬", trigger: "change" }],
-    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 visible = ref(false)
+const submitting = ref(false)
+const operationType = ref('add')
+const formRef = ref()
 const userList = ref([])
-function addApproverNode() {
-  approverNodes.value.push({ id: nextApproverId++, userId: null })
+const departmentOptions = ref([])
+const contractOptions = ref([])
+const flowOptions = ref([])
+const selectedContract = ref(null)
+const fileList = ref([])
+let nodeKey = 1
+const approverNodes = ref([])
+const upload = reactive({ url: import.meta.env.VITE_APP_BASE_API + '/file/upload', headers: { Authorization: 'Bearer ' + getToken() } })
+const emptyForm = () => ({
+  id: null, approveId: '', approveUser: userStore.id, approveDeptId: userStore.currentDeptId,
+  approveTime: getCurrentDate(), approveReason: '', businessId: null, businessNo: '', businessTitle: '',
+  amount: null, businessData: '', flowDefinitionId: null, category: '', urgency: '鏅��', purchaseContent: '', quantity: null,
+  unit: '', expectedDate: '', startDate: '', endDate: '', tempFileIds: []
+})
+const form = ref(emptyForm())
+const rules = {
+  approveDeptId: [{ required: true, message: '璇烽�夋嫨鐢宠閮ㄩ棬', trigger: 'change' }],
+  approveReason: [{ required: true, message: '璇疯緭鍏ョ敵璇蜂簨鐢�', trigger: 'blur' }],
+  businessId: [{ required: true, message: '璇烽�夋嫨閿�鍞悎鍚�', trigger: 'change' }],
+  businessTitle: [{ required: true, message: '璇疯緭鍏ュ鎵规爣棰�', trigger: 'blur' }],
+  category: [{ required: true, message: '璇疯緭鍏ユ垨閫夋嫨绫诲埆', trigger: 'blur' }],
+  urgency: [{ required: true, message: '璇烽�夋嫨绱ф�ョ▼搴�', trigger: 'change' }],
+  purchaseContent: [{ required: true, message: '璇疯緭鍏ラ噰璐唴瀹�', trigger: 'blur' }],
+  quantity: [{ required: true, message: '璇疯緭鍏ユ暟閲�', trigger: 'blur' }],
+  unit: [{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: 'blur' }],
+  amount: [{ required: true, message: '璇疯緭鍏ラ绠楅噾棰�', trigger: 'blur' }],
+  expectedDate: [{ required: true, message: '璇烽�夋嫨鏈熸湜鍒拌揣鏃ユ湡', trigger: 'change' }],
+  startDate: [{ required: true, message: '璇烽�夋嫨璇峰亣寮�濮嬫棩鏈�', trigger: 'change' }],
+  endDate: [{ required: true, message: '璇烽�夋嫨璇峰亣缁撴潫鏃ユ湡', trigger: 'change' }]
 }
-function removeApproverNode(index) {
-  approverNodes.value.splice(index, 1)
+
+const createNode = (data = {}) => ({ key: nodeKey++, userId: data.userId || null, nodeName: data.nodeName || '瀹℃壒', conditionField: data.conditionField || null, conditionOperator: data.conditionOperator || 'ALWAYS', conditionValue: data.conditionValue == null ? null : Number(data.conditionValue) })
+const addNode = () => approverNodes.value.push(createNode())
+const removeNode = index => approverNodes.value.splice(index, 1)
+const moveNode = (index, offset) => {
+  const target = index + offset
+  if (target < 0 || target >= approverNodes.value.length) return
+  const item = approverNodes.value.splice(index, 1)[0]
+  approverNodes.value.splice(target, 0, item)
 }
-// 澶勭悊閮ㄩ棬閫夋嫨鍙樺寲
-const handleDeptChange = (deptId) => {
-  if (deptId) {
-    const selectedDept = productOptions.value.find(dept => dept.deptId === deptId);
-    if (selectedDept) {
-      form.value.approveDeptName = selectedDept.deptName;
-    }
+const handleConditionChange = node => {
+  node.conditionField = node.conditionOperator === 'ALWAYS' ? null : 'amount'
+  if (node.conditionOperator === 'ALWAYS') node.conditionValue = null
+}
+
+const loadOptions = async () => {
+  const tasks = [userListNoPageByTenantId(), getDept()]
+  if (approveTypeNumber.value === 3) tasks.push(ledgerListNoPage({}))
+  const flowPromise = [3, 5].includes(approveTypeNumber.value) ? approveFlowDefinitionList({ approveType: approveTypeNumber.value }) : Promise.resolve({ data: [] })
+  tasks.push(flowPromise)
+  const results = await Promise.all(tasks)
+  userList.value = results[0].data || []
+  departmentOptions.value = results[1].data || []
+  if (approveTypeNumber.value === 3) {
+    contractOptions.value = results[2]?.data || []
+    flowOptions.value = results[3]?.data || []
   } else {
-    form.value.approveDeptName = '';
+    contractOptions.value = []
+    flowOptions.value = results[2]?.data || []
   }
-};
-// 鎵撳紑寮规
-const openDialog = (type, row) => {
-  operationType.value = type;
-  dialogFormVisible.value = true;
-	userListNoPageByTenantId().then((res) => {
-    userList.value = res.data;
-  });
-  getProductOptions();
-	form.value = {}
-	approverNodes.value = [
-		{ id: 1, userId: null }
-	]
-  form.value.approveUser = userStore.id;
-  form.value.approveTime = getCurrentDate();
-  
-  // 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅骞惰缃儴闂↖D
-  form.value.approveDeptId = userStore.currentDeptId
-  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 defaultFlow = flowOptions.value.find(item => item.isDefault === 1 && item.enabled === 1)
+  if (defaultFlow && operationType.value === 'add') form.value.flowDefinitionId = defaultFlow.id
 }
-const getProductOptions = () => {
-  getDept().then((res) => {
-    productOptions.value = res.data;
-  });
-};
-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 = () => {
+
+const openDialog = async (type, row) => {
+  operationType.value = type
+  visible.value = true
+  form.value = emptyForm()
+  approverNodes.value = [createNode()]
   fileList.value = []
-  proxy.resetForm("formRef");
-  dialogFormVisible.value = false;
-  emit('close')
-};
-
-// 涓婁紶鍓嶆牎妫�
-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 = [];
+  selectedContract.value = null
+  await loadOptions()
+  if (type === 'edit' && row?.approveId) {
+    const [processRes, nodeRes] = await Promise.all([approveProcessGetInfo({ id: row.approveId }), approveProcessDetails(row.approveId)])
+    const process = processRes.data || {}
+    let business = {}
+    try { business = process.businessData ? JSON.parse(process.businessData) : {} } catch (_) { business = {} }
+    form.value = { ...emptyForm(), ...process, ...business, tempFileIds: [] }
+    fileList.value = process.commonFileList || []
+    approverNodes.value = (nodeRes.data || []).map(item => createNode({
+      userId: item.approveNodeUserId,
+      nodeName: item.approveNodeName,
+      conditionField: item.conditionField,
+      conditionOperator: item.conditionOperator,
+      conditionValue: item.conditionValue
+    }))
+    if (!approverNodes.value.length) approverNodes.value = [createNode()]
+    if (approveTypeNumber.value === 3) {
+      selectedContract.value = contractOptions.value.find(item => item.id === process.businessId) || business
     }
-    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,
-});
+const handleContractChange = id => {
+  selectedContract.value = contractOptions.value.find(item => item.id === id) || null
+  if (selectedContract.value) form.value.businessTitle = `閿�鍞悎鍚屽鎵�-${selectedContract.value.salesContractNo || ''}`
+}
+
+const buildBusinessData = () => {
+  if (approveTypeNumber.value === 5) return JSON.stringify({ category: form.value.category, urgency: form.value.urgency, purchaseContent: form.value.purchaseContent, quantity: form.value.quantity, unit: form.value.unit, expectedDate: form.value.expectedDate })
+  if (approveTypeNumber.value === 1) return JSON.stringify({ category: form.value.category })
+  if (approveTypeNumber.value === 2) return JSON.stringify({ startDate: form.value.startDate, endDate: form.value.endDate })
+  if (approveTypeNumber.value === 4) return JSON.stringify({ amount: form.value.amount })
+  return form.value.businessData || ''
+}
+
+const submit = async () => {
+  await formRef.value.validate()
+  if (approveTypeNumber.value === 2 && new Date(form.value.endDate) < new Date(form.value.startDate)) return proxy.$modal.msgError('璇峰亣缁撴潫鏃ユ湡涓嶈兘鏃╀簬寮�濮嬫棩鏈�')
+  const usePresetFlow = operationType.value === 'add' && !!form.value.flowDefinitionId
+  if (!usePresetFlow && approverNodes.value.some(item => !item.userId || !item.nodeName)) return proxy.$modal.msgError('璇峰畬鏁村~鍐欏鎵硅妭鐐�')
+  if (!usePresetFlow && approverNodes.value.some((item, index) => index > 0 && item.userId === approverNodes.value[index - 1].userId)) return proxy.$modal.msgError('鐩搁偦瀹℃壒鑺傜偣涓嶈兘閫夋嫨鍚屼竴瀹℃壒浜�')
+  if (!usePresetFlow && approverNodes.value.some(item => item.conditionOperator !== 'ALWAYS' && item.conditionValue == null)) return proxy.$modal.msgError('璇峰~鍐欒妭鐐规潯浠堕噾棰�')
+  const nodes = usePresetFlow ? [] : approverNodes.value.map(item => ({ userId: item.userId, nodeName: item.nodeName, conditionField: item.conditionOperator === 'ALWAYS' ? null : 'amount', conditionOperator: item.conditionOperator, conditionValue: item.conditionValue == null ? null : String(item.conditionValue) }))
+  const payload = { ...form.value, approveType: approveTypeNumber.value, approveUser: userStore.id, nodes, approveUserIds: nodes.map(item => item.userId).join(','), businessData: buildBusinessData() }
+  submitting.value = true
+  try {
+    if (operationType.value === 'add') await approveProcessAdd(payload)
+    else await approveProcessUpdate(payload)
+    proxy.$modal.msgSuccess('鎻愪氦鎴愬姛')
+    closeDialog()
+  } finally {
+    submitting.value = false
+  }
+}
+
+const beforeUpload = () => { proxy.$modal.loading('姝e湪涓婁紶鏂囦欢锛岃绋嶅��...'); return true }
+const uploadSuccess = (res, file) => {
+  proxy.$modal.closeLoading()
+  if (res.code === 200) form.value.tempFileIds.push(res.data.tempId)
+  else { proxy.$modal.msgError(res.msg); proxy.$refs.fileUpload?.handleRemove(file) }
+}
+const uploadError = () => { proxy.$modal.closeLoading(); proxy.$modal.msgError('涓婁紶澶辫触') }
+const removeFile = file => {
+  if (file.id) delLedgerFile([file.id])
+  if (file.response?.data?.tempId) form.value.tempFileIds = form.value.tempFileIds.filter(id => id !== file.response.data.tempId)
+}
+const closeDialog = () => { visible.value = false; emit('close') }
+
+defineExpose({ openDialog })
 </script>
 
 <style scoped>
-
-</style>
\ No newline at end of file
+.node-row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
+.node-order { display: inline-flex; width: 26px; height: 26px; align-items: center; justify-content: center; border-radius: 50%; color: #fff; background: #409eff; }
+.add-node { margin: 0 0 20px 120px; }
+</style>

--
Gitblit v1.9.3