feat(wm-revenue): #54 客服工作台+水费查询+语音自助
- Entity: CsWorkItem, VoiceCallRecord - DTO: CsWorkbenchStats, BillQueryResult, VoiceMenuResponse - Mapper: CsWorkItemMapper, VoiceCallRecordMapper - Service: CsWorkbenchService, BillQueryService, VoiceQueryService - Controller: CsWorkbenchController(9端点), BillQueryController(5端点), VoiceController(7端点) - DDL: V_cs_workbench.sql (2表+索引) - Test: CsWorkbenchTest (10个测试用例)
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="wx-install-apply">
|
||||
<el-page-header @back="$router.back()" title="返回" content="报装申请" />
|
||||
|
||||
<!-- 申请表单 -->
|
||||
<el-card shadow="never" style="margin-top: 16px">
|
||||
<template #header>
|
||||
<span>新建报装申请</span>
|
||||
</template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" style="max-width: 600px">
|
||||
<el-form-item label="申请人" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入申请人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入手机号" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所在区域" prop="area">
|
||||
<el-select v-model="form.area" placeholder="请选择区域" style="width: 100%">
|
||||
<el-option label="城东片区" value="城东片区" />
|
||||
<el-option label="城西片区" value="城西片区" />
|
||||
<el-option label="城南片区" value="城南片区" />
|
||||
<el-option label="城北片区" value="城北片区" />
|
||||
<el-option label="开发区" value="开发区" />
|
||||
<el-option label="高新区" value="高新区" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细地址" prop="address">
|
||||
<el-input v-model="form.address" type="textarea" :rows="2" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用水类型">
|
||||
<el-select v-model="form.customerType" style="width: 100%">
|
||||
<el-option label="居民用水" value="resident" />
|
||||
<el-option label="商业用水" value="commercial" />
|
||||
<el-option label="工业用水" value="industrial" />
|
||||
<el-option label="行政事业" value="public" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="口径">
|
||||
<el-select v-model="form.caliber" style="width: 100%">
|
||||
<el-option label="DN15" value="DN15" />
|
||||
<el-option label="DN20" value="DN20" />
|
||||
<el-option label="DN25" value="DN25" />
|
||||
<el-option label="DN32" value="DN32" />
|
||||
<el-option label="DN40" value="DN40" />
|
||||
<el-option label="DN50" value="DN50" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitting">提交申请</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 进度查询 -->
|
||||
<el-card shadow="never" style="margin-top: 12px">
|
||||
<template #header>
|
||||
<span>进度查询</span>
|
||||
</template>
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="申请编号">
|
||||
<el-input v-model="progressQueryNo" placeholder="输入申请编号查询" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="queryProgress" :disabled="!progressQueryNo">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-steps :active="progressStep" finish-status="success" align-center v-if="progressData"
|
||||
style="margin-top: 20px">
|
||||
<el-step title="预受理" :description="progressData.status === 'pre_apply' ? '当前阶段' : ''" />
|
||||
<el-step title="工程审核" :description="progressData.status === 'engineering' ? '当前阶段' : ''" />
|
||||
<el-step title="派单施工" :description="progressData.status === 'pending_review' ? '当前阶段' : ''" />
|
||||
<el-step title="已完成" :description="progressData.status === 'completed' ? '当前阶段' : ''" />
|
||||
</el-steps>
|
||||
|
||||
<el-descriptions :column="2" border style="margin-top: 16px" v-if="progressData">
|
||||
<el-descriptions-item label="申请编号">{{ progressData.application_no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请人">{{ progressData.applicant_name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ progressData.applicant_phone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="区域">{{ progressData.area }}</el-descriptions-item>
|
||||
<el-descriptions-item label="地址" :span="2">{{ progressData.address }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用水类型">{{ progressData.customer_type }}</el-descriptions-item>
|
||||
<el-descriptions-item label="口径">{{ progressData.caliber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="installStatusTag(progressData.status)">{{ installStatusLabel(progressData.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ progressData.updated_at }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
|
||||
<!-- 申请记录 -->
|
||||
<el-card shadow="never" style="margin-top: 12px">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>申请记录</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :inline="true" style="margin-bottom: 12px">
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="recordPhone" placeholder="输入手机号查询" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="fetchRecords">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="records" border stripe v-loading="recordLoading">
|
||||
<el-table-column prop="application_no" label="申请编号" min-width="180" />
|
||||
<el-table-column prop="applicant_name" label="申请人" width="100" />
|
||||
<el-table-column prop="area" label="区域" width="100" />
|
||||
<el-table-column prop="address" label="地址" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="customer_type" label="用水类型" width="100" />
|
||||
<el-table-column prop="caliber" label="口径" width="80" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="installStatusTag(row.status)" size="small">{{ installStatusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="申请时间" width="170" />
|
||||
<el-table-column label="操作" width="100" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="viewRecordProgress(row)">进度</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { submitInstallApply, getInstallProgress, getInstallList } from '@/api/wx-hall'
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const progressQueryNo = ref('')
|
||||
const progressData = ref<any>(null)
|
||||
const progressStep = ref(0)
|
||||
const recordPhone = ref('')
|
||||
const records = ref<any[]>([])
|
||||
const recordLoading = ref(false)
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
phone: '',
|
||||
area: '',
|
||||
address: '',
|
||||
customerType: 'resident',
|
||||
caliber: 'DN15'
|
||||
})
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入申请人姓名', trigger: 'blur' }],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
|
||||
],
|
||||
area: [{ required: true, message: '请选择区域', trigger: 'change' }],
|
||||
address: [{ required: true, message: '请输入详细地址', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate()
|
||||
submitting.value = true
|
||||
try {
|
||||
const res = await submitInstallApply(form)
|
||||
ElMessage.success(`申请提交成功!申请编号: ${res.data?.applicationNo}`)
|
||||
resetForm()
|
||||
} catch (e) { /* ignore */ } finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formRef.value?.resetFields()
|
||||
Object.assign(form, { name: '', phone: '', area: '', address: '', customerType: 'resident', caliber: 'DN15' })
|
||||
}
|
||||
|
||||
async function queryProgress() {
|
||||
if (!progressQueryNo.value) return
|
||||
try {
|
||||
const res = await getInstallProgress(progressQueryNo.value)
|
||||
progressData.value = res.data
|
||||
progressStep.value = calcStep(res.data?.status)
|
||||
} catch (e) {
|
||||
progressData.value = null
|
||||
progressStep.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchRecords() {
|
||||
if (!recordPhone.value) {
|
||||
ElMessage.warning('请输入手机号')
|
||||
return
|
||||
}
|
||||
recordLoading.value = true
|
||||
try {
|
||||
const res = await getInstallList({ phone: recordPhone.value, limit: 50 })
|
||||
records.value = res.data || []
|
||||
} finally {
|
||||
recordLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function viewRecordProgress(row: any) {
|
||||
progressQueryNo.value = row.application_no
|
||||
queryProgress()
|
||||
}
|
||||
|
||||
function calcStep(status: string): number {
|
||||
const map: Record<string, number> = {
|
||||
pre_apply: 0,
|
||||
engineering: 1,
|
||||
pending_review: 2,
|
||||
completed: 3
|
||||
}
|
||||
return map[status] ?? 0
|
||||
}
|
||||
|
||||
function installStatusLabel(s: string): string {
|
||||
const map: Record<string, string> = {
|
||||
pre_apply: '预受理',
|
||||
engineering: '工程审核',
|
||||
pending_review: '派单施工',
|
||||
completed: '已完成'
|
||||
}
|
||||
return map[s] || s
|
||||
}
|
||||
|
||||
function installStatusTag(s: string): any {
|
||||
const map: Record<string, string> = {
|
||||
pre_apply: 'info',
|
||||
engineering: 'warning',
|
||||
pending_review: '',
|
||||
completed: 'success'
|
||||
}
|
||||
return map[s] || 'info'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.wx-install-apply {
|
||||
padding: 20px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user