Files
nodejs/exam/frontend/src/views/quote/QuoteForm.vue
T
2026-08-21 11:19:36 +08:00

275 lines
8.1 KiB
Vue

<template>
<div class="page-container">
<el-card>
<template #header>
<span>{{ isEdit ? '编辑报价' : '创建报价' }}</span>
</template>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="120px"
style="max-width: 700px"
>
<el-form-item label="选择车辆" prop="vehicle_id">
<el-select
v-model="form.vehicle_id"
placeholder="请选择车辆"
style="width: 100%"
@change="handleVehicleChange"
:disabled="isEdit"
>
<el-option
v-for="vehicle in vehicleList"
:key="vehicle.id"
:label="`${vehicle.license_plate} - ${vehicle.brand} ${vehicle.model}`"
:value="vehicle.id"
/>
</el-select>
</el-form-item>
<el-form-item label="上年出险次数" prop="accident_count">
<el-input-number
v-model="form.accident_count"
:min="0"
:max="10"
style="width: 200px"
@change="handleAccidentChange"
/>
</el-form-item>
<el-divider content-position="left">商业险选择</el-divider>
<el-form-item label="车损险">
<el-switch v-model="form.has_damage" active-text="投保" inactive-text="不投" />
</el-form-item>
<el-form-item label="第三者责任险">
<el-switch v-model="form.has_third_party" active-text="投保" inactive-text="不投" />
</el-form-item>
<el-form-item label="三者保额" v-if="form.has_third_party">
<el-radio-group v-model="form.third_party_amount">
<el-radio :value="500000">50万</el-radio>
<el-radio :value="1000000">100万</el-radio>
<el-radio :value="2000000">200万</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="盗抢险">
<el-switch v-model="form.has_theft" active-text="投保" inactive-text="不投" />
</el-form-item>
<el-form-item label="有效期至" prop="valid_until">
<el-date-picker
v-model="form.valid_until"
type="date"
placeholder="请选择有效期"
value-format="YYYY-MM-DD"
style="width: 200px"
/>
</el-form-item>
<el-divider content-position="left">保费预览</el-divider>
<el-descriptions :column="2" border v-if="selectedVehicle">
<el-descriptions-item label="车辆">
{{ selectedVehicle.license_plate }} {{ selectedVehicle.brand }} {{ selectedVehicle.model }}
</el-descriptions-item>
<el-descriptions-item label="购置价">{{ selectedVehicle.purchase_price.toLocaleString() }} 元</el-descriptions-item>
<el-descriptions-item label="交强险">
<span class="premium">{{ compulsoryPremium.toFixed(2) }} 元</span>
</el-descriptions-item>
<el-descriptions-item label="NCD系数">
<span class="premium">{{ ncdRatio }}</span>
</el-descriptions-item>
<el-descriptions-item label="商业险">
<span class="premium">{{ commercialPremium.toFixed(2) }} 元</span>
</el-descriptions-item>
<el-descriptions-item label="总保费">
<span class="total">{{ totalPremium.toFixed(2) }} 元</span>
</el-descriptions-item>
</el-descriptions>
<el-form-item style="margin-top: 20px">
<el-button type="primary" :loading="submitting" @click="handleSubmit">
{{ isEdit ? '保存修改' : '创建报价' }}
</el-button>
<el-button @click="handleBack">返回</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { getQuote, createQuote, updateQuote } from '../../api/quote'
import { getVehicles } from '../../api/vehicle'
import type { Vehicle } from '../../types'
const router = useRouter()
const route = useRoute()
const formRef = ref<FormInstance>()
const isEdit = computed(() => !!route.params.id)
const form = reactive({
vehicle_id: undefined as number | undefined,
accident_count: 0,
has_damage: true,
has_third_party: true,
third_party_amount: 1000000,
has_theft: true,
valid_until: '',
})
const rules: FormRules = {
vehicle_id: [{ required: true, message: '请选择车辆', trigger: 'change' }],
}
const submitting = ref(false)
const vehicleList = ref<Vehicle[]>([])
const selectedVehicle = ref<Vehicle | null>(null)
// 保费计算(与后端一致)
function getNcdRatio(accidentCount: number): number {
if (accidentCount === 0) return 0.7
if (accidentCount === 1) return 0.8
if (accidentCount === 2) return 1.0
if (accidentCount === 3) return 1.1
return 1.2
}
function getCompulsoryPremium(seatCount: number): number {
return seatCount <= 6 ? 950 : 1100
}
const ncdRatio = computed(() => getNcdRatio(form.accident_count))
const compulsoryPremium = computed(() => {
if (!selectedVehicle.value) return 0
return getCompulsoryPremium(selectedVehicle.value.seat_count)
})
const commercialPremium = computed(() => {
if (!selectedVehicle.value) return 0
const price = selectedVehicle.value.purchase_price
let total = 0
if (form.has_damage) {
total += price * 0.015
}
if (form.has_third_party) {
const amount = form.third_party_amount
if (amount === 500000) total += 800
else if (amount === 1000000) total += 1200
else if (amount === 2000000) total += 1800
}
if (form.has_theft) {
total += price * 0.005
}
return total
})
const totalPremium = computed(() => {
return (compulsoryPremium.value + commercialPremium.value) * ncdRatio.value
})
async function loadVehicles() {
try {
const res = await getVehicles({ page: 1, pageSize: 100 })
vehicleList.value = res.data.list
} catch (e: any) {
ElMessage.error(e.message || '获取车辆列表失败')
}
}
function handleVehicleChange(vehicleId: number) {
selectedVehicle.value = vehicleList.value.find(v => v.id === vehicleId) || null
}
function handleAccidentChange(value: number) {
form.accident_count = value
}
async function loadDetail() {
const id = Number(route.params.id)
try {
const res = await getQuote(id)
Object.assign(form, {
vehicle_id: res.data.vehicle_id,
accident_count: res.data.accident_count,
has_damage: res.data.commercial_premium > 0,
has_third_party: true,
third_party_amount: 1000000,
has_theft: res.data.commercial_premium > 0,
valid_until: res.data.valid_until,
})
const vehicle = vehicleList.value.find(v => v.id === res.data.vehicle_id)
if (vehicle) {
selectedVehicle.value = vehicle
}
} catch (e: any) {
ElMessage.error(e.message || '获取报价信息失败')
}
}
async function handleSubmit() {
if (!formRef.value) return
const valid = await formRef.value.validate().catch(() => false)
if (!valid) return
submitting.value = true
try {
const data = {
vehicle_id: form.vehicle_id,
accident_count: form.accident_count,
has_damage: form.has_damage,
has_third_party: form.has_third_party,
third_party_amount: form.third_party_amount,
has_theft: form.has_theft,
valid_until: form.valid_until,
}
if (isEdit.value) {
await updateQuote(Number(route.params.id), data)
ElMessage.success('保存成功')
} else {
await createQuote(data)
ElMessage.success('报价创建成功')
}
router.push('/quotes')
} catch (e: any) {
ElMessage.error(e.message || '操作失败')
} finally {
submitting.value = false
}
}
function handleBack() {
router.push('/quotes')
}
onMounted(async () => {
await loadVehicles()
if (isEdit.value) {
await loadDetail()
}
})
</script>
<style scoped>
.premium {
font-weight: 600;
color: #409eff;
}
.total {
font-weight: 700;
color: #e6a23c;
font-size: 18px;
}
</style>