|
|
@@ -0,0 +1,605 @@
|
|
|
+<!-- 发起审批 / 草稿编辑共用弹窗 -->
|
|
|
+<template>
|
|
|
+ <el-dialog
|
|
|
+ v-model="dlgVisible"
|
|
|
+ :title="picked?.appName || '发起审批'"
|
|
|
+ width="920px"
|
|
|
+ class="dialog-form-layout"
|
|
|
+ align-center
|
|
|
+ append-to-body
|
|
|
+ modal-class="protocol-dialog-mask"
|
|
|
+ destroy-on-close
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ @closed="onDialogClosed"
|
|
|
+ >
|
|
|
+ <div v-loading="detailLoading">
|
|
|
+ <form-create
|
|
|
+ v-if="formRule.length"
|
|
|
+ v-model="formData"
|
|
|
+ v-model:api="fapi"
|
|
|
+ :rule="formRule"
|
|
|
+ :option="formOption"
|
|
|
+ />
|
|
|
+ <p v-if="confirmationDateManualFields.length" class="oa-start__tip oa-start__confirm-relink">
|
|
|
+ <el-button link type="primary" @click="restoreConfirmationDateAuto">
|
|
|
+ 恢复转正日期自动计算
|
|
|
+ </el-button>
|
|
|
+ </p>
|
|
|
+ <p v-else-if="!formRule.length && !detailLoading" class="oa-start__tip">
|
|
|
+ 该模板暂无表单字段,仍可直接提交。
|
|
|
+ </p>
|
|
|
+ <OaFlowPreview
|
|
|
+ v-if="!detailLoading"
|
|
|
+ v-model:select-by-node="initiatorSelectByNode"
|
|
|
+ :steps="flowSteps"
|
|
|
+ :loading="previewLoading"
|
|
|
+ :resolved="flowResolved"
|
|
|
+ :pending-message="flowPendingMessage"
|
|
|
+ :user-options="userOptions"
|
|
|
+ @select-change="scheduleFlowPreview"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <el-button class="btn-ghost" @click="closeDialog">
|
|
|
+ <el-icon><Close /></el-icon>
|
|
|
+ 取消
|
|
|
+ </el-button>
|
|
|
+ <el-button class="btn-ghost" :loading="savingDraft" @click="saveDraft">
|
|
|
+ <el-icon><Document /></el-icon>
|
|
|
+ {{ editingDraftId ? '保存草稿' : '暂存草稿' }}
|
|
|
+ </el-button>
|
|
|
+ <el-button class="btn-main" type="primary" :loading="submitting" @click="submit">
|
|
|
+ <el-icon><Check /></el-icon>
|
|
|
+ 提交
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+ import { computed, ref, watch } from 'vue'
|
|
|
+ import { Check, Close, Document } from '@element-plus/icons-vue'
|
|
|
+ import { ElMessage } from 'element-plus'
|
|
|
+ import { fetchUserList } from '@/api/admin'
|
|
|
+ import {
|
|
|
+ fetchCreateOaInstanceDraft,
|
|
|
+ fetchOaFlowPreview,
|
|
|
+ fetchOaInstanceDetail,
|
|
|
+ fetchOaInstanceDraftDetail,
|
|
|
+ fetchOaTemplateDetail,
|
|
|
+ fetchStartOaInstance,
|
|
|
+ fetchSubmitOaInstanceDraft,
|
|
|
+ fetchUpdateOaInstanceDraft,
|
|
|
+ type OaFlowPreviewStep
|
|
|
+ } from '@/api/oaApproval'
|
|
|
+ import OaFlowPreview from '@/components/oa-approval/OaFlowPreview.vue'
|
|
|
+ import { MAX_ADMIN_LIST_PAGE_SIZE } from '@/utils/fetch-paged-list'
|
|
|
+ import { OA_FC_DEFAULT_GUTTER, ensureOaUploadRuleProps } from '@/utils/oa-form-create-upload'
|
|
|
+ import {
|
|
|
+ applyLeaveKitDurationsToFormData,
|
|
|
+ ensureLeaveKitRuleProps,
|
|
|
+ LEAVE_MANUAL_PREFIX,
|
|
|
+ listLeaveKitBindings,
|
|
|
+ markLeaveDurationManual
|
|
|
+ } from '@/utils/oa-leave-kit-runtime'
|
|
|
+ import {
|
|
|
+ applyOvertimeKitDefaults,
|
|
|
+ ensureOvertimeKitRuleProps
|
|
|
+ } from '@/utils/oa-overtime-kit-runtime'
|
|
|
+ import { applyResignationKitDefaults } from '@/utils/oa-resignation-kit-runtime'
|
|
|
+ import {
|
|
|
+ applyConfirmationKitDateLinkage,
|
|
|
+ applyConfirmationKitDefaults,
|
|
|
+ clearConfirmationDateManualAndRelink,
|
|
|
+ CONFIRMATION_MANUAL_PREFIX,
|
|
|
+ ensureConfirmationKitVisibility,
|
|
|
+ listConfirmationKitBindings,
|
|
|
+ listManualConfirmationDateFields,
|
|
|
+ markConfirmationDateManual
|
|
|
+ } from '@/utils/oa-confirmation-kit-runtime'
|
|
|
+ import {
|
|
|
+ applyOwnerFieldDefaults,
|
|
|
+ ensureOwnerFieldRuleProps
|
|
|
+ } from '@/utils/oa-owner-field-defaults'
|
|
|
+ import {
|
|
|
+ applyTripKitDaysToFormData,
|
|
|
+ listTripKits,
|
|
|
+ TRIP_MANUAL_PREFIX
|
|
|
+ } from '@/utils/oa-trip-kit-runtime'
|
|
|
+ import { useUserStore } from '@/store/modules/user'
|
|
|
+
|
|
|
+ defineOptions({ name: 'OaStartFormDialog' })
|
|
|
+
|
|
|
+ const emit = defineEmits<{
|
|
|
+ saved: []
|
|
|
+ submitted: [instance: any]
|
|
|
+ closed: []
|
|
|
+ }>()
|
|
|
+
|
|
|
+ const userStore = useUserStore()
|
|
|
+ const dlgVisible = ref(false)
|
|
|
+ const detailLoading = ref(false)
|
|
|
+ const picked = ref<any>(null)
|
|
|
+ const formData = ref<Record<string, any>>({})
|
|
|
+ const formRule = ref<any[]>([])
|
|
|
+ const formOption = ref<Record<string, any>>({
|
|
|
+ submitBtn: false,
|
|
|
+ resetBtn: false,
|
|
|
+ row: { gutter: OA_FC_DEFAULT_GUTTER }
|
|
|
+ })
|
|
|
+ const fapi = ref<any>(null)
|
|
|
+ const submitting = ref(false)
|
|
|
+ const savingDraft = ref(false)
|
|
|
+ const editingDraftId = ref('')
|
|
|
+ const needInitiatorSelect = ref(false)
|
|
|
+ const initiatorSelectByNode = ref<Record<string, string[]>>({})
|
|
|
+ const userOptions = ref<Array<{ id: string; nickName: string; userNo: string }>>([])
|
|
|
+ const flowSteps = ref<OaFlowPreviewStep[]>([])
|
|
|
+ const flowResolved = ref(true)
|
|
|
+ const flowPendingMessage = ref('必填信息填写后,流程将自动显示')
|
|
|
+ const previewLoading = ref(false)
|
|
|
+ let previewTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
+ /** 避免时长自动回写再次触发手改判定 */
|
|
|
+ let leaveKitSyncing = false
|
|
|
+ let leaveKitPrevSnapshot: Record<string, unknown> = {}
|
|
|
+ let tripKitSyncing = false
|
|
|
+ let tripKitPrevSnapshot: Record<string, unknown> = {}
|
|
|
+ let confirmationKitSyncing = false
|
|
|
+ let confirmationKitPrevSnapshot: Record<string, unknown> = {}
|
|
|
+
|
|
|
+ const confirmationDateManualFields = computed(() =>
|
|
|
+ listManualConfirmationDateFields({ rule: formRule.value }, formData.value)
|
|
|
+ )
|
|
|
+
|
|
|
+ function flowNeedsInitiatorSelect(node: any): boolean {
|
|
|
+ if (!node || typeof node !== 'object') return false
|
|
|
+ const cfg = node.approverConfig || {}
|
|
|
+ if ((cfg.assigneeType || '') === 'initiator_select') return true
|
|
|
+ if (node.child && flowNeedsInitiatorSelect(node.child)) return true
|
|
|
+ for (const g of node.conditionGroups || []) {
|
|
|
+ if (g?.child && flowNeedsInitiatorSelect(g.child)) return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ async function loadUsers() {
|
|
|
+ try {
|
|
|
+ const pageSize = MAX_ADMIN_LIST_PAGE_SIZE
|
|
|
+ const raw: any[] = []
|
|
|
+ let page = 1
|
|
|
+ for (;;) {
|
|
|
+ const res = await fetchUserList({ page, pageSize, status: '1' })
|
|
|
+ const list = res?.list || []
|
|
|
+ raw.push(...list)
|
|
|
+ if (list.length < pageSize) break
|
|
|
+ if (typeof res?.total === 'number' && raw.length >= res.total) break
|
|
|
+ page += 1
|
|
|
+ }
|
|
|
+ userOptions.value = raw.map((u) => ({
|
|
|
+ id: u.id,
|
|
|
+ nickName: u.nickName || '',
|
|
|
+ userNo: u.userNo || ''
|
|
|
+ }))
|
|
|
+ } catch {
|
|
|
+ userOptions.value = []
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function loadFlowPreview(appId: string) {
|
|
|
+ previewLoading.value = true
|
|
|
+ try {
|
|
|
+ const preview = await fetchOaFlowPreview(appId, {
|
|
|
+ formData: { ...formData.value },
|
|
|
+ initiatorSelectByNode: { ...initiatorSelectByNode.value }
|
|
|
+ })
|
|
|
+ flowResolved.value = preview?.resolved !== false
|
|
|
+ flowPendingMessage.value = preview?.message || '必填信息填写后,流程将自动显示'
|
|
|
+ flowSteps.value = flowResolved.value ? preview?.steps || [] : []
|
|
|
+ } catch {
|
|
|
+ flowSteps.value = []
|
|
|
+ flowResolved.value = true
|
|
|
+ } finally {
|
|
|
+ previewLoading.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function scheduleFlowPreview() {
|
|
|
+ if (!dlgVisible.value || !picked.value?.id) return
|
|
|
+ if (previewTimer) clearTimeout(previewTimer)
|
|
|
+ previewTimer = setTimeout(() => {
|
|
|
+ loadFlowPreview(picked.value.id)
|
|
|
+ }, 300)
|
|
|
+ }
|
|
|
+
|
|
|
+ async function onPick(t: any) {
|
|
|
+ editingDraftId.value = ''
|
|
|
+ picked.value = t
|
|
|
+ formData.value = {}
|
|
|
+ formRule.value = []
|
|
|
+ leaveKitPrevSnapshot = {}
|
|
|
+ tripKitPrevSnapshot = {}
|
|
|
+ confirmationKitPrevSnapshot = {}
|
|
|
+ needInitiatorSelect.value = false
|
|
|
+ initiatorSelectByNode.value = {}
|
|
|
+ flowSteps.value = []
|
|
|
+ flowResolved.value = false
|
|
|
+ flowPendingMessage.value = '必填信息填写后,流程将自动显示'
|
|
|
+ dlgVisible.value = true
|
|
|
+ detailLoading.value = true
|
|
|
+ previewLoading.value = true
|
|
|
+ try {
|
|
|
+ const detail = await fetchOaTemplateDetail(t.id)
|
|
|
+ const schema = detail?.formSchemaJson || {}
|
|
|
+ const rawRule = Array.isArray(schema.rule) ? schema.rule : Array.isArray(schema) ? schema : []
|
|
|
+ formRule.value = ensureConfirmationKitVisibility(
|
|
|
+ ensureOwnerFieldRuleProps(
|
|
|
+ ensureOvertimeKitRuleProps(ensureLeaveKitRuleProps(ensureOaUploadRuleProps(rawRule)))
|
|
|
+ )
|
|
|
+ )
|
|
|
+ const savedGutter = Number(schema.option?.row?.gutter)
|
|
|
+ formOption.value = {
|
|
|
+ ...(schema.option || {}),
|
|
|
+ form: {
|
|
|
+ ...(schema.option?.form || {}),
|
|
|
+ labelPosition: 'top'
|
|
|
+ },
|
|
|
+ // 顶层字段 col.span 布局用;栅格组件 fcRow 由 ensureOaUploadRuleProps 处理
|
|
|
+ row: {
|
|
|
+ ...(schema.option?.row || {}),
|
|
|
+ gutter: savedGutter > 0 ? savedGutter : OA_FC_DEFAULT_GUTTER
|
|
|
+ },
|
|
|
+ submitBtn: false,
|
|
|
+ resetBtn: false
|
|
|
+ }
|
|
|
+ let data = await applyOwnerFieldDefaults(
|
|
|
+ formRule.value,
|
|
|
+ {},
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ data = await applyConfirmationKitDefaults(
|
|
|
+ { rule: formRule.value },
|
|
|
+ data,
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ data = applyResignationKitDefaults(
|
|
|
+ { rule: formRule.value },
|
|
|
+ data,
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ data = applyOvertimeKitDefaults(
|
|
|
+ { rule: formRule.value },
|
|
|
+ data,
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ const linked = applyConfirmationKitDateLinkage({ rule: formRule.value }, data)
|
|
|
+ formData.value = linked.data
|
|
|
+ confirmationKitPrevSnapshot = { ...linked.data }
|
|
|
+ const flow = detail?.flowTreeJson || {}
|
|
|
+ const root = flow.root || flow
|
|
|
+ needInitiatorSelect.value = flowNeedsInitiatorSelect(root)
|
|
|
+ if (needInitiatorSelect.value && !userOptions.value.length) {
|
|
|
+ await loadUsers()
|
|
|
+ }
|
|
|
+ await loadFlowPreview(t.id)
|
|
|
+ } catch {
|
|
|
+ formRule.value = []
|
|
|
+ flowSteps.value = []
|
|
|
+ flowResolved.value = true
|
|
|
+ previewLoading.value = false
|
|
|
+ } finally {
|
|
|
+ detailLoading.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function closeDialog() {
|
|
|
+ dlgVisible.value = false
|
|
|
+ editingDraftId.value = ''
|
|
|
+ }
|
|
|
+
|
|
|
+ function onDialogClosed() {
|
|
|
+ editingDraftId.value = ''
|
|
|
+ emit('closed')
|
|
|
+ }
|
|
|
+
|
|
|
+ async function openForApp(appRow: any) {
|
|
|
+ await onPick(appRow)
|
|
|
+ }
|
|
|
+
|
|
|
+ async function openForDraft(draftId: string, templatesOptional?: any[]) {
|
|
|
+ const detail = await fetchOaInstanceDraftDetail(draftId)
|
|
|
+ const appId = String(detail?.appId || '').trim()
|
|
|
+ if (!appId) {
|
|
|
+ ElMessage.error('草稿缺少审批模板')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const list = templatesOptional || []
|
|
|
+ let row = list.find((t: any) => t.id === appId)
|
|
|
+ if (!row) {
|
|
|
+ row = {
|
|
|
+ id: appId,
|
|
|
+ appName: detail?.appName || '发起审批'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ await onPick(row)
|
|
|
+ editingDraftId.value = draftId
|
|
|
+ const saved = { ...(detail?.formData || {}) }
|
|
|
+ formData.value = await applyOwnerFieldDefaults(
|
|
|
+ formRule.value,
|
|
|
+ saved,
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ formData.value = applyOvertimeKitDefaults(
|
|
|
+ { rule: formRule.value },
|
|
|
+ formData.value,
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ const linked = applyConfirmationKitDateLinkage({ rule: formRule.value }, formData.value)
|
|
|
+ formData.value = linked.data
|
|
|
+ confirmationKitPrevSnapshot = { ...linked.data }
|
|
|
+ leaveKitPrevSnapshot = { ...formData.value }
|
|
|
+ tripKitPrevSnapshot = { ...formData.value }
|
|
|
+ const sel = detail?.initiatorSelectByNode
|
|
|
+ initiatorSelectByNode.value =
|
|
|
+ sel && typeof sel === 'object' && !Array.isArray(sel) ? { ...sel } : {}
|
|
|
+ await loadFlowPreview(appId)
|
|
|
+ }
|
|
|
+
|
|
|
+ async function openForAppWithCopy(appRow: any, copyFromInstanceId: string) {
|
|
|
+ await openForApp(appRow)
|
|
|
+ const copyId = String(copyFromInstanceId || '').trim()
|
|
|
+ if (!copyId) return
|
|
|
+ try {
|
|
|
+ const old = await fetchOaInstanceDetail(copyId)
|
|
|
+ formData.value = await applyOwnerFieldDefaults(
|
|
|
+ formRule.value,
|
|
|
+ { ...(old?.formDataJson || {}) },
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ formData.value = applyOvertimeKitDefaults(
|
|
|
+ { rule: formRule.value },
|
|
|
+ formData.value,
|
|
|
+ (userStore.info || userStore.getUserInfo) as any
|
|
|
+ )
|
|
|
+ } catch {
|
|
|
+ /* 预填失败仍可手工填 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function saveDraft() {
|
|
|
+ if (!picked.value?.id) return
|
|
|
+ savingDraft.value = true
|
|
|
+ try {
|
|
|
+ const payload = {
|
|
|
+ formData: { ...formData.value },
|
|
|
+ initiatorSelectByNode: { ...initiatorSelectByNode.value },
|
|
|
+ title: picked.value.appName || ''
|
|
|
+ }
|
|
|
+ if (editingDraftId.value) {
|
|
|
+ await fetchUpdateOaInstanceDraft(editingDraftId.value, payload)
|
|
|
+ } else {
|
|
|
+ const created: any = await fetchCreateOaInstanceDraft({
|
|
|
+ appId: picked.value.id,
|
|
|
+ ...payload
|
|
|
+ })
|
|
|
+ if (created?.id) editingDraftId.value = created.id
|
|
|
+ }
|
|
|
+ emit('saved')
|
|
|
+ closeDialog()
|
|
|
+ } finally {
|
|
|
+ savingDraft.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function syncLeaveKitDurations() {
|
|
|
+ if (leaveKitSyncing) return
|
|
|
+ if (!formRule.value.length) return
|
|
|
+ const schema = { rule: formRule.value }
|
|
|
+ const bindings = listLeaveKitBindings(schema)
|
|
|
+ if (!bindings.length) return
|
|
|
+
|
|
|
+ const curr = { ...formData.value }
|
|
|
+ for (const kit of bindings) {
|
|
|
+ const prevDur = leaveKitPrevSnapshot[kit.durationField]
|
|
|
+ const currDur = curr[kit.durationField]
|
|
|
+ const startChanged = leaveKitPrevSnapshot[kit.startField] !== curr[kit.startField]
|
|
|
+ const endChanged = leaveKitPrevSnapshot[kit.endField] !== curr[kit.endField]
|
|
|
+ const durChanged = prevDur !== currDur
|
|
|
+ if (durChanged && !startChanged && !endChanged && currDur !== undefined && currDur !== '') {
|
|
|
+ Object.assign(curr, markLeaveDurationManual(curr, kit.durationField, true))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const { data, changed } = applyLeaveKitDurationsToFormData(schema, curr)
|
|
|
+ leaveKitPrevSnapshot = { ...data }
|
|
|
+ const manualKeysChanged = bindings.some(
|
|
|
+ (k) =>
|
|
|
+ !!data[LEAVE_MANUAL_PREFIX + k.durationField] !==
|
|
|
+ !!formData.value[LEAVE_MANUAL_PREFIX + k.durationField]
|
|
|
+ )
|
|
|
+ const durationChanged = bindings.some(
|
|
|
+ (k) => data[k.durationField] !== formData.value[k.durationField]
|
|
|
+ )
|
|
|
+ if (changed || manualKeysChanged || durationChanged) {
|
|
|
+ leaveKitSyncing = true
|
|
|
+ formData.value = data
|
|
|
+ queueMicrotask(() => {
|
|
|
+ leaveKitSyncing = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function syncTripKitDurations() {
|
|
|
+ if (tripKitSyncing) return
|
|
|
+ if (!formRule.value.length) return
|
|
|
+ const schema = { rule: formRule.value }
|
|
|
+ const kits = listTripKits(schema)
|
|
|
+ if (!kits.length) return
|
|
|
+
|
|
|
+ const curr = { ...formData.value }
|
|
|
+ for (const kit of kits) {
|
|
|
+ const prevDays = tripKitPrevSnapshot[kit.daysField]
|
|
|
+ const currDays = curr[kit.daysField]
|
|
|
+ const daysChanged = prevDays !== currDays
|
|
|
+ const listChanged =
|
|
|
+ JSON.stringify(tripKitPrevSnapshot[kit.listField]) !== JSON.stringify(curr[kit.listField])
|
|
|
+ if (daysChanged && !listChanged && currDays !== undefined && currDays !== '') {
|
|
|
+ curr[TRIP_MANUAL_PREFIX + kit.daysField] = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const { data, changed } = applyTripKitDaysToFormData(schema, curr)
|
|
|
+ tripKitPrevSnapshot = { ...data }
|
|
|
+ const manualKeysChanged = kits.some(
|
|
|
+ (k) =>
|
|
|
+ !!data[TRIP_MANUAL_PREFIX + k.daysField] !==
|
|
|
+ !!formData.value[TRIP_MANUAL_PREFIX + k.daysField]
|
|
|
+ )
|
|
|
+ const daysFieldChanged = kits.some((k) => data[k.daysField] !== formData.value[k.daysField])
|
|
|
+ const listFieldChanged = kits.some(
|
|
|
+ (k) => JSON.stringify(data[k.listField]) !== JSON.stringify(formData.value[k.listField])
|
|
|
+ )
|
|
|
+ if (changed || manualKeysChanged || daysFieldChanged || listFieldChanged) {
|
|
|
+ tripKitSyncing = true
|
|
|
+ formData.value = data
|
|
|
+ queueMicrotask(() => {
|
|
|
+ tripKitSyncing = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function syncConfirmationKitDates() {
|
|
|
+ if (confirmationKitSyncing) return
|
|
|
+ if (!formRule.value.length) return
|
|
|
+ const schema = { rule: formRule.value }
|
|
|
+ const kits = listConfirmationKitBindings(schema)
|
|
|
+ if (!kits.length) return
|
|
|
+
|
|
|
+ let curr = { ...formData.value }
|
|
|
+ for (const kit of kits) {
|
|
|
+ const confField = kit.confirmationDateField
|
|
|
+ const hireField = kit.hireDateField
|
|
|
+ const probField = kit.probationMonthsField
|
|
|
+ if (!confField || !hireField || !probField) continue
|
|
|
+ const confChanged = confirmationKitPrevSnapshot[confField] !== curr[confField]
|
|
|
+ const hireChanged = confirmationKitPrevSnapshot[hireField] !== curr[hireField]
|
|
|
+ const probChanged = confirmationKitPrevSnapshot[probField] !== curr[probField]
|
|
|
+ if (
|
|
|
+ confChanged &&
|
|
|
+ !hireChanged &&
|
|
|
+ !probChanged &&
|
|
|
+ curr[confField] !== undefined &&
|
|
|
+ curr[confField] !== ''
|
|
|
+ ) {
|
|
|
+ curr = markConfirmationDateManual(curr, confField, true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const { data, changed } = applyConfirmationKitDateLinkage(schema, curr)
|
|
|
+ confirmationKitPrevSnapshot = { ...data }
|
|
|
+ const anyDiff =
|
|
|
+ changed ||
|
|
|
+ kits.some((k) => {
|
|
|
+ const f = k.confirmationDateField
|
|
|
+ if (!f) return false
|
|
|
+ return (
|
|
|
+ data[f] !== formData.value[f] ||
|
|
|
+ !!data[CONFIRMATION_MANUAL_PREFIX + f] !==
|
|
|
+ !!formData.value[CONFIRMATION_MANUAL_PREFIX + f]
|
|
|
+ )
|
|
|
+ })
|
|
|
+ if (anyDiff) {
|
|
|
+ confirmationKitSyncing = true
|
|
|
+ formData.value = data
|
|
|
+ queueMicrotask(() => {
|
|
|
+ confirmationKitSyncing = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function restoreConfirmationDateAuto() {
|
|
|
+ const schema = { rule: formRule.value }
|
|
|
+ let data = { ...formData.value }
|
|
|
+ for (const field of listManualConfirmationDateFields(schema, data)) {
|
|
|
+ data = clearConfirmationDateManualAndRelink(schema, data, field)
|
|
|
+ }
|
|
|
+ confirmationKitSyncing = true
|
|
|
+ formData.value = data
|
|
|
+ confirmationKitPrevSnapshot = { ...data }
|
|
|
+ queueMicrotask(() => {
|
|
|
+ confirmationKitSyncing = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ watch(
|
|
|
+ formData,
|
|
|
+ () => {
|
|
|
+ syncLeaveKitDurations()
|
|
|
+ syncTripKitDurations()
|
|
|
+ syncConfirmationKitDates()
|
|
|
+ scheduleFlowPreview()
|
|
|
+ },
|
|
|
+ { deep: true }
|
|
|
+ )
|
|
|
+
|
|
|
+ async function submit() {
|
|
|
+ if (!picked.value?.id) return
|
|
|
+ for (const s of flowSteps.value) {
|
|
|
+ if (s.type === 'approval') {
|
|
|
+ const types =
|
|
|
+ Array.isArray(s.assigneeTypes) && s.assigneeTypes.length
|
|
|
+ ? s.assigneeTypes
|
|
|
+ : s.assigneeType
|
|
|
+ ? [s.assigneeType]
|
|
|
+ : []
|
|
|
+ if (types.includes('initiator_select')) {
|
|
|
+ const ids = initiatorSelectByNode.value[s.id] || []
|
|
|
+ if (!ids.length) {
|
|
|
+ ElMessage.warning(`请为「${s.name || '审批人'}」选择审批人`)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (fapi.value?.validate) {
|
|
|
+ try {
|
|
|
+ await fapi.value.validate()
|
|
|
+ } catch {
|
|
|
+ ElMessage.warning('请完善表单必填项')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ submitting.value = true
|
|
|
+ try {
|
|
|
+ const payload = {
|
|
|
+ formData: { ...formData.value },
|
|
|
+ initiatorSelectByNode: { ...initiatorSelectByNode.value }
|
|
|
+ }
|
|
|
+ let data: any
|
|
|
+ if (editingDraftId.value) {
|
|
|
+ data = await fetchSubmitOaInstanceDraft(editingDraftId.value, {
|
|
|
+ ...payload,
|
|
|
+ title: picked.value.appName || ''
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ data = await fetchStartOaInstance({
|
|
|
+ appId: picked.value.id,
|
|
|
+ ...payload
|
|
|
+ })
|
|
|
+ }
|
|
|
+ emit('submitted', data)
|
|
|
+ closeDialog()
|
|
|
+ } finally {
|
|
|
+ submitting.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ defineExpose({ openForApp, openForDraft, openForAppWithCopy })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ .oa-start__tip {
|
|
|
+ margin: 8px 0 0;
|
|
|
+ color: var(--mint-text-muted, #94a3b8);
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+</style>
|