From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/views/bpm/oa/leave/create.vue |  257 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 257 insertions(+), 0 deletions(-)

diff --git a/src/views/bpm/oa/leave/create.vue b/src/views/bpm/oa/leave/create.vue
new file mode 100644
index 0000000..b64f4ca
--- /dev/null
+++ b/src/views/bpm/oa/leave/create.vue
@@ -0,0 +1,257 @@
+<template>
+  <el-row :gutter="20">
+    <el-col :span="16">
+      <ContentWrap title="鐢宠淇℃伅">
+        <el-form
+          ref="formRef"
+          v-loading="formLoading"
+          :model="formData"
+          :rules="formRules"
+          label-width="80px"
+        >
+          <el-form-item label="璇峰亣绫诲瀷" prop="type">
+            <el-select v-model="formData.type" clearable placeholder="璇烽�夋嫨璇峰亣绫诲瀷">
+              <el-option
+                v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="寮�濮嬫椂闂�" prop="startTime">
+            <el-date-picker
+              v-model="formData.startTime"
+              clearable
+              placeholder="璇烽�夋嫨寮�濮嬫椂闂�"
+              type="datetime"
+              value-format="x"
+            />
+          </el-form-item>
+          <el-form-item label="缁撴潫鏃堕棿" prop="endTime">
+            <el-date-picker
+              v-model="formData.endTime"
+              clearable
+              placeholder="璇烽�夋嫨缁撴潫鏃堕棿"
+              type="datetime"
+              value-format="x"
+            />
+          </el-form-item>
+          <el-form-item label="鍘熷洜" prop="reason">
+            <el-input v-model="formData.reason" placeholder="璇疯緭鍏ヨ鍋囧師鍥�" type="textarea" />
+          </el-form-item>
+          <el-form-item>
+            <el-button :disabled="formLoading" type="primary" @click="submitForm">
+              纭� 瀹�
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </ContentWrap>
+    </el-col>
+
+    <!-- 瀹℃壒鐩稿叧锛氭祦绋嬩俊鎭� -->
+    <el-col :span="8">
+      <ContentWrap title="瀹℃壒娴佺▼" :bodyStyle="{ padding: '0 20px 0' }">
+        <ProcessInstanceTimeline
+          ref="timelineRef"
+          :activity-nodes="activityNodes"
+          :show-status-icon="false"
+          @select-user-confirm="selectUserConfirm"
+        />
+      </ContentWrap>
+    </el-col>
+  </el-row>
+</template>
+<script lang="ts" setup>
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import * as LeaveApi from '@/api/bpm/leave'
+import { useTagsViewStore } from '@/store/modules/tagsView'
+
+// 瀹℃壒鐩稿叧锛歩mport
+import * as DefinitionApi from '@/api/bpm/definition'
+import ProcessInstanceTimeline from '@/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue'
+import * as ProcessInstanceApi from '@/api/bpm/processInstance'
+import { CandidateStrategy, NodeId } from '@/components/SimpleProcessDesignerV2/src/consts'
+import { ApprovalNodeInfo } from '@/api/bpm/processInstance'
+
+defineOptions({ name: 'BpmOALeaveCreate' })
+
+const message = useMessage() // 娑堟伅寮圭獥
+const { delView } = useTagsViewStore() // 瑙嗗浘鎿嶄綔
+const { push, currentRoute } = useRouter() // 璺敱
+const { query } = useRoute() // 鏌ヨ鍙傛暟
+
+const formLoading = ref(false) // 琛ㄥ崟鐨勫姞杞戒腑锛�1锛変慨鏀规椂鐨勬暟鎹姞杞斤紱2锛夋彁浜ょ殑鎸夐挳绂佺敤
+const formData = ref({
+  type: undefined,
+  reason: undefined,
+  startTime: undefined,
+  endTime: undefined
+})
+const formRules = reactive({
+  type: [{ required: true, message: '璇峰亣绫诲瀷涓嶈兘涓虹┖', trigger: 'blur' }],
+  reason: [{ required: true, message: '璇峰亣鍘熷洜涓嶈兘涓虹┖', trigger: 'change' }],
+  startTime: [{ required: true, message: '璇峰亣寮�濮嬫椂闂翠笉鑳戒负绌�', trigger: 'change' }],
+  endTime: [{ required: true, message: '璇峰亣缁撴潫鏃堕棿涓嶈兘涓虹┖', trigger: 'change' }]
+})
+const formRef = ref() // 琛ㄥ崟 Ref
+
+// 瀹℃壒鐩稿叧锛氬彉閲�
+const processDefineKey = 'oa_leave' // 娴佺▼瀹氫箟 Key
+const startUserSelectTasks = ref([]) // 鍙戣捣浜洪渶瑕侀�夋嫨瀹℃壒浜虹殑鐢ㄦ埛浠诲姟鍒楄〃
+const startUserSelectAssignees = ref({}) // 鍙戣捣浜洪�夋嫨瀹℃壒浜虹殑鏁版嵁
+const tempStartUserSelectAssignees = ref({}) // 鍘嗗彶鍙戣捣浜洪�夋嫨瀹℃壒浜虹殑鏁版嵁锛岀敤浜庢瘡娆¤〃鍗曞彉鏇存椂锛屼复鏃朵繚瀛�
+const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 瀹℃壒鑺傜偣淇℃伅
+const processDefinitionId = ref('')
+
+/** 鎻愪氦琛ㄥ崟 */
+const submitForm = async () => {
+  // 1.1 鏍¢獙琛ㄥ崟
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 1.2 瀹℃壒鐩稿叧锛氭牎楠屾寚瀹氬鎵逛汉
+  if (startUserSelectTasks.value?.length > 0) {
+    for (const userTask of startUserSelectTasks.value) {
+      if (
+        Array.isArray(startUserSelectAssignees.value[userTask.id]) &&
+        startUserSelectAssignees.value[userTask.id].length === 0
+      ) {
+        return message.warning(`璇烽�夋嫨${userTask.name}鐨勫鎵逛汉`)
+      }
+    }
+  }
+
+  // 2. 鎻愪氦璇锋眰
+  formLoading.value = true
+  try {
+    const data = { ...formData.value } as unknown as LeaveApi.LeaveVO
+    // 瀹℃壒鐩稿叧锛氳缃寚瀹氬鎵逛汉
+    if (startUserSelectTasks.value?.length > 0) {
+      data.startUserSelectAssignees = startUserSelectAssignees.value
+    }
+    await LeaveApi.createLeave(data)
+    message.success('鍙戣捣鎴愬姛')
+    // 鍏抽棴褰撳墠 Tab
+    delView(unref(currentRoute))
+    await push({ name: 'BpmOALeave' })
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 瀹℃壒鐩稿叧锛氳幏鍙栧鎵硅鎯� */
+const getApprovalDetail = async () => {
+  try {
+    const data = await ProcessInstanceApi.getApprovalDetail({
+      processDefinitionId: processDefinitionId.value,
+      // TODO 灏忓寳锛氬彲浠ユ敮鎸� processDefinitionKey 鏌ヨ
+      activityId: NodeId.START_USER_NODE_ID,
+      processVariablesStr: JSON.stringify({ day: daysDifference() }) // 瑙e喅 GET 鏃犳硶浼犻�掑璞$殑闂锛屽悗绔� String 鍐嶈浆 JSON
+    })
+
+    if (!data) {
+      message.error('鏌ヨ涓嶅埌瀹℃壒璇︽儏淇℃伅锛�')
+      return
+    }
+    // 鑾峰彇瀹℃壒鑺傜偣锛屾樉绀� Timeline 鐨勬暟鎹�
+    activityNodes.value = data.activityNodes
+
+    // 鑾峰彇鍙戣捣浜鸿嚜閫夌殑浠诲姟
+    startUserSelectTasks.value = data.activityNodes?.filter(
+      (node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
+    )
+    // 鎭㈠涔嬪墠鐨勯�夋嫨瀹℃壒浜�
+    if (startUserSelectTasks.value?.length > 0) {
+      for (const node of startUserSelectTasks.value) {
+        if (
+          tempStartUserSelectAssignees.value[node.id] &&
+          tempStartUserSelectAssignees.value[node.id].length > 0
+        ) {
+          startUserSelectAssignees.value[node.id] = tempStartUserSelectAssignees.value[node.id]
+        } else {
+          startUserSelectAssignees.value[node.id] = []
+        }
+      }
+    }
+  } finally {
+  }
+}
+
+/** 瀹℃壒鐩稿叧锛氶�夋嫨鍙戣捣浜� */
+const selectUserConfirm = (id: string, userList: any[]) => {
+  startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id)
+}
+
+// 璁$畻澶╂暟宸�
+// TODO @灏忓寳锛氬彲浠ユ悶鍒� formatTime 閲岄潰鍘伙紝鐒跺悗鐪嬬湅 dayjs 閲岄潰鏈夋病鏈夌幇鎴愮殑鏂规硶锛屾垨鑰呰緟鍔╄绠楃殑鏂规硶銆�
+const daysDifference = () => {
+  const oneDay = 24 * 60 * 60 * 1000 // 涓�澶╃殑姣鏁�
+  const diffTime = Math.abs(Number(formData.value.endTime) - Number(formData.value.startTime))
+  return Math.floor(diffTime / oneDay)
+}
+
+/** 鑾峰彇璇峰亣鏁版嵁锛岀敤浜庨噸鏂板彂璧锋椂鑷姩濉厖 */
+const getDetail = async (id: number) => {
+  try {
+    formLoading.value = true
+    const data = await LeaveApi.getLeave(id)
+    if (!data) {
+      message.error('閲嶆柊鍙戣捣璇峰亣澶辫触锛屽師鍥狅細璇峰亣鏁版嵁涓嶅瓨鍦�')
+      return
+    }
+    formData.value = {
+      type: data.type,
+      reason: data.reason,
+      startTime: data.startTime,
+      endTime: data.endTime
+    }
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 鍒濆鍖� */
+onMounted(async () => {
+  // TODO @灏忓寳锛氳繖閲屽彲浠ョ畝鍖栵紝缁熶竴閫氳繃 getApprovalDetail 澶勭悊涔堬紵
+  const processDefinitionDetail = await DefinitionApi.getProcessDefinition(
+    undefined,
+    processDefineKey
+  )
+
+  if (!processDefinitionDetail) {
+    message.error('OA 璇峰亣鐨勬祦绋嬫ā鍨嬫湭閰嶇疆锛岃妫�鏌ワ紒')
+    return
+  }
+  processDefinitionId.value = processDefinitionDetail.id
+  startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
+
+  // 濡傛灉鏈変笟鍔$紪鍙凤紝璇存槑鏄噸鏂板彂璧凤紝闇�瑕佸姞杞藉師鏈夋暟鎹�
+  if (query.id) {
+    await getDetail(Number(query.id))
+  }
+
+  // 瀹℃壒鐩稿叧锛氬姞杞芥渶鏂扮殑瀹℃壒璇︽儏锛屼富瑕佺敤浜庤妭鐐归娴�
+  await getApprovalDetail()
+})
+
+/** 瀹℃壒鐩稿叧锛氶娴嬫祦绋嬭妭鐐逛細鍥犱负杈撳叆鐨勫弬鏁板�艰�屼骇鐢熸柊鐨勯娴嬬粨鏋滃�硷紝鎵�浠ラ渶閲嶆柊棰勬祴涓�娆�, formData.value鍙敼鎴愬疄闄呬笟鍔′腑鐨勭壒瀹氬瓧娈� */
+watch(
+  formData.value,
+  (newValue, oldValue) => {
+    if (!oldValue) {
+      return
+    }
+    if (newValue && Object.keys(newValue).length > 0) {
+      // 璁板綍涔嬪墠鐨勮妭鐐瑰鎵逛汉
+      tempStartUserSelectAssignees.value = startUserSelectAssignees.value
+      startUserSelectAssignees.value = {}
+      // 鍔犺浇鏈�鏂扮殑瀹℃壒璇︽儏,涓昏鐢ㄤ簬鑺傜偣棰勬祴
+      getApprovalDetail()
+    }
+  },
+  {
+    immediate: true
+  }
+)
+</script>

--
Gitblit v1.8.0