Files
mom-web/src/views/erp/aftersale/aftersalesvisit/index.vue
2026-03-14 13:51:02 +08:00

307 lines
11 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>
<!-- 统计卡片 -->
<ContentWrap>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
<SummaryCard title="总回访数" :value="total" icon="ep:user" icon-color="text-blue-600" icon-bg-color="bg-blue-100" />
<SummaryCard title="平均评分" :value="avgRating" :decimals="1" icon="ep:star" icon-color="text-yellow-600"
icon-bg-color="bg-yellow-100" />
<SummaryCard title="今日新增" :value="todayCount" icon="ep:calendar" icon-color="text-green-600"
icon-bg-color="bg-green-100" />
<SummaryCard title="五星评分" :value="fiveStarCount" icon="ep:star" icon-color="text-yellow-600"
icon-bg-color="bg-yellow-100" />
</div>
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="80px">
<el-form-item label="客户名称" prop="customerName">
<el-input v-model="queryParams.customerName" placeholder="请输入客户名称" clearable @keyup.enter="handleQuery"
class="!w-200px" />
</el-form-item>
<el-form-item label="联系方式" prop="contactInfo">
<el-input v-model="queryParams.contactInfo" placeholder="请输入联系方式" clearable @keyup.enter="handleQuery"
class="!w-200px" />
</el-form-item>
<el-form-item label="产品名称" prop="productName">
<el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable @keyup.enter="handleQuery"
class="!w-200px" />
</el-form-item>
<el-form-item label="客户类型" prop="customerType">
<el-select v-model="queryParams.customerType" placeholder="请选择客户类型" clearable class="!w-200px">
<el-option v-for="item in customerTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="采购意愿" prop="repurchaseIntention">
<el-select v-model="queryParams.repurchaseIntention" placeholder="请选择采购意愿" clearable class="!w-200px">
<el-option v-for="item in repurchaseOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
start-placeholder="开始日期" end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-220px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置
</el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<div class="flex flex-wrap gap-2 mb-4">
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['erp:after-sales-visit:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['erp:after-sales-visit:export']">
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch"
v-hasPermi="['erp:after-sales-visit:delete']">
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
</div>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table row-key="id" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
@selection-change="handleRowCheckboxChange" class="mb-4">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="客户名称" min-width="120" prop="customerName" show-overflow-tooltip />
<el-table-column label="联系方式" min-width="120" prop="contactInfo" show-overflow-tooltip />
<el-table-column label="产品名称" min-width="120" prop="productName" show-overflow-tooltip />
<el-table-column label="客户类型" min-width="140" align="center">
<template #default="scope">
<el-tag :type="getCustomerTypeColor(scope.row.customerType)">
{{ getCustomerTypeLabel(scope.row.customerType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="采购意愿" min-width="100" align="center">
<template #default="scope">
<span :class="getRepurchaseColor(scope.row.repurchaseIntention)">
{{ getRepurchaseLabel(scope.row.repurchaseIntention) }}
</span>
</template>
</el-table-column>
<el-table-column label="服务评分" width="120" align="center">
<template #default="scope">
<el-rate :model-value="scope.row.serviceRating" disabled :max="5" show-score text-color="#ff9900"
score-template="{value}" />
</template>
</el-table-column>
<el-table-column label="创建时间" width="160" align="center" prop="createTime" :formatter="dateFormatter" />
<el-table-column label="操作" width="140" align="center" fixed="right">
<template #default="scope">
<el-button link type="primary" size="small" @click="openForm('update', scope.row.id)"
v-hasPermi="['erp:after-sales-visit:update']">
<Icon icon="ep:edit" class="mr-1" />编辑
</el-button>
<el-button link type="danger" size="small" @click="handleDelete(scope.row.id)"
v-hasPermi="['erp:after-sales-visit:delete']">
<Icon icon="ep:delete" class="mr-1" />删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<AfterSalesVisitForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { isEmpty } from '@/utils/is'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import SummaryCard from '@/components/SummaryCard/index.vue'
import { AfterSalesVisitApi, AfterSalesVisit } from '@/api/erp/aftersale/aftersalesvisit'
import AfterSalesVisitForm from './AfterSalesVisitForm.vue'
/** 售后回访 列表 */
defineOptions({ name: 'AfterSalesVisit' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<AfterSalesVisit[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const activeNames = ref(['search']) // 折叠面板默认展开
// 客户类型选项
const customerTypeOptions = [
{ value: 'large_food_factory', label: '大型食品厂' },
{ value: 'small_food_factory', label: '中小型加工厂' },
{ value: 'distributor', label: '经销商' },
{ value: 'catering_chain', label: '餐饮连锁' },
{ value: 'government_unit', label: '机关单位' },
{ value: 'other', label: '其他' }
]
// 重复采购意愿选项
const repurchaseOptions = [
{ value: 'definitely', label: '一定会' },
{ value: 'probably', label: '应该会' },
{ value: 'uncertain', label: '不确定' },
{ value: 'unlikely', label: '可能不会' },
{ value: 'never', label: '肯定不会' }
]
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
customerName: undefined,
contactInfo: undefined,
customerUsage: undefined,
productName: undefined,
productEvaluation: undefined,
customerType: undefined,
repurchaseIntention: undefined,
serviceRating: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await AfterSalesVisitApi.getAfterSalesVisitPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
// 重置评分查询条件
queryParams.serviceRating = undefined
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await AfterSalesVisitApi.deleteAfterSalesVisit(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch { }
}
/** 批量删除售后回访 */
const handleDeleteBatch = async () => {
try {
// 删除的二次确认
await message.delConfirm()
await AfterSalesVisitApi.deleteAfterSalesVisitList(checkedIds.value);
message.success(t('common.delSuccess'))
await getList();
} catch { }
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: AfterSalesVisit[]) => {
checkedIds.value = records.map((item) => item.id);
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await AfterSalesVisitApi.exportAfterSalesVisit(queryParams)
download.excel(data, '售后回访.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 统计数据计算 */
const avgRating = computed(() => {
if (list.value.length === 0) return 0
const sum = list.value.reduce((acc, item) => acc + (item.serviceRating || 0), 0)
return sum / list.value.length
})
const todayCount = computed(() => {
const today = new Date().toISOString().split('T')[0]
return list.value.filter(item => {
const createDate = new Date(item.createTime).toISOString().split('T')[0]
return createDate === today
}).length
})
const fiveStarCount = computed(() => {
return list.value.filter(item => (item.serviceRating || 0) === 5).length
})
/** 格式化函数 */
const getCustomerTypeLabel = (value: string) => {
const option = customerTypeOptions.find(item => item.value === value)
return option ? option.label : value
}
const getCustomerTypeColor = (value: string) => {
const colorMap = {
'large_food_factory': 'success',
'small_food_factory': 'info',
'distributor': 'warning',
'catering_chain': 'danger',
'government_unit': 'primary',
'other': ''
}
return colorMap[value as keyof typeof colorMap] || ''
}
const getRepurchaseLabel = (value: string) => {
const option = repurchaseOptions.find(item => item.value === value)
return option ? option.label : value
}
const getRepurchaseColor = (value: string) => {
const colorMap = {
'definitely': 'text-green-600 font-medium',
'probably': 'text-blue-600',
'uncertain': 'text-yellow-600',
'unlikely': 'text-orange-600',
'never': 'text-red-600'
}
return colorMap[value as keyof typeof colorMap] || ''
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>