-
默认风险点
-
+
{{ optionsTitle }}
+
{{ option.label }}
+
{{ selectionError }}
@@ -58,7 +60,7 @@ import { computed, ref, watch } from 'vue'
import ConfirmDialog from './ConfirmDialog.vue'
-const RETURN_REASON_OPTIONS = [
+const CLAIM_RETURN_REASON_OPTIONS = [
{ code: 'missing_attachment', label: '附件缺失或不清晰', icon: 'mdi mdi-paperclip-alert' },
{ code: 'invoice_mismatch', label: '票据类型/金额与明细不一致', icon: 'mdi mdi-file-compare' },
{ code: 'over_policy', label: '超出制度标准或缺少超标说明', icon: 'mdi mdi-scale-unbalanced' },
@@ -67,10 +69,44 @@ const RETURN_REASON_OPTIONS = [
{ code: 'approval_question', label: '审批人需要补充说明', icon: 'mdi mdi-comment-question-outline' }
]
+const APPLICATION_RETURN_REASON_OPTIONS = [
+ {
+ code: 'application_info_incomplete',
+ label: '申请信息不完整',
+ icon: 'mdi mdi-form-textbox',
+ defaultReason: '请补充出差时间、地点、事由、天数或出行方式等关键信息后重新提交。'
+ },
+ {
+ code: 'application_business_need_unclear',
+ label: '业务必要性说明不足',
+ icon: 'mdi mdi-briefcase-question-outline',
+ defaultReason: '请说明本次申请对应的项目、客户或任务背景,以及必须现场处理的原因。'
+ },
+ {
+ code: 'application_budget_basis_missing',
+ label: '预算测算依据不足',
+ icon: 'mdi mdi-calculator-variant-outline',
+ defaultReason: '请补充预计住宿、交通、补贴等费用构成及测算依据,便于判断预算合理性。'
+ },
+ {
+ code: 'application_policy_mismatch',
+ label: '制度口径不匹配',
+ icon: 'mdi mdi-scale-balance',
+ defaultReason: '当前申请与差旅制度口径存在不一致,请核对职级、目的地、天数或费用标准后调整。'
+ },
+ {
+ code: 'application_attachment_needed',
+ label: '前置材料需补充',
+ icon: 'mdi mdi-file-document-plus-outline',
+ defaultReason: '请补充会议通知、客户邀约、项目安排或其他能支撑申请必要性的材料。'
+ }
+]
+
const props = defineProps({
open: { type: Boolean, default: false },
busy: { type: Boolean, default: false },
claimNo: { type: String, default: '' },
+ application: { type: Boolean, default: false },
title: { type: String, default: '确认退回该单据吗?' },
description: {
type: String,
@@ -83,15 +119,40 @@ const emit = defineEmits(['close', 'confirm'])
const selectedCodes = ref([])
const reasonText = ref('')
const touched = ref(false)
+const selectionTouched = ref(false)
+const lastAutoReason = ref('')
-const options = computed(() => RETURN_REASON_OPTIONS)
+const options = computed(() => (props.application ? APPLICATION_RETURN_REASON_OPTIONS : CLAIM_RETURN_REASON_OPTIONS))
+const dialogBadge = computed(() => (props.application ? '退回申请' : '退回单据'))
+const optionsTitle = computed(() => (props.application ? '退单选项' : '默认风险点'))
+const optionsAriaLabel = computed(() => (props.application ? '申请退单选项' : '默认退回风险点'))
+const reasonPlaceholder = computed(() => (
+ props.application
+ ? '请选择退单选项,系统会自动带入默认理由。领导可按实际情况继续修改。'
+ : '请写清楚需要申请人补充或修改的内容,例如:发票金额与明细金额不一致,请重新上传正确票据。'
+))
const trimmedReason = computed(() => reasonText.value.trim())
+const selectionError = computed(() => {
+ if (!props.application || !selectionTouched.value || selectedCodes.value.length > 0) {
+ return ''
+ }
+ return '请选择至少一个退单选项,便于后续看板统计。'
+})
const reasonError = computed(() => {
if (!touched.value || trimmedReason.value.length >= 6) {
return ''
}
return '请至少填写 6 个字的明确退单理由。'
})
+const validationMessage = computed(() => (
+ selectionError.value
+ || reasonError.value
+ || (
+ props.application
+ ? '退单选项会写入结构化埋点,理由会展示给申请人。'
+ : '会同步记录到退单埋点,并展示给申请人。'
+ )
+))
watch(
() => props.open,
@@ -100,10 +161,33 @@ watch(
selectedCodes.value = []
reasonText.value = ''
touched.value = false
+ selectionTouched.value = false
+ lastAutoReason.value = ''
}
}
)
+watch(selectedCodes, () => {
+ if (!props.application) {
+ return
+ }
+
+ const defaultReason = selectedCodes.value
+ .map((code) => options.value.find((option) => option.code === code)?.defaultReason || '')
+ .filter(Boolean)
+ .join('\n')
+
+ const canAutoFill = !touched.value || !reasonText.value.trim() || reasonText.value === lastAutoReason.value
+ if (canAutoFill) {
+ reasonText.value = defaultReason
+ }
+ lastAutoReason.value = defaultReason
+})
+
+function handleOptionChange() {
+ selectionTouched.value = true
+}
+
function handleClose() {
if (!props.busy) {
emit('close')
@@ -112,7 +196,8 @@ function handleClose() {
function handleConfirm() {
touched.value = true
- if (trimmedReason.value.length < 6 || props.busy) {
+ selectionTouched.value = true
+ if ((props.application && selectedCodes.value.length === 0) || trimmedReason.value.length < 6 || props.busy) {
return
}
diff --git a/web/src/components/travel/TravelRequestApprovalDialog.vue b/web/src/components/travel/TravelRequestApprovalDialog.vue
index 6e4ce62..d701a23 100644
--- a/web/src/components/travel/TravelRequestApprovalDialog.vue
+++ b/web/src/components/travel/TravelRequestApprovalDialog.vue
@@ -27,11 +27,27 @@
{{ summaryLabel }}
{{ nextStage }}
-