Files
one-pipe-system/src/components/business/ShopCreditLimitDialog.vue
luo 93ed3fbd63
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m15s
fix: 代理充值扫码
2026-07-30 09:51:04 +08:00

243 lines
7.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<ElDialog
v-model="dialogVisible"
:title="`调整实际信用额度 - ${currentShop?.shop_name || ''}`"
width="520px"
:close-on-click-modal="false"
@closed="resetDialog"
>
<ElDescriptions :column="1" border class="credit-preview">
<ElDescriptionsItem label="店铺名称">
{{ currentShop?.shop_name || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="当前版本">
{{ currentShop?.version ?? '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="修改前">
{{ formatCreditPreview(currentShop?.credit_enabled, currentShop?.credit_limit) }}
</ElDescriptionsItem>
<ElDescriptionsItem label="修改后">
{{ formatCreditPreview(creditForm.credit_enabled, creditLimitFen) }}
</ElDescriptionsItem>
</ElDescriptions>
<ElAlert
title="实际可用金额、欠款金额和欠款状态以后端刷新后的资金概况为准。"
type="info"
:closable="false"
show-icon
class="credit-dialog-alert"
/>
<ElForm ref="creditFormRef" :model="creditForm" :rules="creditRules" label-width="110px">
<ElFormItem label="启用信用">
<ElSwitch v-model="creditForm.credit_enabled" @change="handleCreditEnabledChange" />
</ElFormItem>
<ElFormItem label="实际信用额度" prop="credit_limit_yuan">
<ElInputNumber
v-model="creditForm.credit_limit_yuan"
:disabled="!creditForm.credit_enabled"
:min="0"
:precision="2"
:step="100"
controls-position="right"
style="width: 100%"
placeholder="请输入实际信用额度"
/>
<div class="credit-dialog-tip">单位关闭信用时额度将自动归零</div>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" :loading="submitting" @click="handleSubmit">确认调整</ElButton>
</div>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { CommissionService, ShopService } from '@/api/modules'
import type { ShopFundSummaryItem } from '@/types/api/commission'
import { fenToYuan, formatMoney, yuanToFen } from '@/utils/business/format'
import { normalizeApiError } from '@/utils/business/apiError'
const props = defineProps<{
modelValue: boolean
shop: ShopFundSummaryItem | null
}>()
const emit = defineEmits<{
'update:modelValue': [value: boolean]
submitted: []
}>()
const dialogVisible = computed({
get: () => props.modelValue,
set: (value: boolean) => emit('update:modelValue', value)
})
const creditFormRef = ref<FormInstance>()
const submitting = ref(false)
const currentShop = ref<ShopFundSummaryItem | null>(null)
const creditForm = reactive({
credit_enabled: false,
credit_limit_yuan: 0
})
const creditLimitFen = computed(() =>
creditForm.credit_enabled ? yuanToFen(creditForm.credit_limit_yuan) || 0 : 0
)
const creditRules = computed<FormRules>(() => ({
credit_limit_yuan: [
{
validator: (
_rule: unknown,
value: number | undefined,
callback: (error?: Error) => void
) => {
if (!creditForm.credit_enabled) {
callback()
return
}
if (value === undefined || value === null || Number.isNaN(value)) {
callback(new Error('请输入实际信用额度'))
return
}
if (value <= 0) {
callback(new Error('实际信用额度必须大于0'))
return
}
callback()
},
trigger: 'blur'
}
]
}))
const formatCreditPreview = (enabled?: boolean, creditLimit?: number) =>
enabled ? `启用 / ${formatMoney(creditLimit || 0)}` : '关闭 / ¥0.00'
const syncForm = (summary: ShopFundSummaryItem) => {
creditForm.credit_enabled = Boolean(summary.credit_enabled)
creditForm.credit_limit_yuan = summary.credit_enabled ? fenToYuan(summary.credit_limit) : 0
nextTick(() => creditFormRef.value?.clearValidate())
}
watch(
() => [props.modelValue, props.shop] as const,
([visible, shop]) => {
if (visible && shop) {
currentShop.value = shop
syncForm(shop)
}
},
{ immediate: true }
)
const handleCreditEnabledChange = (enabled: boolean | string | number) => {
if (!enabled) {
creditForm.credit_limit_yuan = 0
creditFormRef.value?.clearValidate('credit_limit_yuan')
}
}
const resetDialog = () => {
creditFormRef.value?.resetFields()
currentShop.value = null
creditForm.credit_enabled = false
creditForm.credit_limit_yuan = 0
}
const loadLatestSummary = async () => {
if (!currentShop.value) return null
const res = await CommissionService.getShopFundSummary({
page: 1,
page_size: 100,
shop_name: currentShop.value.shop_name
})
if (res.code !== 0) return null
return (
(res.data.items || []).find((item) => item.shop_id === currentShop.value?.shop_id) || null
)
}
const isConflict = (error: unknown) => {
const normalized = normalizeApiError(error)
return normalized.kind === 'conflict' || normalized.status === 409
}
const handleSubmit = async () => {
if (!currentShop.value || !creditFormRef.value) return
try {
await creditFormRef.value.validate()
} catch {
return
}
submitting.value = true
try {
const res = await ShopService.updateShopCreditLimit(currentShop.value.shop_id, {
credit_enabled: creditForm.credit_enabled,
credit_limit: creditLimitFen.value,
version: currentShop.value.version
})
if (res.code === 0) {
ElMessage.success('实际信用额度调整成功')
dialogVisible.value = false
emit('submitted')
return
}
if (res.code === 409) {
const latest = await loadLatestSummary()
if (latest) {
currentShop.value = latest
syncForm(latest)
}
ElMessage.warning('资金概况已被其他操作更新,已刷新最新版本;请确认后重新提交')
return
}
ElMessage.error(res.msg || '实际信用额度调整失败')
} catch (error) {
if (isConflict(error)) {
const latest = await loadLatestSummary()
if (latest) {
currentShop.value = latest
syncForm(latest)
}
ElMessage.warning('资金概况已被其他操作更新,已刷新最新版本;请确认后重新提交')
} else {
console.error('实际信用额度调整失败:', error)
}
} finally {
submitting.value = false
}
}
</script>
<style scoped lang="scss">
.credit-preview {
margin-bottom: 16px;
}
.credit-dialog-alert {
margin-bottom: 16px;
}
.credit-dialog-tip {
margin-top: 6px;
font-size: 12px;
line-height: 1.4;
color: var(--el-text-color-secondary);
}
</style>