From a563ea879ef5fb6897e76d2df661e465dce2ab9b Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期一, 01 六月 2026 15:02:27 +0800
Subject: [PATCH] Merge branch 'dev_新疆_大罗素马铃薯new' of http://114.132.189.42:9002/r/product-inventory-management into dev_新疆_大罗素马铃薯new

---
 src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue |  347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 347 insertions(+), 0 deletions(-)

diff --git a/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
new file mode 100644
index 0000000..f731d57
--- /dev/null
+++ b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
@@ -0,0 +1,347 @@
+<!--OA妯″潡锛氳皟宀楃敵璇�-->
+<template>
+  <div class="app-container">
+    <div class="search_form mb20">
+      <div>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
+        <el-select
+          v-model="searchForm.applicantId"
+          filterable
+          remote
+          clearable
+          reserve-keyword
+          placeholder="璇烽�夋嫨鎴栨悳绱㈢敵璇蜂汉"
+          style="width: 220px"
+          :remote-method="remoteSearchApplicant"
+          :loading="applicantSearchLoading"
+        >
+          <el-option
+            v-for="u in applicantSearchOptions"
+            :key="u.userId"
+            :label="userSelectLabel(u)"
+            :value="u.userId"
+          />
+        </el-select>
+        <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
+        <el-button @click="resetSearch">閲嶇疆</el-button>
+      </div>
+      <div>
+        <el-button type="primary" @click="openAddWithTemplate">鏂板璋冨矖鐢宠</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable
+        rowKey="id"
+        :column="tableColumn"
+        :tableData="tableData"
+        :page="page"
+        :isSelection="false"
+        :tableLoading="tableLoading"
+        @pagination="onPagination"
+        :total="page.total"
+      />
+    </div>
+
+    <ApprovalInstanceSubmitDialog
+      v-model="submitDialog.visible"
+      :title="submitDialogTitle"
+      :form="submitForm"
+      :rules="submitFormRules"
+      :fields="submitFormFields"
+      :active-template="activeTemplate"
+      :user-options="flowUserOptions"
+      :is-edit="isSubmitEdit"
+      :saving="submitSaving"
+      :form-ref="submitFormRef"
+      flow-attachments-only
+      @submit="onSubmit"
+    >
+      <template #before="{ form, fields }">
+        <FormPayloadFields :fields="displayTemplateFields(fields)" :form-payload="form.formPayload" />
+        <el-form-item label="鍘熷矖浣�">
+          <el-input :model-value="originalPostName" placeholder="閫夋嫨鐢宠浜哄悗鑷姩甯﹀嚭" disabled />
+        </el-form-item>
+      </template>
+    </ApprovalInstanceSubmitDialog>
+
+    <ApprovalTemplateBindDialog
+      v-model:visible="templateBindVisible"
+      :module-key="APPROVAL_MODULE_KEYS.TRANSFER"
+      skip-form-confirm
+      @confirm="onTemplateBound"
+      @closed="onTemplateBindClosed"
+    />
+
+    <ApprovalInstanceDetailDialog
+      v-model="detailDialog.visible"
+      title="璋冨矖鐢宠璇︽儏"
+      :row="detailRow"
+      @edit="openEditFromDetail"
+    />
+  </div>
+</template>
+
+<script setup>
+import { findPostOptions } from "@/api/system/post.js";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import { ElMessage } from "element-plus";
+import { computed, onMounted, reactive, ref, watch } from "vue";
+import FormPayloadFields from "../../ApproveManage/approve-list/components/FormPayloadFields.vue";
+import ApprovalInstanceDetailDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceDetailDialog.vue";
+import ApprovalInstanceSubmitDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue";
+import ApprovalTemplateBindDialog from "../../ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue";
+import { buildInstanceTableColumns } from "../../ApproveManage/approve-shared/approvalInstanceFormConfigTable.js";
+import { APPROVAL_MODULE_KEYS } from "../../ApproveManage/approve-shared/approvalModuleRegistry.js";
+import { useApprovalInstanceModule } from "../../ApproveManage/approve-shared/useApprovalInstanceModule.js";
+import { useFlowUserOptions } from "../../ApproveManage/approve-shared/useFlowUserOptions.js";
+import { SELECT_OPTION_SOURCE } from "../../ApproveManage/approve-template/selectOptionSource.js";
+
+function isOriginalPostField(field) {
+  const label = String(field?.label || "");
+  return (
+    label.includes("鍘熷矖浣�") ||
+    field?.key === "originalPost" ||
+    field?.key === "originalPostName" ||
+    field?.key === "originalPostId"
+  );
+}
+
+function displayTemplateFields(fields = []) {
+  return (fields || []).filter((f) => !isOriginalPostField(f));
+}
+
+function findApplicantTemplateField(fields = []) {
+  return (
+    fields.find((f) => String(f?.label || "").includes("鐢宠浜�")) ||
+    fields.find((f) => f?.type === "select" && f?.optionSource === SELECT_OPTION_SOURCE.USER) ||
+    null
+  );
+}
+
+const searchForm = reactive({
+  instanceNo: "",
+  applicantId: "",
+});
+
+const mod = useApprovalInstanceModule({
+  moduleKey: APPROVAL_MODULE_KEYS.TRANSFER,
+});
+
+const {
+  tableData,
+  tableLoading,
+  page,
+  detailDialog,
+  detailRow,
+  submitDialog,
+  submitForm,
+  submitFormRef,
+  submitSaving,
+  isSubmitEdit,
+  activeTemplate,
+  submitFormFields,
+  submitFormRules,
+  submitDialogTitle,
+  templateBindVisible,
+  handleQuery,
+  initModuleList,
+  pagination,
+  openAddWithTemplate,
+  onTemplateBound,
+  onTemplateBindClosed,
+  openEditFromDetail,
+  submitInstanceForm,
+  buildTableActions,
+} = mod;
+
+const { flowUserOptions, loadFlowUsers } = useFlowUserOptions();
+
+const allUsersCache = ref([]);
+const postIdToName = ref({});
+const targetPostOptions = ref([]);
+const applicantSearchLoading = ref(false);
+const applicantSearchOptions = ref([]);
+const originalPostName = ref("");
+
+const applicantTemplateField = computed(() =>
+  findApplicantTemplateField(submitForm.formFieldDefs)
+);
+
+function unwrapArray(payload) {
+  if (Array.isArray(payload)) return payload;
+  if (payload && Array.isArray(payload.data)) return payload.data;
+  if (payload && Array.isArray(payload.rows)) return payload.rows;
+  return [];
+}
+
+function isActiveUser(u) {
+  if (u.delFlag === "2" || u.delFlag === 2) return false;
+  if (u.status == null) return true;
+  return String(u.status) === "0";
+}
+
+function userSelectLabel(u) {
+  const nick = u.nickName || "";
+  const name = u.userName || "";
+  if (nick && name && nick !== name) return `${nick}锛�${name}锛塦;
+  return nick || name || `鐢ㄦ埛${u.userId ?? u.id ?? ""}`;
+}
+
+function firstPostId(user) {
+  if (!user) return undefined;
+  if (Array.isArray(user.postIds) && user.postIds.length) return user.postIds[0];
+  if (user.postId != null && user.postId !== "") return user.postId;
+  return undefined;
+}
+
+function resolveOriginalPost(user) {
+  if (!user) return { originalPostName: "" };
+  const nameStr = (user.postName ?? user.postname ?? "").toString().trim();
+  if (nameStr) return { originalPostName: nameStr };
+  if (Array.isArray(user.posts) && user.posts.length) {
+    return { originalPostName: (user.posts[0].postName ?? "").toString() || "鏈懡鍚嶅矖浣�" };
+  }
+  const pid = firstPostId(user);
+  if (pid != null && pid !== "") {
+    const n = postIdToName.value[String(pid)] || "";
+    return { originalPostName: n || "褰撳墠宀椾綅锛堟湭鍦ㄥ矖浣嶅瓧鍏镐腑锛�" };
+  }
+  return { originalPostName: "鏈垎閰嶅矖浣�" };
+}
+
+function userById(id) {
+  if (id == null || id === "") return undefined;
+  return allUsersCache.value.find((u) => String(u.userId ?? u.id) === String(id));
+}
+
+function filterUsersByQuery(query) {
+  const list = allUsersCache.value.filter((u) => isActiveUser(u));
+  const q = (query || "").trim().toLowerCase();
+  if (!q) return [...list];
+  return list.filter((u) => {
+    const nick = (u.nickName || "").toLowerCase();
+    const uname = (u.userName || "").toLowerCase();
+    const phone = (u.phonenumber || u.phone || "").toString();
+    return nick.includes(q) || uname.includes(q) || phone.includes(q);
+  });
+}
+
+async function loadUserPool() {
+  try {
+    const res = await userListNoPageByTenantId();
+    allUsersCache.value = unwrapArray(res);
+  } catch {
+    allUsersCache.value = [];
+  }
+}
+
+async function loadPostOptions() {
+  try {
+    const res = await findPostOptions();
+    const rows = res.data ?? res.rows ?? [];
+    targetPostOptions.value = Array.isArray(rows) ? rows : [];
+  } catch {
+    targetPostOptions.value = [];
+  }
+  const m = {};
+  for (const p of targetPostOptions.value) {
+    const id = p.postId ?? p.value ?? p.id;
+    if (id != null && id !== "") m[String(id)] = p.postName ?? p.label ?? "";
+  }
+  postIdToName.value = m;
+}
+
+async function remoteSearchApplicant(query) {
+  applicantSearchLoading.value = true;
+  try {
+    if (!allUsersCache.value.length) await loadUserPool();
+    applicantSearchOptions.value = filterUsersByQuery(query);
+  } finally {
+    applicantSearchLoading.value = false;
+  }
+}
+
+function syncOriginalPostFromApplicant(uid) {
+  const u = userById(uid);
+  originalPostName.value = resolveOriginalPost(u).originalPostName;
+}
+
+watch(
+  () => {
+    const key = applicantTemplateField.value?.key;
+    return key ? submitForm.formPayload[key] : undefined;
+  },
+  async (uid) => {
+    if (!applicantTemplateField.value) return;
+    if (!allUsersCache.value.length) await loadUserPool();
+    syncOriginalPostFromApplicant(uid);
+  }
+);
+
+watch(
+  () => submitDialog.visible,
+  async (v) => {
+    if (!v) return;
+    const key = applicantTemplateField.value?.key;
+    if (key && submitForm.formPayload[key]) {
+      syncOriginalPostFromApplicant(submitForm.formPayload[key]);
+    }
+  }
+);
+
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+  moduleKey: APPROVAL_MODULE_KEYS.TRANSFER,
+});
+
+function onSearch() {
+  handleQuery(searchForm);
+}
+
+async function resetSearch() {
+  searchForm.instanceNo = "";
+  searchForm.applicantId = "";
+  onSearch();
+  await remoteSearchApplicant("");
+}
+
+function onPagination(obj) {
+  pagination(obj, searchForm);
+}
+
+async function onSubmit() {
+  const ok = await submitInstanceForm({ skipValidate: true });
+  if (ok) ElMessage.success(isSubmitEdit.value ? "淇敼鎴愬姛" : "鎻愪氦鎴愬姛");
+}
+
+onMounted(async () => {
+  await Promise.all([loadUserPool(), loadPostOptions()]);
+  loadFlowUsers();
+  await remoteSearchApplicant("");
+  await initModuleList(searchForm);
+});
+</script>
+
+<style scoped>
+.mb20 {
+  margin-bottom: 20px;
+}
+.search_form {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+.search_title {
+  font-size: 14px;
+  color: var(--el-text-color-regular);
+}
+</style>

--
Gitblit v1.9.3