diff --git a/src/components/business/package-silence/SilenceExtendDialog.vue b/src/components/business/package-silence/SilenceExtendDialog.vue index 25eb86a..5bc64a3 100644 --- a/src/components/business/package-silence/SilenceExtendDialog.vue +++ b/src/components/business/package-silence/SilenceExtendDialog.vue @@ -32,9 +32,21 @@ value="special" label="特殊延期" /> + (uploadingReceipt = value)" + />先上传收款凭证,系统会自动识别交易单号,可在下方核对修改。 - {{ usageId }} + {{ packageLabel }} 刷新资格 - + {{ + ocrMessage + }} @@ -122,7 +142,7 @@ value-format="YYYY-MM-DDTHH:mm:ssZ" format="YYYY-MM-DD HH:mm:ss" /> - (), + defineProps<{ + usageId?: number + allowed?: boolean + batchMode?: boolean + packageName?: string + assetIdentifier?: string + }>(), { allowed: true, batchMode: false } ) const emit = defineEmits<{ close: []; changed: [] }>() + // 展示套餐名称(及资产),不展示内部套餐记录 ID;调用方未传名称时才退回显示 ID + const packageLabel = computed(() => { + const name = props.packageName?.trim() + if (!name) return props.usageId ? `套餐记录 ${props.usageId}` : '-' + return props.assetIdentifier ? `${name}(${props.assetIdentifier})` : name + }) const { hasAuth } = useAuth(), user = useUserStore() const mode = ref<'online' | 'offline' | 'special'>( @@ -293,6 +326,62 @@ quote.value.forUsage === props.usageId && Number.isSafeInteger(quote.value.amount)) ) + // 线下登记:上传第一张收款凭证后自动识别交易单号(复用代理充值的凭证识别,只预填、需人工核对) + const ocrLoading = ref(false), + ocrMessage = ref(''), + ocrTone = ref<'info' | 'success' | 'warning'>('info'), + tradeNoTouched = ref(false) + let ocrSequence = 0, + ocrKey = '' + async function recognizeTradeNo(key: string) { + if (!key || key === ocrKey) return + ocrKey = key + const current = ++ocrSequence + ocrLoading.value = true + ocrTone.value = 'info' + ocrMessage.value = '正在识别交易单号…' + try { + const res = await AgentRechargeService.recognizePaymentVoucher({ payment_voucher_key: key }) + if (current !== ocrSequence) return + const transactionNo = res.code === 0 ? res.data?.external_transaction_no?.trim() : '' + if (!transactionNo) { + ocrTone.value = 'warning' + ocrMessage.value = '未识别到交易单号,请手动填写' + return + } + if (!tradeNoTouched.value || !tradeNo.value.trim()) { + tradeNo.value = transactionNo + tradeNoTouched.value = false + } + ocrTone.value = 'success' + ocrMessage.value = `已识别交易单号 ${transactionNo},请对照凭证核对` + } catch { + if (current !== ocrSequence) return + ocrTone.value = 'warning' + ocrMessage.value = '识别失败,请手动填写交易单号' + } finally { + if (current === ocrSequence) ocrLoading.value = false + } + } + watch( + () => [mode.value, vouchers.value[0]] as const, + ([currentMode, firstKey]) => { + if (currentMode === 'offline' && firstKey) void recognizeTradeNo(firstKey) + if (!firstKey) { + ocrKey = '' + ocrMessage.value = '' + } + } + ) + // 线下登记的实收金额必须等于试算金额:试算后自动带入,仍可修改 + watch( + () => quote.value?.amount, + (amount) => { + if (mode.value === 'offline' && Number.isSafeInteger(amount) && !paidYuan.value) { + paidYuan.value = (Number(amount) / 100).toFixed(2) + } + } + ) let active = true, version = 0, timer: ReturnType | undefined @@ -319,6 +408,11 @@ basis.value = '' methodId.value = undefined tradeNo.value = '' + tradeNoTouched.value = false + ocrSequence++ + ocrKey = '' + ocrLoading.value = false + ocrMessage.value = '' paidYuan.value = '' paidAt.value = '' batchItems.value = [] @@ -597,4 +691,23 @@ margin-left: 0 !important; } } + + .silence-receipt { + width: 100%; + } + + .silence-ocr-tip { + margin: 4px 0 0; + font-size: 12px; + line-height: 1.5; + color: var(--el-text-color-secondary); + + &.success { + color: var(--el-color-success); + } + + &.warning { + color: var(--el-color-warning); + } + } diff --git a/src/components/business/package-silence/SilenceManagement.vue b/src/components/business/package-silence/SilenceManagement.vue index 85714eb..9ea4a71 100644 --- a/src/components/business/package-silence/SilenceManagement.vue +++ b/src/components/business/package-silence/SilenceManagement.vue @@ -71,7 +71,7 @@ v-if="row.silence.extendable && (hasAuth(P.extend) || hasAuth(P.offline))" link type="primary" - @click="extendId = row.package_usage_id" + @click="openExtension(row)" >延期 @@ -140,6 +140,8 @@ (), extendId = ref(), + extendRow = ref>(), batchVisible = ref(false), policyVisible = ref(false), policyId = ref(), @@ -533,6 +536,10 @@ actionError.value = gatewayError(e) } } + function openExtension(row: Record) { + extendRow.value = row + extendId.value = row.package_usage_id + } async function matchPolicy() { if (!canManagePolicy.value || !matchPackage.value || matching.value) return const scope = pollingScope(user), diff --git a/src/components/business/package-silence/SilenceSummary.vue b/src/components/business/package-silence/SilenceSummary.vue index b44de6c..a7531ba 100644 --- a/src/components/business/package-silence/SilenceSummary.vue +++ b/src/components/business/package-silence/SilenceSummary.vue @@ -42,6 +42,8 @@ @@ -56,7 +58,12 @@ import type { PackageSilenceInfo } from '@/types/api/packageSilence' import { silenceStatusLabel } from '@/utils/business/packageSilence' import { formatDateTime } from '@/utils/business/format' - const props = defineProps<{ silence?: PackageSilenceInfo | null; usageId?: number }>() + const props = defineProps<{ + silence?: PackageSilenceInfo | null + usageId?: number + packageName?: string + assetIdentifier?: string + }>() const emit = defineEmits<{ changed: [] }>() const { hasAuth } = useAuth() const recordsVisible = ref(false), diff --git a/src/views/asset-management/asset-information/components/CurrentPackageCard.vue b/src/views/asset-management/asset-information/components/CurrentPackageCard.vue index 5530281..78fc899 100644 --- a/src/views/asset-management/asset-information/components/CurrentPackageCard.vue +++ b/src/views/asset-management/asset-information/components/CurrentPackageCard.vue @@ -28,6 +28,7 @@ diff --git a/src/views/asset-management/asset-information/components/PackageListCard.vue b/src/views/asset-management/asset-information/components/PackageListCard.vue index 4777c5d..3ddaa29 100644 --- a/src/views/asset-management/asset-information/components/PackageListCard.vue +++ b/src/views/asset-management/asset-information/components/PackageListCard.vue @@ -60,6 +60,7 @@ v-if="row.silence" :silence="row.silence" :usage-id="row.package_usage_id ?? row.id" + :package-name="row.package_name" @changed="emit('refresh')" />-
先上传收款凭证,系统会自动识别交易单号,可在下方核对修改。