Files
by-crm/frontend/src/views/Report.vue
T
2026-01-28 16:04:49 +08:00

526 lines
16 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>
<div class="report-page">
<el-card>
<template #header>
<div class="card-header">
<div>
<span>报备管理</span>
<el-tag type="info" size="small" style="margin-left: 10px">
保护期:{{ protectDays }} 天
</el-tag>
</div>
<el-button type="primary" @click="handleAdd" v-if="!userStore.isAdmin">
<el-icon><Plus /></el-icon>
提交报备
</el-button>
</div>
</template>
<!-- 查询表单 -->
<el-form :inline="true" :model="queryForm" class="query-form">
<el-form-item label="客户名称">
<el-input v-model="queryForm.customerName" placeholder="请输入客户名称" clearable />
</el-form-item>
<el-form-item label="状态" style="width: 180px;">
<el-select v-model="queryForm.status" placeholder="请选择状态" clearable>
<el-option label="待审核" :value="0" />
<el-option label="已通过" :value="1" />
<el-option label="已驳回" :value="2" />
<el-option label="已失效" :value="3" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<el-icon><Search /></el-icon>
查询
</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table :data="tableData" border stripe v-loading="loading">
<el-table-column prop="dealerName" label="经销商" />
<el-table-column prop="customerName" label="客户名称" />
<el-table-column prop="customerPhone" label="联系电话" />
<el-table-column prop="description" label="报备说明" show-overflow-tooltip />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag v-if="row.status === 0" type="warning">待审核</el-tag>
<el-tag v-else-if="row.status === 1" type="success">已通过</el-tag>
<el-tag v-else-if="row.status === 2" type="danger">已驳回</el-tag>
<el-tag v-else type="info">已失效</el-tag>
</template>
</el-table-column>
<el-table-column prop="protectEndDate" label="保护期截止" width="180">
<template #default="{ row }">
{{ formatDate(row.protectEndDate) }}
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" width="180" />
<el-table-column label="操作" width="250" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="handleView(row)">查看</el-button>
<el-button
v-if="row.status === 1"
link
type="info"
@click="handleShowProgress(row)"
>
进展
</el-button>
<el-button
v-if="userStore.isAdmin && row.status === 0"
link
type="success"
@click="handleAudit(row)"
>
审核
</el-button>
<el-button
v-if="!userStore.isAdmin && row.status === 0"
link
type="danger"
@click="handleWithdraw(row)"
>
撤回
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-config-provider :locale="zhCn">
<el-pagination
v-model:current-page="queryForm.current"
v-model:page-size="queryForm.size"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
prev-text="上一页"
next-text="下一页"
@size-change="fetchData"
@current-change="fetchData"
style="margin-top: 20px; justify-content: flex-end"
/>
</el-config-provider>
</el-card>
<!-- 提交报备对话框 -->
<el-dialog v-model="dialogVisible" title="提交报备" width="600px" @close="handleDialogClose">
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="客户" prop="customerId">
<el-select
v-model="formData.customerId"
placeholder="请选择客户"
filterable
remote
:remote-method="searchCustomer"
:loading="searchLoading"
style="width: 100%"
>
<el-option
v-for="item in customerOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="报备说明" prop="description">
<el-input
v-model="formData.description"
type="textarea"
:rows="4"
placeholder="请输入报备说明"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
<!-- 审核对话框 -->
<el-dialog v-model="auditDialogVisible" title="审核报备" width="600px">
<el-form :model="auditForm" label-width="100px">
<el-form-item label="审核结果">
<el-radio-group v-model="auditForm.approved">
<el-radio :label="true">通过</el-radio>
<el-radio :label="false">驳回</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="驳回原因" prop="rejectReason" v-if="!auditForm.approved">
<el-input
v-model="auditForm.rejectReason"
type="textarea"
:rows="4"
placeholder="请输入驳回原因"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="auditDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleAuditSubmit">确定</el-button>
</template>
</el-dialog>
<!-- 进展记录对话框 -->
<el-dialog v-model="progressDialogVisible" title="报备进展记录" width="1000px" @close="handleProgressDialogClose">
<el-card>
<template #header>
<div class="card-header">
<span>添加进展记录</span>
</div>
</template>
<el-form ref="progressFormRef" :model="progressFormData" :rules="progressRules" label-width="100px">
<el-form-item label="进展内容" prop="progressContent">
<el-input
v-model="progressFormData.progressContent"
type="textarea"
:rows="4"
placeholder="请输入进展内容"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button v-if="editingProgressId" @click="handleCancelEdit">取消编辑</el-button>
<el-button type="primary" @click="handleSaveProgress">{{ editingProgressId ? '保存修改' : '添加' }}</el-button>
</template>
</el-card>
<el-divider content-position="left">进展记录列表</el-divider>
<el-table :data="progressList" border stripe v-loading="progressLoading">
<el-table-column prop="progressContent" label="进展内容" show-overflow-tooltip />
<el-table-column prop="createdAt" label="创建时间" width="180" />
<el-table-column prop="updatedAt" label="更新时间" width="180" />
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button
v-if="!userStore.isAdmin"
link
type="primary"
@click="handleEditProgress(row)"
>
编辑
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { useUserStore } from '@/stores/user'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { getReportPage, createReport, auditReport, withdrawReport } from '@/api/report'
import { getProgressByReportId, createProgress, updateProgress } from '@/api/report-progress'
import { searchCustomerByName } from '@/api/customer'
import { getConfigValue } from '@/api/system'
import type { Report, ReportForm, ReportProgress, ReportProgressForm } from '@/types'
const userStore = useUserStore()
const protectDays = ref<number>(90)
const loading = ref(false)
const tableData = ref<Report[]>([])
const total = ref(0)
const dialogVisible = ref(false)
const auditDialogVisible = ref(false)
const auditReportId = ref<number>()
const formRef = ref<FormInstance>()
const searchLoading = ref(false)
const customerOptions = ref<Array<{ id: number; name: string }>>([])
// 进展记录相关
const progressDialogVisible = ref(false)
const progressFormRef = ref<FormInstance>()
const progressFormData = reactive<ReportProgressForm>({
reportId: 0,
progressContent: ''
})
const progressList = ref<ReportProgress[]>([])
const editingProgressId = ref<number | undefined>()
const progressLoading = ref(false)
const progressRules: FormRules = {
progressContent: [{ required: true, message: '请输入进展内容', trigger: 'blur' }]
}
const queryForm = reactive({
current: 1,
size: 10,
customerName: '',
status: undefined as number | undefined
})
const formData = reactive<ReportForm>({
customerId: null,
description: ''
})
const auditForm = reactive({
approved: true,
rejectReason: ''
})
const rules: FormRules = {
customerId: [{ required: true, message: '请选择客户', trigger: 'change' }]
}
// 格式化日期,去掉时分秒
const formatDate = (dateStr: string | null | undefined) => {
if (!dateStr) return '-'
return dateStr.split(' ')[0] // 只保留日期部分,去掉时间
}
// 显示进展记录对话框
const handleShowProgress = async (row: Report) => {
progressFormData.reportId = row.id
progressFormData.progressContent = ''
editingProgressId.value = undefined
await fetchProgressList(row.id)
progressDialogVisible.value = true
}
// 获取进展记录列表
const fetchProgressList = async (reportId: number) => {
progressLoading.value = true
try {
const res = await getProgressByReportId(reportId)
progressList.value = res
} catch (error) {
console.error('获取进展记录失败', error)
progressList.value = []
} finally {
progressLoading.value = false
}
}
// 保存进展记录
const handleSaveProgress = async () => {
if (!progressFormRef.value) return
await progressFormRef.value.validate(async (valid) => {
if (valid) {
try {
if (editingProgressId.value) {
// 编辑模式
await updateProgress(editingProgressId.value, progressFormData)
ElMessage.success('更新成功')
} else {
// 新增模式
await createProgress(progressFormData)
ElMessage.success('添加成功')
}
// 重新获取进展记录列表
await fetchProgressList(progressFormData.reportId)
// 重置表单
progressFormData.progressContent = ''
editingProgressId.value = undefined
} catch (error) {
console.error('保存进展记录失败', error)
}
}
})
}
// 编辑进展记录
const handleEditProgress = (row: ReportProgress) => {
editingProgressId.value = row.id
progressFormData.progressContent = row.progressContent
}
// 取消编辑
const handleCancelEdit = () => {
editingProgressId.value = undefined
progressFormData.progressContent = ''
}
// 关闭进展记录对话框
const handleProgressDialogClose = () => {
progressDialogVisible.value = false
progressFormRef.value?.resetFields()
progressFormData.progressContent = ''
editingProgressId.value = undefined
progressList.value = []
}
// 查询数据
const fetchData = async () => {
loading.value = true
try {
const res = await getReportPage(queryForm)
tableData.value = res.records
total.value = res.total
} catch (error) {
console.error('查询失败', error)
} finally {
loading.value = false
}
}
// 搜索客户
const searchCustomer = async (query: string) => {
if (!query) return
searchLoading.value = true
try {
const res = await searchCustomerByName(query)
customerOptions.value = res
} catch (error) {
console.error('搜索客户失败', error)
} finally {
searchLoading.value = false
}
}
// 查询按钮
const handleQuery = () => {
queryForm.current = 1
fetchData()
}
// 重置按钮
const handleReset = () => {
queryForm.customerName = ''
queryForm.status = undefined
queryForm.current = 1
fetchData()
}
// 新增按钮
const handleAdd = () => {
dialogVisible.value = true
}
// 查看详情
const handleView = (row: Report) => {
ElMessageBox.alert(
`
<div style="line-height: 2;">
<p><strong>经销商:</strong>${row.dealerName}</p>
<p><strong>客户名称:</strong>${row.customerName}</p>
<p><strong>联系电话:</strong>${row.customerPhone || '-'}</p>
<p><strong>报备说明:</strong>${row.description || '-'}</p>
<p><strong>状态:</strong>${row.statusDesc}</p>
<p><strong>驳回原因:</strong>${row.rejectReason || '-'}</p>
<p><strong>保护期:</strong>${formatDate(row.protectStartDate)} ~ ${formatDate(row.protectEndDate)}</p>
</div>
`,
'报备详情',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '关闭'
}
)
}
// 审核按钮
const handleAudit = (row: Report) => {
auditReportId.value = row.id
auditForm.approved = true
auditForm.rejectReason = ''
auditDialogVisible.value = true
}
// 撤回按钮
const handleWithdraw = async (row: Report) => {
try {
await ElMessageBox.confirm(
'确定要撤回该报备吗?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
await withdrawReport(row.id)
ElMessage.success('撤回成功')
fetchData()
} catch (error) {
// 取消撤回
}
}
// 提交报备
const handleSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (valid) {
try {
await createReport(formData)
ElMessage.success('提交成功')
dialogVisible.value = false
fetchData()
} catch (error) {
console.error('提交失败', error)
}
}
})
}
// 审核提交
const handleAuditSubmit = async () => {
if (!auditReportId.value) return
if (!auditForm.approved && !auditForm.rejectReason) {
ElMessage.warning('请输入驳回原因')
return
}
try {
await auditReport(auditReportId.value, auditForm)
ElMessage.success('审核成功')
auditDialogVisible.value = false
fetchData()
} catch (error) {
console.error('审核失败', error)
}
}
// 对话框关闭
const handleDialogClose = () => {
formRef.value?.resetFields()
Object.assign(formData, {
customerId: null,
description: ''
})
customerOptions.value = []
}
// 获取保护期配置
const fetchProtectDays = async () => {
try {
const res = await getConfigValue('report.protect.days')
protectDays.value = parseInt(res) || 90
} catch (error) {
console.error('获取配置失败', error)
}
}
onMounted(() => {
fetchProtectDays()
fetchData()
})
</script>
<style scoped>
.report-page {
width: 100%;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.query-form {
margin-bottom: 20px;
}
</style>