增加试算功能
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/** Quote trial for billing center */
|
||||
export function quoteBilling(data) {
|
||||
return request({
|
||||
url: '/price/billing/quote',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="app-container billing-quote">
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :lg="10">
|
||||
<el-card shadow="never">
|
||||
<template #header><span>试算参数</span></template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="客户ID" prop="customerId">
|
||||
<el-input-number
|
||||
v-model="form.customerId"
|
||||
:min="1"
|
||||
:controls="false"
|
||||
placeholder="请输入客户ID"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="form-hint">与套餐绑定客户ID一致;无套餐时走产品默认价</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品" prop="productCode">
|
||||
<el-select
|
||||
v-model="form.productCode"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择产品"
|
||||
style="width: 100%"
|
||||
@change="handleProductChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in productOptions"
|
||||
:key="item.productCode"
|
||||
:label="item.productName + ' (' + item.productCode + ')'"
|
||||
:value="item.productCode"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="目的国/地区" prop="countryCode">
|
||||
<el-select
|
||||
v-model="form.countryCode"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请先选择产品"
|
||||
style="width: 100%"
|
||||
:disabled="!form.productCode"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in countryOptions"
|
||||
:key="item.countryCode + '-' + (item.detailId || item.weightMin)"
|
||||
:label="countryOptionLabel(item)"
|
||||
:value="item.countryCode"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="计费重量(kg)" prop="weight">
|
||||
<el-input-number
|
||||
v-model="form.weight"
|
||||
:min="0.001"
|
||||
:precision="3"
|
||||
:step="0.1"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务日期" prop="bizDate">
|
||||
<el-date-picker
|
||||
v-model="form.bizDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="默认今天"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loading" @click="handleQuote">计算价格</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :lg="14">
|
||||
<el-card shadow="never">
|
||||
<template #header><span>试算结果</span></template>
|
||||
<el-empty v-if="!result && !errorMsg" description="请输入参数后点击计算价格" />
|
||||
<el-alert v-else-if="errorMsg" :title="errorMsg" type="error" show-icon :closable="false" />
|
||||
<div v-else class="quote-result">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="客户ID">{{ result.customerId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品">{{ result.productName || '-' }} ({{ result.productCode }})</el-descriptions-item>
|
||||
<el-descriptions-item label="国家">{{ result.countryName || '-' }} ({{ result.countryCode }})</el-descriptions-item>
|
||||
<el-descriptions-item label="计费重量(kg)">{{ result.chargeWeight ?? result.weight }}</el-descriptions-item>
|
||||
<el-descriptions-item label="重量段">{{ result.weightLabel || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计价来源">
|
||||
<el-tag size="small" :type="sourceTagType(result.matchSource)">{{ sourceLabel(result.matchSource) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐编号">{{ result.priceSetCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐名称">{{ result.priceSetName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐类型">{{ result.setType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="币种">{{ result.currency || 'RMB' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运费单价">{{ formatMoney(result.freightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="折扣前单价">{{ formatMoney(result.baseFreightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="折扣%">{{ result.discountPercent != null ? result.discountPercent : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运费金额">{{ formatMoney(result.freightAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="挂号费">{{ formatMoney(result.registrationFee) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="应付总额">
|
||||
<span class="payable">{{ formatMoney(result.payableTotal) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="命中说明" :span="2">{{ result.matchMessage || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="formula-tip">计算公式:运费金额 = 运费单价 × 计费重量;应付总额 = 运费金额 + 挂号费</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="BillingQuote">
|
||||
import { quoteBilling } from '@/api/price/billing'
|
||||
import { listProduct, listProductCountries } from '@/api/price/product'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const formRef = ref()
|
||||
const loading = ref(false)
|
||||
const productOptions = ref([])
|
||||
const countryOptions = ref([])
|
||||
const result = ref(null)
|
||||
const errorMsg = ref('')
|
||||
|
||||
const form = reactive({
|
||||
customerId: undefined,
|
||||
productCode: undefined,
|
||||
countryCode: undefined,
|
||||
weight: undefined,
|
||||
bizDate: undefined
|
||||
})
|
||||
|
||||
const rules = {
|
||||
customerId: [{ required: true, message: '请输入客户ID', trigger: 'blur' }],
|
||||
productCode: [{ required: true, message: '请选择产品', trigger: 'change' }],
|
||||
countryCode: [{ required: true, message: '请选择目的国/地区', trigger: 'change' }],
|
||||
weight: [{ required: true, message: '请输入计费重量', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function loadProducts() {
|
||||
listProduct({ pageNum: 1, pageSize: 500, status: '0' }).then(res => {
|
||||
productOptions.value = res.rows || []
|
||||
})
|
||||
}
|
||||
|
||||
function uniqueCountries(list) {
|
||||
const map = new Map()
|
||||
;(list || []).forEach(item => {
|
||||
if (item && item.countryCode && !map.has(item.countryCode)) {
|
||||
map.set(item.countryCode, item)
|
||||
}
|
||||
})
|
||||
return Array.from(map.values())
|
||||
}
|
||||
|
||||
function countryOptionLabel(item) {
|
||||
const name = item.countryName || item.countryCode
|
||||
return name + ' (' + item.countryCode + ')'
|
||||
}
|
||||
|
||||
function handleProductChange(productCode) {
|
||||
form.countryCode = undefined
|
||||
countryOptions.value = []
|
||||
if (!productCode) {
|
||||
return
|
||||
}
|
||||
listProductCountries(productCode).then(res => {
|
||||
countryOptions.value = uniqueCountries(res.data || [])
|
||||
})
|
||||
}
|
||||
|
||||
function handleQuote() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
result.value = null
|
||||
errorMsg.value = ''
|
||||
const payload = {
|
||||
customerId: form.customerId,
|
||||
productCode: form.productCode,
|
||||
countryCode: form.countryCode,
|
||||
weight: form.weight
|
||||
}
|
||||
if (form.bizDate) {
|
||||
payload.bizDate = form.bizDate
|
||||
}
|
||||
quoteBilling(payload).then(res => {
|
||||
result.value = res.data || null
|
||||
if (!result.value) {
|
||||
errorMsg.value = res.msg || '计算失败'
|
||||
}
|
||||
}).catch(err => {
|
||||
errorMsg.value = (err && (err.msg || err.message)) || '计算失败'
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.customerId = undefined
|
||||
form.productCode = undefined
|
||||
form.countryCode = undefined
|
||||
form.weight = undefined
|
||||
form.bizDate = undefined
|
||||
countryOptions.value = []
|
||||
result.value = null
|
||||
errorMsg.value = ''
|
||||
proxy.$nextTick(() => {
|
||||
formRef.value && formRef.value.clearValidate()
|
||||
})
|
||||
}
|
||||
|
||||
function formatMoney(val) {
|
||||
if (val === null || val === undefined || val === '') {
|
||||
return '-'
|
||||
}
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) {
|
||||
return val
|
||||
}
|
||||
return num.toFixed(4)
|
||||
}
|
||||
|
||||
function sourceLabel(source) {
|
||||
const map = {
|
||||
PACKAGE_FIXED: '客户固定价套餐',
|
||||
PACKAGE_DISCOUNT: '客户折扣套餐',
|
||||
PRODUCT_DEFAULT: '产品默认价',
|
||||
FIXED: '客户固定价套餐',
|
||||
DISCOUNT: '客户折扣套餐'
|
||||
}
|
||||
return map[source] || source || '-'
|
||||
}
|
||||
|
||||
function sourceTagType(source) {
|
||||
if (source === 'PACKAGE_FIXED' || source === 'FIXED') return 'success'
|
||||
if (source === 'PACKAGE_DISCOUNT' || source === 'DISCOUNT') return 'warning'
|
||||
if (source === 'PRODUCT_DEFAULT') return 'info'
|
||||
return ''
|
||||
}
|
||||
|
||||
loadProducts()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.billing-quote .form-hint {
|
||||
margin-top: 4px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.billing-quote .payable {
|
||||
color: #f56c6c;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.billing-quote .formula-tip {
|
||||
margin-top: 12px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user