fix:轮胎数据汇总
This commit is contained in:
38
.env.dev
Normal file
38
.env.dev
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# 开发环境:本地只启动前端项目,依赖开发环境(后端、APP)
|
||||||
|
NODE_ENV=production
|
||||||
|
|
||||||
|
VITE_DEV=true
|
||||||
|
|
||||||
|
# 请求路径
|
||||||
|
VITE_BASE_URL='http://localhost:48080'
|
||||||
|
#VITE_BASE_URL='https://test.zmingzhikeji.cn'
|
||||||
|
|
||||||
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
|
VITE_UPLOAD_TYPE=client
|
||||||
|
|
||||||
|
# 上传路径
|
||||||
|
VITE_UPLOAD_URL='https://www.zmingzhikeji.cn/admin-api/infra/file/upload'
|
||||||
|
|
||||||
|
# 接口地址
|
||||||
|
VITE_API_URL=/admin-api
|
||||||
|
|
||||||
|
# 是否删除debugger
|
||||||
|
VITE_DROP_DEBUGGER=false
|
||||||
|
|
||||||
|
# 是否删除console.log
|
||||||
|
VITE_DROP_CONSOLE=false
|
||||||
|
|
||||||
|
# 是否sourcemap
|
||||||
|
VITE_SOURCEMAP=true
|
||||||
|
|
||||||
|
# 打包路径
|
||||||
|
VITE_BASE_PATH=/
|
||||||
|
|
||||||
|
# 输出路径
|
||||||
|
VITE_OUT_DIR=dist
|
||||||
|
|
||||||
|
# 商城H5会员端域名
|
||||||
|
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
|
||||||
|
|
||||||
|
# 验证码的开关
|
||||||
|
VITE_APP_CAPTCHA_ENABLE=true
|
||||||
68
src/api/car/renewalorder/index.ts
Normal file
68
src/api/car/renewalorder/index.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// 车辆续保订单 VO
|
||||||
|
export interface RenewalOrderVO {
|
||||||
|
id: number // 主键ID
|
||||||
|
carBrand: string // 汽车品牌
|
||||||
|
carModel: string // 车型
|
||||||
|
licensePlate: string // 车牌号
|
||||||
|
factoryModel: string // 厂牌型号
|
||||||
|
invoiceAmount: number // 发票金额
|
||||||
|
purchaseMileage: number // 购买时公里数
|
||||||
|
engineNo: string // 发动机号
|
||||||
|
vin: string // 车架号
|
||||||
|
invoiceDate: Date // 发票日期
|
||||||
|
invoiceUrl: string // 发票图片地址
|
||||||
|
serviceBuyer: string // 服务购买方
|
||||||
|
carBuyer: string // 车辆购买方
|
||||||
|
certType: string // 证件类型
|
||||||
|
mobile: string // 联系电话
|
||||||
|
certNo: string // 证件号码
|
||||||
|
contactAddress: string // 联系地址
|
||||||
|
memberEmail: string // 会员邮箱
|
||||||
|
storeId: number // 门店ID(关联 tire_store.id)
|
||||||
|
storeName?: string // 门店名称
|
||||||
|
productId: number // 续保产品ID(关联 car_renewal_product.id)
|
||||||
|
serviceProduct: string // 服务产品
|
||||||
|
productValidity: string // 产品时效
|
||||||
|
originalWarrantyYears: string // 原厂质保时长
|
||||||
|
originalWarrantyMileage: string // 原厂质保里程
|
||||||
|
productFee: number // 产品费用
|
||||||
|
settlementMethod: string // 结算方式
|
||||||
|
remark: string // 备注
|
||||||
|
inputUser: string // 录单人
|
||||||
|
contractRemark: string // 合同备注
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆续保订单 API
|
||||||
|
export const RenewalOrderApi = {
|
||||||
|
// 查询车辆续保订单分页
|
||||||
|
getRenewalOrderPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/car/renewal-order/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询车辆续保订单详情
|
||||||
|
getRenewalOrder: async (id: number) => {
|
||||||
|
return await request.get({ url: `/car/renewal-order/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增车辆续保订单
|
||||||
|
createRenewalOrder: async (data: RenewalOrderVO) => {
|
||||||
|
return await request.post({ url: `/car/renewal-order/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改车辆续保订单
|
||||||
|
updateRenewalOrder: async (data: RenewalOrderVO) => {
|
||||||
|
return await request.put({ url: `/car/renewal-order/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除车辆续保订单
|
||||||
|
deleteRenewalOrder: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/car/renewal-order/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 导出车辆续保订单 Excel
|
||||||
|
exportRenewalOrder: async (params) => {
|
||||||
|
return await request.download({ url: `/car/renewal-order/export-excel`, params })
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/api/car/renewalproduct/index.ts
Normal file
43
src/api/car/renewalproduct/index.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// 车辆续保产品信息 VO
|
||||||
|
export interface RenewalProductVO {
|
||||||
|
id: number // 主键ID
|
||||||
|
productName: string // 产品名称
|
||||||
|
productContent: string // 产品内容
|
||||||
|
productType: string // 产品类别(car_renewal_product_type:00 无忧,01 延保)
|
||||||
|
remark: string // 备注
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆续保产品信息 API
|
||||||
|
export const RenewalProductApi = {
|
||||||
|
// 查询车辆续保产品信息分页
|
||||||
|
getRenewalProductPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/car/renewal-product/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询车辆续保产品信息详情
|
||||||
|
getRenewalProduct: async (id: number) => {
|
||||||
|
return await request.get({ url: `/car/renewal-product/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增车辆续保产品信息
|
||||||
|
createRenewalProduct: async (data: RenewalProductVO) => {
|
||||||
|
return await request.post({ url: `/car/renewal-product/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改车辆续保产品信息
|
||||||
|
updateRenewalProduct: async (data: RenewalProductVO) => {
|
||||||
|
return await request.put({ url: `/car/renewal-product/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除车辆续保产品信息
|
||||||
|
deleteRenewalProduct: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/car/renewal-product/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 导出车辆续保产品信息 Excel
|
||||||
|
exportRenewalProduct: async (params) => {
|
||||||
|
return await request.download({ url: `/car/renewal-product/export-excel`, params })
|
||||||
|
}
|
||||||
|
}
|
||||||
604
src/views/Home/Index2.vue
Normal file
604
src/views/Home/Index2.vue
Normal file
@@ -0,0 +1,604 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<ContentWrap class="mb-20px">
|
||||||
|
<el-form
|
||||||
|
class="-mb-15px"
|
||||||
|
:model="queryParams"
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item label="时间范围" prop="times">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.times"
|
||||||
|
:shortcuts="defaultShortcuts"
|
||||||
|
class="!w-240px"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="门店" prop="storeId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.storeId"
|
||||||
|
placeholder="请选择门店"
|
||||||
|
clearable
|
||||||
|
class="!w-240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="store in storeList"
|
||||||
|
:key="store.id"
|
||||||
|
:label="store.name"
|
||||||
|
:value="store.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @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>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 统计卡片 -->
|
||||||
|
<el-row :class="prefixCls" :gutter="20" justify="space-between">
|
||||||
|
<el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
|
||||||
|
<el-card class="mb-20px" shadow="hover">
|
||||||
|
<el-skeleton :loading="loading" :rows="2" animated>
|
||||||
|
<template #default>
|
||||||
|
<div :class="`${prefixCls}__item flex justify-between`">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
:class="`${prefixCls}__item--icon ${prefixCls}__item--money p-16px inline-block rounded-6px`"
|
||||||
|
>
|
||||||
|
<Icon :size="40" icon="svg-icon:money" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">
|
||||||
|
总订单金额
|
||||||
|
</div>
|
||||||
|
<CountTo
|
||||||
|
:duration="2600"
|
||||||
|
:end-val="summaryData.totalAmount || 0"
|
||||||
|
:start-val="0"
|
||||||
|
:decimals="2"
|
||||||
|
prefix="¥"
|
||||||
|
class="text-right text-20px font-700"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
|
||||||
|
<el-card class="mb-20px" shadow="hover">
|
||||||
|
<el-skeleton :loading="loading" :rows="2" animated>
|
||||||
|
<template #default>
|
||||||
|
<div :class="`${prefixCls}__item flex justify-between`">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
:class="`${prefixCls}__item--icon ${prefixCls}__item--shopping p-16px inline-block rounded-6px`"
|
||||||
|
>
|
||||||
|
<Icon :size="40" icon="svg-icon:shopping" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">
|
||||||
|
总订单数
|
||||||
|
</div>
|
||||||
|
<CountTo
|
||||||
|
:duration="2600"
|
||||||
|
:end-val="summaryData.totalOrderCount || 0"
|
||||||
|
:start-val="0"
|
||||||
|
class="text-right text-20px font-700"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
|
||||||
|
<el-card class="mb-20px" shadow="hover">
|
||||||
|
<el-skeleton :loading="loading" :rows="2" animated>
|
||||||
|
<template #default>
|
||||||
|
<div :class="`${prefixCls}__item flex justify-between`">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
:class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`"
|
||||||
|
>
|
||||||
|
<Icon :size="40" icon="svg-icon:peoples" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">
|
||||||
|
平均订单金额
|
||||||
|
</div>
|
||||||
|
<CountTo
|
||||||
|
:duration="2600"
|
||||||
|
:end-val="summaryData.averageOrderAmount || 0"
|
||||||
|
:start-val="0"
|
||||||
|
:decimals="2"
|
||||||
|
prefix="¥"
|
||||||
|
class="text-right text-20px font-700"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
|
||||||
|
<el-card class="mb-20px" shadow="hover">
|
||||||
|
<el-skeleton :loading="loading" :rows="2" animated>
|
||||||
|
<template #default>
|
||||||
|
<div :class="`${prefixCls}__item flex justify-between`">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
:class="`${prefixCls}__item--icon ${prefixCls}__item--message p-16px inline-block rounded-6px`"
|
||||||
|
>
|
||||||
|
<Icon :size="40" icon="svg-icon:message" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">
|
||||||
|
活跃门店数
|
||||||
|
</div>
|
||||||
|
<CountTo
|
||||||
|
:duration="2600"
|
||||||
|
:end-val="summaryData.activeStoreCount || 0"
|
||||||
|
:start-val="0"
|
||||||
|
class="text-right text-20px font-700"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 图表区域 -->
|
||||||
|
<el-row :gutter="20" justify="space-between">
|
||||||
|
<!-- 门店销售统计 -->
|
||||||
|
<el-col :lg="12" :md="24" :sm="24" :xl="12" :xs="24">
|
||||||
|
<el-card class="mb-20px" shadow="hover">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">门店销售统计</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-skeleton :loading="loading" animated>
|
||||||
|
<Echart :height="300" :options="storeSalesOptions" />
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<!-- 业务员销售排行 -->
|
||||||
|
<el-col :lg="12" :md="24" :sm="24" :xl="12" :xs="24">
|
||||||
|
<el-card class="mb-20px" shadow="hover">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">业务员销售排行</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-skeleton :loading="loading" animated>
|
||||||
|
<Echart :height="300" :options="salespersonRankOptions" />
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<!-- 销售趋势 -->
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-card class="mb-20px" shadow="hover">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">销售趋势分析</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-skeleton :loading="loading" animated>
|
||||||
|
<Echart :height="350" :options="salesTrendOptions" />
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { EChartsOption } from 'echarts'
|
||||||
|
import { defaultShortcuts } from '@/utils/formatTime'
|
||||||
|
import { CarStatisticsApi } from '@/api/car/statistics'
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { ContentWrap } from '@/components/ContentWrap'
|
||||||
|
import { StoreApi } from '@/api/tire/store'
|
||||||
|
|
||||||
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
|
||||||
|
defineOptions({ name: 'Home2' })
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const loading = ref(true)
|
||||||
|
const queryFormRef = ref()
|
||||||
|
const storeList = ref<any[]>([]) // 门店列表
|
||||||
|
const summaryData = ref({
|
||||||
|
totalAmount: 0,
|
||||||
|
totalOrderCount: 0,
|
||||||
|
averageOrderAmount: 0,
|
||||||
|
activeStoreCount: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const { getPrefixCls } = useDesign()
|
||||||
|
const prefixCls = getPrefixCls('panel')
|
||||||
|
|
||||||
|
const queryParams = reactive({
|
||||||
|
times: [
|
||||||
|
// 默认显示最近30天的数据
|
||||||
|
dayjs().subtract(30, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||||
|
dayjs().format('YYYY-MM-DD 23:59:59')
|
||||||
|
],
|
||||||
|
storeId: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
// 门店销售统计图表
|
||||||
|
const storeSalesOptions = reactive<EChartsOption>({
|
||||||
|
dataset: {
|
||||||
|
dimensions: ['storeName', 'totalAmount'],
|
||||||
|
source: []
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
bottom: 40,
|
||||||
|
top: 20,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const data = params[0]
|
||||||
|
return `${data.name}<br/>销售金额: ${erpPriceTableColumnFormatter(null, null, data.value.totalAmount, null)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45,
|
||||||
|
interval: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '销售金额'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售金额',
|
||||||
|
type: 'bar',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#409EFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
saveAsImage: { show: true, name: '门店销售统计' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as EChartsOption
|
||||||
|
|
||||||
|
// 业务员销售排行图表
|
||||||
|
const salespersonRankOptions = reactive<EChartsOption>({
|
||||||
|
dataset: {
|
||||||
|
dimensions: ['salespersonName', 'totalAmount'],
|
||||||
|
source: []
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
bottom: 20,
|
||||||
|
top: 20,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const data = params[0]
|
||||||
|
return `${data.name}<br/>销售金额: ${erpPriceTableColumnFormatter(null, null, data.value.totalAmount, null)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '销售金额'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'category',
|
||||||
|
name: '业务员',
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售金额',
|
||||||
|
type: 'bar',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#67C23A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
saveAsImage: { show: true, name: '业务员销售排行' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as EChartsOption
|
||||||
|
|
||||||
|
// 销售趋势图表
|
||||||
|
const salesTrendOptions = reactive<EChartsOption>({
|
||||||
|
dataset: {
|
||||||
|
dimensions: ['date', 'amount'],
|
||||||
|
source: []
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
bottom: 20,
|
||||||
|
top: 40,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
top: 10
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross'
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
let result = params[0].name + '<br/>'
|
||||||
|
params.forEach((item: any) => {
|
||||||
|
result += `${item.seriesName}: ${erpPriceTableColumnFormatter(null, null, item.value.amount, null)}<br/>`
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '金额',
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售金额',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#409EFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: false
|
||||||
|
},
|
||||||
|
brush: {
|
||||||
|
type: ['lineX', 'clear']
|
||||||
|
},
|
||||||
|
saveAsImage: { show: true, name: '销售趋势分析' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as EChartsOption
|
||||||
|
|
||||||
|
/** 获取门店列表 */
|
||||||
|
const getStoreList = async () => {
|
||||||
|
try {
|
||||||
|
const data = await StoreApi.getStorePage({ pageSize: -1 })
|
||||||
|
if (data && data.list && Array.isArray(data.list)) {
|
||||||
|
storeList.value = data.list.map((item: any) => ({
|
||||||
|
id: item.id,
|
||||||
|
name: item.storeName
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取门店列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取汇总数据 */
|
||||||
|
const getSummaryData = async () => {
|
||||||
|
try {
|
||||||
|
const data = await CarStatisticsApi.getSummaryStatistics(queryParams)
|
||||||
|
if (data) {
|
||||||
|
summaryData.value = {
|
||||||
|
totalAmount: data.totalAmount || 0,
|
||||||
|
totalOrderCount: data.totalOrderCount || 0,
|
||||||
|
averageOrderAmount: data.averageOrderAmount || 0,
|
||||||
|
activeStoreCount: data.activeStoreCount || 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取汇总数据失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取门店销售统计 */
|
||||||
|
const getStoreSales = async () => {
|
||||||
|
try {
|
||||||
|
const data = await CarStatisticsApi.getStoreSalesStatistics(queryParams)
|
||||||
|
if (data && Array.isArray(data)) {
|
||||||
|
if (storeSalesOptions.dataset && storeSalesOptions.dataset['source']) {
|
||||||
|
storeSalesOptions.dataset['source'] = data.slice(0, 10).map(item => ({
|
||||||
|
storeName: item.storeName,
|
||||||
|
totalAmount: item.totalAmount
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取门店销售统计失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取业务员销售排行 */
|
||||||
|
const getSalespersonRank = async () => {
|
||||||
|
try {
|
||||||
|
const data = await CarStatisticsApi.getSalespersonRank(queryParams)
|
||||||
|
if (data && Array.isArray(data)) {
|
||||||
|
if (salespersonRankOptions.dataset && salespersonRankOptions.dataset['source']) {
|
||||||
|
salespersonRankOptions.dataset['source'] = data.slice(0, 10).map(item => ({
|
||||||
|
salespersonName: item.salespersonName,
|
||||||
|
totalAmount: item.totalAmount
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取业务员销售排行失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取销售趋势 */
|
||||||
|
const getSalesTrend = async () => {
|
||||||
|
try {
|
||||||
|
// 只使用门店销售趋势,避免与业务员销售趋势重复计算
|
||||||
|
const storeTrend = await CarStatisticsApi.getStoreSalesTrend(queryParams)
|
||||||
|
|
||||||
|
const trendMap = new Map()
|
||||||
|
|
||||||
|
// 辅助函数:将金额转换为数字
|
||||||
|
const parseAmount = (amount: any): number => {
|
||||||
|
if (amount == null || amount === '') return 0
|
||||||
|
const num = Number(amount)
|
||||||
|
return isNaN(num) ? 0 : num
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按日期汇总门店销售趋势数据
|
||||||
|
if (storeTrend && Array.isArray(storeTrend)) {
|
||||||
|
storeTrend.forEach((item: any) => {
|
||||||
|
const key = item.date
|
||||||
|
if (!trendMap.has(key)) {
|
||||||
|
trendMap.set(key, { date: key, amount: 0 })
|
||||||
|
}
|
||||||
|
const currentAmount = trendMap.get(key).amount
|
||||||
|
const itemAmount = parseAmount(item.amount)
|
||||||
|
trendMap.get(key).amount = currentAmount + itemAmount
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const trendData = Array.from(trendMap.values()).sort((a, b) =>
|
||||||
|
new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (salesTrendOptions.dataset && salesTrendOptions.dataset['source']) {
|
||||||
|
salesTrendOptions.dataset['source'] = trendData
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取销售趋势失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
getAllApi()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
const resetQuery = () => {
|
||||||
|
queryFormRef.value.resetFields()
|
||||||
|
queryParams.times = [
|
||||||
|
dayjs().subtract(30, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||||
|
dayjs().format('YYYY-MM-DD 23:59:59')
|
||||||
|
]
|
||||||
|
queryParams.storeId = undefined
|
||||||
|
handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllApi = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
getSummaryData(),
|
||||||
|
getStoreSales(),
|
||||||
|
getSalespersonRank(),
|
||||||
|
getSalesTrend()
|
||||||
|
])
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getStoreList()
|
||||||
|
getAllApi()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$prefix-cls: #{$namespace}-panel;
|
||||||
|
|
||||||
|
.#{$prefix-cls} {
|
||||||
|
&__item {
|
||||||
|
&--peoples {
|
||||||
|
color: #40c9c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--message {
|
||||||
|
color: #36a3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--money {
|
||||||
|
color: #f4516c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--shopping {
|
||||||
|
color: #34bfa3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
:deep(.#{$namespace}-icon) {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$prefix-cls}__item--icon {
|
||||||
|
transition: all 0.38s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$prefix-cls}__item--peoples {
|
||||||
|
background: #40c9c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$prefix-cls}__item--message {
|
||||||
|
background: #36a3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$prefix-cls}__item--money {
|
||||||
|
background: #f4516c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$prefix-cls}__item--shopping {
|
||||||
|
background: #34bfa3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
438
src/views/car/renewalorder/RenewalOrderForm.vue
Normal file
438
src/views/car/renewalorder/RenewalOrderForm.vue
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1200px">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="120px"
|
||||||
|
v-loading="formLoading"
|
||||||
|
>
|
||||||
|
<el-divider content-position="left">车辆信息</el-divider>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="汽车品牌" prop="carBrand">
|
||||||
|
<el-input v-model="formData.carBrand" placeholder="请输入汽车品牌" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="车型" prop="carModel">
|
||||||
|
<el-input v-model="formData.carModel" placeholder="请输入车型" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="车牌号" prop="licensePlate">
|
||||||
|
<el-input v-model="formData.licensePlate" placeholder="请输入车牌号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="厂牌型号" prop="factoryModel">
|
||||||
|
<el-input v-model="formData.factoryModel" placeholder="请输入厂牌型号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="车架号" prop="vin">
|
||||||
|
<el-input v-model="formData.vin" placeholder="请输入车架号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="发动机号" prop="engineNo">
|
||||||
|
<el-input v-model="formData.engineNo" placeholder="请输入发动机号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="发票日期" prop="invoiceDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.invoiceDate"
|
||||||
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
placeholder="选择发票日期"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="发票金额" prop="invoiceAmount">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.invoiceAmount"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:step="0.01"
|
||||||
|
controls-position="right"
|
||||||
|
placeholder="请输入发票金额"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="购买时公里数" prop="purchaseMileage">
|
||||||
|
<el-input v-model="formData.purchaseMileage" placeholder="请输入购买时公里数" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="发票图片" prop="invoiceUrl">
|
||||||
|
<UploadImg v-model="formData.invoiceUrl" :file-size="10" :file-type="['image/jpeg', 'image/png', 'image/jpg', 'image/gif']" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">购买方信息</el-divider>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="服务购买方" prop="serviceBuyer">
|
||||||
|
<el-input v-model="formData.serviceBuyer" placeholder="请输入服务购买方" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="车辆购买方" prop="carBuyer">
|
||||||
|
<el-input v-model="formData.carBuyer" placeholder="请输入车辆购买方" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="证件类型" prop="certType">
|
||||||
|
<el-select v-model="formData.certType" placeholder="请选择证件类型" style="width: 100%">
|
||||||
|
<el-option label="请选择字典生成" value="" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="证件号码" prop="certNo">
|
||||||
|
<el-input v-model="formData.certNo" placeholder="请输入证件号码" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="联系电话" prop="mobile">
|
||||||
|
<el-input v-model="formData.mobile" placeholder="请输入联系电话" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="会员邮箱" prop="memberEmail">
|
||||||
|
<el-input v-model="formData.memberEmail" placeholder="请输入会员邮箱" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="联系地址" prop="contactAddress">
|
||||||
|
<el-input v-model="formData.contactAddress" placeholder="请输入联系地址" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="门店" prop="storeId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.storeId"
|
||||||
|
placeholder="请选择门店"
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="store in storeList"
|
||||||
|
:key="store.id"
|
||||||
|
:label="store.storeName"
|
||||||
|
:value="store.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">产品信息</el-divider>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="续保产品" prop="productId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.productId"
|
||||||
|
placeholder="请选择续保产品"
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleProductChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="product in productList"
|
||||||
|
:key="product.id"
|
||||||
|
:label="product.productName"
|
||||||
|
:value="product.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="服务产品" prop="serviceProduct">
|
||||||
|
<el-input v-model="formData.serviceProduct" placeholder="选择产品后自动填充" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="产品时效" prop="productValidity">
|
||||||
|
<el-input v-model="formData.productValidity" placeholder="选择产品后自动填充" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="产品费用" prop="productFee">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.productFee"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:step="0.01"
|
||||||
|
controls-position="right"
|
||||||
|
placeholder="请输入产品费用"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="原厂质保时长" prop="originalWarrantyYears">
|
||||||
|
<el-input v-model="formData.originalWarrantyYears" placeholder="请输入原厂质保时长" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="原厂质保里程" prop="originalWarrantyMileage">
|
||||||
|
<el-input v-model="formData.originalWarrantyMileage" placeholder="请输入原厂质保里程" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="结算方式" prop="settlementMethod">
|
||||||
|
<el-input v-model="formData.settlementMethod" placeholder="请输入结算方式" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="录单人" prop="inputUser">
|
||||||
|
<el-input v-model="formData.inputUser" placeholder="请输入录单人" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">其他信息</el-divider>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="合同备注" prop="contractRemark">
|
||||||
|
<el-input v-model="formData.contractRemark" type="textarea" :rows="3" placeholder="请输入合同备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { RenewalOrderApi, RenewalOrderVO } from '@/api/car/renewalorder'
|
||||||
|
import { RenewalProductApi, RenewalProductVO } from '@/api/car/renewalproduct'
|
||||||
|
import { StoreApi, StoreVO } from '@/api/tire/store'
|
||||||
|
import UploadImg from '@/components/UploadFile/src/UploadImg.vue'
|
||||||
|
|
||||||
|
/** 车辆续保订单 表单 */
|
||||||
|
defineOptions({ name: 'RenewalOrderForm' })
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
|
const productList = ref<RenewalProductVO[]>([]) // 续保产品列表
|
||||||
|
const storeList = ref<StoreVO[]>([]) // 门店列表
|
||||||
|
const formData = ref({
|
||||||
|
id: undefined,
|
||||||
|
carBrand: undefined,
|
||||||
|
carModel: undefined,
|
||||||
|
licensePlate: undefined,
|
||||||
|
factoryModel: undefined,
|
||||||
|
invoiceAmount: undefined,
|
||||||
|
purchaseMileage: undefined,
|
||||||
|
engineNo: undefined,
|
||||||
|
vin: undefined,
|
||||||
|
invoiceDate: undefined,
|
||||||
|
invoiceUrl: undefined,
|
||||||
|
serviceBuyer: undefined,
|
||||||
|
carBuyer: undefined,
|
||||||
|
certType: undefined,
|
||||||
|
mobile: undefined,
|
||||||
|
certNo: undefined,
|
||||||
|
contactAddress: undefined,
|
||||||
|
memberEmail: undefined,
|
||||||
|
storeId: undefined,
|
||||||
|
productId: undefined,
|
||||||
|
serviceProduct: undefined,
|
||||||
|
productValidity: undefined,
|
||||||
|
originalWarrantyYears: undefined,
|
||||||
|
originalWarrantyMileage: undefined,
|
||||||
|
productFee: undefined,
|
||||||
|
settlementMethod: undefined,
|
||||||
|
remark: undefined,
|
||||||
|
inputUser: undefined,
|
||||||
|
contractRemark: undefined
|
||||||
|
})
|
||||||
|
const formRules = reactive({
|
||||||
|
licensePlate: [{ required: true, message: '车牌号不能为空', trigger: 'blur' }],
|
||||||
|
productId: [{ required: true, message: '续保产品不能为空', trigger: 'change' }]
|
||||||
|
})
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
/** 获取门店列表 */
|
||||||
|
const getStoreList = async () => {
|
||||||
|
try {
|
||||||
|
const data = await StoreApi.getStorePage({ pageNo: 1, pageSize: -1 })
|
||||||
|
storeList.value = data.list || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取门店列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取续保产品列表 */
|
||||||
|
const getProductList = async () => {
|
||||||
|
try {
|
||||||
|
const data = await RenewalProductApi.getRenewalProductPage({ pageNo: 1, pageSize: -1 })
|
||||||
|
productList.value = data.list || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取续保产品列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理产品选择变化 */
|
||||||
|
const handleProductChange = async (productId: number) => {
|
||||||
|
if (!productId) {
|
||||||
|
formData.value.serviceProduct = undefined
|
||||||
|
formData.value.productValidity = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const product = await RenewalProductApi.getRenewalProduct(productId)
|
||||||
|
// 回显服务产品和产品时效(产品时效 = 产品内容)
|
||||||
|
formData.value.serviceProduct = product.productName || ''
|
||||||
|
formData.value.productValidity = product.productContent || ''
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取产品详情失败:', error)
|
||||||
|
message.error('获取产品详情失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = async (type: string, id?: number) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
dialogTitle.value = t('action.' + type)
|
||||||
|
formType.value = type
|
||||||
|
resetForm()
|
||||||
|
// 加载门店列表和产品列表
|
||||||
|
await Promise.all([getStoreList(), getProductList()])
|
||||||
|
// 修改时,设置数据
|
||||||
|
if (id) {
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
const data = await RenewalOrderApi.getRenewalOrder(id)
|
||||||
|
// 处理发票日期:如果是Date对象或时间戳,转换为字符串格式 YYYY-MM-DD
|
||||||
|
if (data.invoiceDate) {
|
||||||
|
if (data.invoiceDate instanceof Date) {
|
||||||
|
const year = data.invoiceDate.getFullYear()
|
||||||
|
const month = String(data.invoiceDate.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(data.invoiceDate.getDate()).padStart(2, '0')
|
||||||
|
data.invoiceDate = `${year}-${month}-${day}`
|
||||||
|
} else if (typeof data.invoiceDate === 'number' || typeof data.invoiceDate === 'string') {
|
||||||
|
// 如果是时间戳或字符串,尝试转换
|
||||||
|
const date = new Date(data.invoiceDate)
|
||||||
|
if (!isNaN(date.getTime())) {
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
data.invoiceDate = `${year}-${month}-${day}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formData.value = data
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
/** 提交表单 */
|
||||||
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
const submitForm = async () => {
|
||||||
|
// 校验表单
|
||||||
|
await formRef.value.validate()
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
const data = formData.value as unknown as RenewalOrderVO
|
||||||
|
if (formType.value === 'create') {
|
||||||
|
await RenewalOrderApi.createRenewalOrder(data)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await RenewalOrderApi.updateRenewalOrder(data)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.value = {
|
||||||
|
id: undefined,
|
||||||
|
carBrand: undefined,
|
||||||
|
carModel: undefined,
|
||||||
|
licensePlate: undefined,
|
||||||
|
factoryModel: undefined,
|
||||||
|
invoiceAmount: undefined,
|
||||||
|
purchaseMileage: undefined,
|
||||||
|
engineNo: undefined,
|
||||||
|
vin: undefined,
|
||||||
|
invoiceDate: undefined,
|
||||||
|
invoiceUrl: undefined,
|
||||||
|
serviceBuyer: undefined,
|
||||||
|
carBuyer: undefined,
|
||||||
|
certType: undefined,
|
||||||
|
mobile: undefined,
|
||||||
|
certNo: undefined,
|
||||||
|
contactAddress: undefined,
|
||||||
|
memberEmail: undefined,
|
||||||
|
storeId: undefined,
|
||||||
|
productId: undefined,
|
||||||
|
serviceProduct: undefined,
|
||||||
|
productValidity: undefined,
|
||||||
|
originalWarrantyYears: undefined,
|
||||||
|
originalWarrantyMileage: undefined,
|
||||||
|
productFee: undefined,
|
||||||
|
settlementMethod: undefined,
|
||||||
|
remark: undefined,
|
||||||
|
inputUser: undefined,
|
||||||
|
contractRemark: undefined
|
||||||
|
}
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
160
src/views/car/renewalorder/components/SalesTrendChart.vue
Normal file
160
src/views/car/renewalorder/components/SalesTrendChart.vue
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">销售趋势分析</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-skeleton :loading="loading" animated>
|
||||||
|
<Echart :height="400" :options="echartsOption" />
|
||||||
|
</el-skeleton>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CarStatisticsApi } from '@/api/car/statistics'
|
||||||
|
import { EChartsOption } from 'echarts'
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import Echart from '@/components/Echart/src/Echart.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'SalesTrendChart' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
queryParams: propTypes.object.def({})
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
/** 折线图配置 */
|
||||||
|
const echartsOption = reactive<EChartsOption>({
|
||||||
|
dataset: {
|
||||||
|
dimensions: ['date', 'amount'],
|
||||||
|
source: []
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
bottom: 20,
|
||||||
|
top: 40,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
top: 10
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross'
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
let result = params[0].name + '<br/>'
|
||||||
|
params.forEach((item: any) => {
|
||||||
|
result += `${item.seriesName}: ${erpPriceTableColumnFormatter(null, null, item.value, null)}<br/>`
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '金额',
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售金额',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#409EFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: false
|
||||||
|
},
|
||||||
|
brush: {
|
||||||
|
type: ['lineX', 'clear']
|
||||||
|
},
|
||||||
|
saveAsImage: { show: true, name: '销售趋势分析' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as EChartsOption
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 获取门店销售趋势和业务员销售趋势
|
||||||
|
const [storeTrend, salespersonTrend] = await Promise.all([
|
||||||
|
CarStatisticsApi.getStoreSalesTrend(props.queryParams),
|
||||||
|
CarStatisticsApi.getSalespersonSalesTrend(props.queryParams)
|
||||||
|
])
|
||||||
|
|
||||||
|
// 合并数据,按日期汇总
|
||||||
|
const trendMap = new Map()
|
||||||
|
|
||||||
|
// 处理门店趋势数据
|
||||||
|
if (storeTrend && Array.isArray(storeTrend)) {
|
||||||
|
storeTrend.forEach((item: any) => {
|
||||||
|
const key = item.date
|
||||||
|
if (!trendMap.has(key)) {
|
||||||
|
trendMap.set(key, { date: key, amount: 0 })
|
||||||
|
}
|
||||||
|
trendMap.get(key).amount += item.amount || 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理业务员趋势数据
|
||||||
|
if (salespersonTrend && Array.isArray(salespersonTrend)) {
|
||||||
|
salespersonTrend.forEach((item: any) => {
|
||||||
|
const key = item.date
|
||||||
|
if (!trendMap.has(key)) {
|
||||||
|
trendMap.set(key, { date: key, amount: 0 })
|
||||||
|
}
|
||||||
|
trendMap.get(key).amount += item.amount || 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为数组并排序
|
||||||
|
const trendData = Array.from(trendMap.values()).sort((a, b) =>
|
||||||
|
new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||||
|
)
|
||||||
|
|
||||||
|
// 更新图表数据
|
||||||
|
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||||
|
echartsOption.dataset['source'] = trendData
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取销售趋势失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.queryParams,
|
||||||
|
() => {
|
||||||
|
loadData()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ loadData })
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">业务员开单率分析</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-table v-loading="loading" :data="list" max-height="500">
|
||||||
|
<el-table-column label="业务员" align="center" prop="salespersonName" min-width="120" />
|
||||||
|
<el-table-column label="门店" align="center" prop="storeName" min-width="120" />
|
||||||
|
<el-table-column label="订单数" align="center" prop="orderCount" min-width="100" />
|
||||||
|
<el-table-column label="平均开单天数" align="center" prop="averageOrderDays" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.averageOrderDays ? scope.row.averageOrderDays + '天' : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="30天开单率" align="center" prop="orderRate30Days" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.orderRate30Days ? (scope.row.orderRate30Days * 100).toFixed(2) + '%' : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="平均订单金额" align="center" prop="averageOrderAmount" :formatter="priceFormatter" min-width="120" />
|
||||||
|
<el-table-column label="距上次开单" align="center" prop="lastOrderDays" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getLastOrderDaysType(scope.row.lastOrderDays)">
|
||||||
|
{{ scope.row.lastOrderDays !== null ? scope.row.lastOrderDays + '天' : '-' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CarStatisticsApi, SalespersonStatisticsVO } from '@/api/car/statistics'
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
defineOptions({ name: 'SalespersonOrderRateAnalysis' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
queryParams: propTypes.object.def({})
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const list = ref<SalespersonStatisticsVO[]>([])
|
||||||
|
|
||||||
|
/** 金额格式化 */
|
||||||
|
const priceFormatter = erpPriceTableColumnFormatter
|
||||||
|
|
||||||
|
/** 根据距上次开单天数返回标签类型 */
|
||||||
|
const getLastOrderDaysType = (days: number | null) => {
|
||||||
|
if (days === null) return 'info'
|
||||||
|
if (days <= 7) return 'success'
|
||||||
|
if (days <= 30) return 'warning'
|
||||||
|
return 'danger'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await CarStatisticsApi.getSalespersonOrderRate(props.queryParams)
|
||||||
|
list.value = data || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取业务员开单率分析失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.queryParams,
|
||||||
|
() => {
|
||||||
|
loadData()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ loadData })
|
||||||
|
</script>
|
||||||
125
src/views/car/renewalorder/components/SalespersonRank.vue
Normal file
125
src/views/car/renewalorder/components/SalespersonRank.vue
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">业务员销售排行</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-skeleton :loading="loading" animated>
|
||||||
|
<Echart :height="400" :options="echartsOption" />
|
||||||
|
</el-skeleton>
|
||||||
|
<!-- 排行列表 -->
|
||||||
|
<el-table v-loading="loading" :data="list" class="mt-4" max-height="300">
|
||||||
|
<el-table-column label="排名" align="center" type="index" width="60" />
|
||||||
|
<el-table-column label="业务员" align="center" prop="salespersonName" min-width="120" />
|
||||||
|
<el-table-column label="门店" align="center" prop="storeName" min-width="120" />
|
||||||
|
<el-table-column label="销售金额" align="center" prop="totalAmount" :formatter="priceFormatter" min-width="120" />
|
||||||
|
<el-table-column label="订单数" align="center" prop="orderCount" min-width="100" />
|
||||||
|
<el-table-column label="平均订单金额" align="center" prop="averageOrderAmount" :formatter="priceFormatter" min-width="120" />
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CarStatisticsApi, SalespersonStatisticsVO } from '@/api/car/statistics'
|
||||||
|
import { EChartsOption } from 'echarts'
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import Echart from '@/components/Echart/src/Echart.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'SalespersonRank' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
queryParams: propTypes.object.def({})
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const list = ref<SalespersonStatisticsVO[]>([])
|
||||||
|
|
||||||
|
/** 金额格式化 */
|
||||||
|
const priceFormatter = erpPriceTableColumnFormatter
|
||||||
|
|
||||||
|
/** 横向柱状图配置 */
|
||||||
|
const echartsOption = reactive<EChartsOption>({
|
||||||
|
dataset: {
|
||||||
|
dimensions: ['salespersonName', 'totalAmount'],
|
||||||
|
source: []
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
bottom: 20,
|
||||||
|
top: 20,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const data = params[0]
|
||||||
|
return `${data.name}<br/>销售金额: ${erpPriceTableColumnFormatter(null, null, data.value, null)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '销售金额'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'category',
|
||||||
|
name: '业务员',
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售金额',
|
||||||
|
type: 'bar',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#67C23A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
saveAsImage: { show: true, name: '业务员销售排行' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as EChartsOption
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await CarStatisticsApi.getSalespersonRank(props.queryParams)
|
||||||
|
list.value = (data || []).slice(0, 10) // 取前10名
|
||||||
|
// 更新图表数据
|
||||||
|
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||||
|
echartsOption.dataset['source'] = list.value.map(item => ({
|
||||||
|
salespersonName: item.salespersonName,
|
||||||
|
totalAmount: item.totalAmount
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取业务员销售排行失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.queryParams,
|
||||||
|
() => {
|
||||||
|
loadData()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ loadData })
|
||||||
|
</script>
|
||||||
84
src/views/car/renewalorder/components/StoreAnalysis.vue
Normal file
84
src/views/car/renewalorder/components/StoreAnalysis.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">门店分析数据</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-table v-loading="loading" :data="list" max-height="500">
|
||||||
|
<el-table-column label="门店名称" align="center" prop="storeName" min-width="150" />
|
||||||
|
<el-table-column label="订单数" align="center" prop="orderCount" min-width="100" />
|
||||||
|
<el-table-column label="平均开单天数" align="center" prop="averageOrderDays" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.averageOrderDays ? scope.row.averageOrderDays + '天' : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="30天开单率" align="center" prop="orderRate30Days" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.orderRate30Days ? (scope.row.orderRate30Days * 100).toFixed(2) + '%' : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="平均订单金额" align="center" prop="averageOrderAmount" :formatter="priceFormatter" min-width="120" />
|
||||||
|
<el-table-column label="距上次开单" align="center" prop="lastOrderDays" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getLastOrderDaysType(scope.row.lastOrderDays)">
|
||||||
|
{{ scope.row.lastOrderDays !== null ? scope.row.lastOrderDays + '天' : '-' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CarStatisticsApi, StoreSalesStatisticsVO } from '@/api/car/statistics'
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
defineOptions({ name: 'StoreAnalysis' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
queryParams: propTypes.object.def({})
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const list = ref<StoreSalesStatisticsVO[]>([])
|
||||||
|
|
||||||
|
/** 金额格式化 */
|
||||||
|
const priceFormatter = erpPriceTableColumnFormatter
|
||||||
|
|
||||||
|
/** 根据距上次开单天数返回标签类型 */
|
||||||
|
const getLastOrderDaysType = (days: number | null) => {
|
||||||
|
if (days === null) return 'info'
|
||||||
|
if (days <= 7) return 'success'
|
||||||
|
if (days <= 30) return 'warning'
|
||||||
|
return 'danger'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await CarStatisticsApi.getStoreOrderRate(props.queryParams)
|
||||||
|
list.value = data || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取门店分析数据失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.queryParams,
|
||||||
|
() => {
|
||||||
|
loadData()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ loadData })
|
||||||
|
</script>
|
||||||
138
src/views/car/renewalorder/components/StoreSalesStatistics.vue
Normal file
138
src/views/car/renewalorder/components/StoreSalesStatistics.vue
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="font-bold">门店销售统计</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-skeleton :loading="loading" animated>
|
||||||
|
<Echart :height="400" :options="echartsOption" />
|
||||||
|
</el-skeleton>
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-table v-loading="loading" :data="list" class="mt-4" max-height="300">
|
||||||
|
<el-table-column label="门店名称" align="center" prop="storeName" min-width="150" />
|
||||||
|
<el-table-column label="销售金额" align="center" prop="totalAmount" :formatter="priceFormatter" min-width="120" />
|
||||||
|
<el-table-column label="订单数" align="center" prop="orderCount" min-width="100" />
|
||||||
|
<el-table-column label="平均订单金额" align="center" prop="averageOrderAmount" :formatter="priceFormatter" min-width="120" />
|
||||||
|
<el-table-column label="平均开单天数" align="center" prop="averageOrderDays" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.averageOrderDays ? scope.row.averageOrderDays + '天' : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="30天开单率" align="center" prop="orderRate30Days" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.orderRate30Days ? (scope.row.orderRate30Days * 100).toFixed(2) + '%' : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="距上次开单" align="center" prop="lastOrderDays" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.lastOrderDays !== null ? scope.row.lastOrderDays + '天' : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CarStatisticsApi, StoreSalesStatisticsVO } from '@/api/car/statistics'
|
||||||
|
import { EChartsOption } from 'echarts'
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import Echart from '@/components/Echart/src/Echart.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'StoreSalesStatistics' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
queryParams: propTypes.object.def({})
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const list = ref<StoreSalesStatisticsVO[]>([])
|
||||||
|
|
||||||
|
/** 金额格式化 */
|
||||||
|
const priceFormatter = erpPriceTableColumnFormatter
|
||||||
|
|
||||||
|
/** 柱状图配置 */
|
||||||
|
const echartsOption = reactive<EChartsOption>({
|
||||||
|
dataset: {
|
||||||
|
dimensions: ['storeName', 'totalAmount'],
|
||||||
|
source: []
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
bottom: 40,
|
||||||
|
top: 20,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const data = params[0]
|
||||||
|
return `${data.name}<br/>销售金额: ${erpPriceTableColumnFormatter(null, null, data.value, null)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45,
|
||||||
|
interval: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '销售金额'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '销售金额',
|
||||||
|
type: 'bar',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#409EFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
saveAsImage: { show: true, name: '门店销售统计' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as EChartsOption
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await CarStatisticsApi.getStoreSalesStatistics(props.queryParams)
|
||||||
|
list.value = data || []
|
||||||
|
// 更新图表数据
|
||||||
|
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||||
|
echartsOption.dataset['source'] = list.value.map(item => ({
|
||||||
|
storeName: item.storeName,
|
||||||
|
totalAmount: item.totalAmount
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取门店销售统计失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.queryParams,
|
||||||
|
() => {
|
||||||
|
loadData()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ loadData })
|
||||||
|
</script>
|
||||||
41
src/views/car/renewalorder/components/SummaryCard.vue
Normal file
41
src/views/car/renewalorder/components/SummaryCard.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="never" v-loading="loading">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div class="text-gray-500 text-sm mb-2">{{ title }}</div>
|
||||||
|
<div class="text-2xl font-bold text-gray-800">
|
||||||
|
{{ formatValue(value) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-4xl text-gray-300">
|
||||||
|
<Icon :icon="icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
defineOptions({ name: 'SummaryCard' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: propTypes.string.def(''),
|
||||||
|
value: propTypes.oneOfType([Number, String]).def(0),
|
||||||
|
loading: propTypes.bool.def(false),
|
||||||
|
icon: propTypes.string.def('ep:money'),
|
||||||
|
isAmount: propTypes.bool.def(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
const formatValue = (val: any) => {
|
||||||
|
if (val === null || val === undefined) return '0'
|
||||||
|
if (props.isAmount) {
|
||||||
|
return erpPriceTableColumnFormatter(null, null, val, null)
|
||||||
|
}
|
||||||
|
if (typeof val === 'number') {
|
||||||
|
return val.toLocaleString()
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
</script>
|
||||||
167
src/views/car/renewalorder/dashboard.vue
Normal file
167
src/views/car/renewalorder/dashboard.vue
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
class="-mb-15px"
|
||||||
|
:model="queryParams"
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item label="时间范围" prop="times">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.times"
|
||||||
|
:shortcuts="defaultShortcuts"
|
||||||
|
class="!w-240px"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="门店" prop="storeId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.storeId"
|
||||||
|
placeholder="请选择门店"
|
||||||
|
clearable
|
||||||
|
class="!w-240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="store in storeList"
|
||||||
|
:key="store.id"
|
||||||
|
:label="store.name"
|
||||||
|
:value="store.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @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>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 看板内容 -->
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<!-- 统计卡片 -->
|
||||||
|
<el-row :gutter="16" class="row">
|
||||||
|
<el-col :md="6" :sm="12" :xs="24">
|
||||||
|
<SummaryCard title="总订单金额" :value="summaryData?.totalAmount" :loading="loading" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :md="6" :sm="12" :xs="24">
|
||||||
|
<SummaryCard title="总订单数" :value="summaryData?.totalOrderCount" :loading="loading" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :md="6" :sm="12" :xs="24">
|
||||||
|
<SummaryCard title="平均订单金额" :value="summaryData?.averageOrderAmount" :loading="loading" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :md="6" :sm="12" :xs="24">
|
||||||
|
<SummaryCard title="活跃门店数" :value="summaryData?.activeStoreCount" :loading="loading" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 图表区域 -->
|
||||||
|
<el-row :gutter="16" class="row">
|
||||||
|
<!-- 门店销售统计 -->
|
||||||
|
<el-col :md="12" :sm="24" :xs="24">
|
||||||
|
<StoreSalesStatistics :query-params="queryParams" />
|
||||||
|
</el-col>
|
||||||
|
<!-- 业务员销售排行 -->
|
||||||
|
<el-col :md="12" :sm="24" :xs="24">
|
||||||
|
<SalespersonRank :query-params="queryParams" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 分析数据 -->
|
||||||
|
<el-row :gutter="16" class="row">
|
||||||
|
<!-- 业务员开单率分析 -->
|
||||||
|
<el-col :md="12" :sm="24" :xs="24">
|
||||||
|
<SalespersonOrderRateAnalysis :query-params="queryParams" />
|
||||||
|
</el-col>
|
||||||
|
<!-- 门店分析数据 -->
|
||||||
|
<el-col :md="12" :sm="24" :xs="24">
|
||||||
|
<StoreAnalysis :query-params="queryParams" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 销售趋势 -->
|
||||||
|
<el-row :gutter="16" class="row">
|
||||||
|
<el-col :span="24">
|
||||||
|
<SalesTrendChart :query-params="queryParams" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defaultShortcuts } from '@/utils/formatTime'
|
||||||
|
import { CarStatisticsApi } from '@/api/car/statistics'
|
||||||
|
import SummaryCard from './components/SummaryCard.vue'
|
||||||
|
import StoreSalesStatistics from './components/StoreSalesStatistics.vue'
|
||||||
|
import SalespersonRank from './components/SalespersonRank.vue'
|
||||||
|
import SalespersonOrderRateAnalysis from './components/SalespersonOrderRateAnalysis.vue'
|
||||||
|
import StoreAnalysis from './components/StoreAnalysis.vue'
|
||||||
|
import SalesTrendChart from './components/SalesTrendChart.vue'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
/** 车辆续保订单看板 */
|
||||||
|
defineOptions({ name: 'RenewalOrderDashboard' })
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
|
const queryFormRef = ref()
|
||||||
|
const storeList = ref<any[]>([]) // 门店列表
|
||||||
|
const summaryData = ref<any>({})
|
||||||
|
|
||||||
|
const queryParams = reactive({
|
||||||
|
times: [
|
||||||
|
// 默认显示最近30天的数据
|
||||||
|
dayjs().subtract(30, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||||
|
dayjs().format('YYYY-MM-DD 23:59:59')
|
||||||
|
],
|
||||||
|
storeId: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 获取汇总数据 */
|
||||||
|
const getSummaryData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
// 这里可以调用汇总接口,暂时使用模拟数据
|
||||||
|
summaryData.value = {
|
||||||
|
totalAmount: 0,
|
||||||
|
totalOrderCount: 0,
|
||||||
|
averageOrderAmount: 0,
|
||||||
|
activeStoreCount: 0
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
getSummaryData()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
const resetQuery = () => {
|
||||||
|
queryFormRef.value.resetFields()
|
||||||
|
queryParams.times = [
|
||||||
|
dayjs().subtract(30, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||||
|
dayjs().format('YYYY-MM-DD 23:59:59')
|
||||||
|
]
|
||||||
|
handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(() => {
|
||||||
|
getSummaryData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.row {
|
||||||
|
.el-col {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
313
src/views/car/renewalorder/index.vue
Normal file
313
src/views/car/renewalorder/index.vue
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
class="-mb-15px"
|
||||||
|
:model="queryParams"
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item label="车牌号" prop="licensePlate">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.licensePlate"
|
||||||
|
placeholder="请输入车牌号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
class="!w-240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="服务购买方" prop="serviceBuyer">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.serviceBuyer"
|
||||||
|
placeholder="请输入服务购买方"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
class="!w-240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系电话" prop="mobile">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.mobile"
|
||||||
|
placeholder="请输入联系电话"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
class="!w-240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="车架号" prop="vin">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.vin"
|
||||||
|
placeholder="请输入车架号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
class="!w-240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="门店" prop="storeId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.storeId"
|
||||||
|
placeholder="请选择门店"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
class="!w-240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="store in storeList"
|
||||||
|
:key="store.id"
|
||||||
|
:label="store.storeName"
|
||||||
|
:value="store.id"
|
||||||
|
/>
|
||||||
|
</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 @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-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="openForm('create')"
|
||||||
|
v-hasPermi="['car:renewal-order:create']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
plain
|
||||||
|
@click="handleExport"
|
||||||
|
:loading="exportLoading"
|
||||||
|
v-hasPermi="['car:renewal-order:export']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap>
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column type="index" label="序号" align="center" width="60" />
|
||||||
|
<el-table-column label="车牌号" align="center" prop="licensePlate" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<span
|
||||||
|
v-if="scope.row.licensePlate"
|
||||||
|
class="license-plate-link"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
>
|
||||||
|
{{ scope.row.licensePlate }}
|
||||||
|
</span>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="汽车品牌" align="center" prop="carBrand" />
|
||||||
|
<el-table-column label="车型" align="center" prop="carModel" />
|
||||||
|
<el-table-column label="厂牌型号" align="center" prop="factoryModel" />
|
||||||
|
<el-table-column label="发票金额" align="center" prop="invoiceAmount" :formatter="priceFormatter" />
|
||||||
|
<el-table-column label="购买时公里数" align="center" prop="purchaseMileage" />
|
||||||
|
<el-table-column label="发动机号" align="center" prop="engineNo" />
|
||||||
|
<el-table-column label="车架号" align="center" prop="vin" />
|
||||||
|
<el-table-column label="发票日期" align="center" prop="invoiceDate" :formatter="dateFormatter2" />
|
||||||
|
<el-table-column label="发票图片" align="center" prop="invoiceUrl" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-image
|
||||||
|
v-if="scope.row.invoiceUrl"
|
||||||
|
:src="scope.row.invoiceUrl"
|
||||||
|
:preview-src-list="[scope.row.invoiceUrl]"
|
||||||
|
preview-teleported
|
||||||
|
fit="cover"
|
||||||
|
class="h-60px w-60px cursor-pointer"
|
||||||
|
lazy
|
||||||
|
/>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="服务购买方" align="center" prop="serviceBuyer" />
|
||||||
|
<el-table-column label="车辆购买方" align="center" prop="carBuyer" />
|
||||||
|
<el-table-column label="证件类型" align="center" prop="certType" />
|
||||||
|
<el-table-column label="联系电话" align="center" prop="mobile" />
|
||||||
|
<el-table-column label="证件号码" align="center" prop="certNo" />
|
||||||
|
<el-table-column label="联系地址" align="center" prop="contactAddress" />
|
||||||
|
<el-table-column label="会员邮箱" align="center" prop="memberEmail" />
|
||||||
|
<el-table-column label="门店" align="center" prop="storeName" />
|
||||||
|
<el-table-column label="服务产品" align="center" prop="serviceProduct" />
|
||||||
|
<el-table-column label="产品时效" align="center" prop="productValidity" />
|
||||||
|
<el-table-column label="原厂质保时长" align="center" prop="originalWarrantyYears" />
|
||||||
|
<el-table-column label="原厂质保里程" align="center" prop="originalWarrantyMileage" />
|
||||||
|
<el-table-column label="产品费用" align="center" prop="productFee" :formatter="priceFormatter" />
|
||||||
|
<el-table-column label="结算方式" align="center" prop="settlementMethod" />
|
||||||
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
|
<el-table-column label="录单人" align="center" prop="inputUser" />
|
||||||
|
<el-table-column label="合同备注" align="center" prop="contractRemark" />
|
||||||
|
<el-table-column
|
||||||
|
label="创建时间"
|
||||||
|
align="center"
|
||||||
|
prop="createTime"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column label="操作" align="center" min-width="120px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['car:renewal-order:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row.id)"
|
||||||
|
v-hasPermi="['car:renewal-order:delete']"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
|
<RenewalOrderForm ref="formRef" @success="getList" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||||
|
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||||
|
import download from '@/utils/download'
|
||||||
|
import { RenewalOrderApi, RenewalOrderVO } from '@/api/car/renewalorder'
|
||||||
|
import { StoreApi, StoreVO } from '@/api/tire/store'
|
||||||
|
import RenewalOrderForm from './RenewalOrderForm.vue'
|
||||||
|
|
||||||
|
/** 车辆续保订单 列表 */
|
||||||
|
defineOptions({ name: 'RenewalOrder' })
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const list = ref<RenewalOrderVO[]>([]) // 列表的数据
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const storeList = ref<StoreVO[]>([]) // 门店列表
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
licensePlate: undefined,
|
||||||
|
serviceBuyer: undefined,
|
||||||
|
mobile: undefined,
|
||||||
|
vin: undefined,
|
||||||
|
storeId: undefined,
|
||||||
|
createTime: []
|
||||||
|
})
|
||||||
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
const exportLoading = ref(false) // 导出的加载中
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await RenewalOrderApi.getRenewalOrderPage(queryParams)
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
const resetQuery = () => {
|
||||||
|
queryFormRef.value.resetFields()
|
||||||
|
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 RenewalOrderApi.deleteRenewalOrder(id)
|
||||||
|
message.success(t('common.delSuccess'))
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出按钮操作 */
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
// 导出的二次确认
|
||||||
|
await message.exportConfirm()
|
||||||
|
// 发起导出
|
||||||
|
exportLoading.value = true
|
||||||
|
const data = await RenewalOrderApi.exportRenewalOrder(queryParams)
|
||||||
|
download.excel(data, '车辆续保订单.xls')
|
||||||
|
} catch {
|
||||||
|
} finally {
|
||||||
|
exportLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取门店列表 */
|
||||||
|
const getStoreList = async () => {
|
||||||
|
try {
|
||||||
|
const data = await StoreApi.getStorePage({ pageNo: 1, pageSize: -1 })
|
||||||
|
storeList.value = data.list || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取门店列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 金额格式化 */
|
||||||
|
const priceFormatter = erpPriceTableColumnFormatter
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(() => {
|
||||||
|
getStoreList()
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.license-plate-link {
|
||||||
|
color: #409eff;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-plate-link:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
130
src/views/car/renewalproduct/RenewalProductForm.vue
Normal file
130
src/views/car/renewalproduct/RenewalProductForm.vue
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :title="dialogTitle" v-model="dialogVisible" width="900px">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="120px"
|
||||||
|
v-loading="formLoading"
|
||||||
|
>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="产品名称" prop="productName">
|
||||||
|
<el-input v-model="formData.productName" placeholder="请输入产品名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="产品类别" prop="productType">
|
||||||
|
<el-select v-model="formData.productType" placeholder="请选择产品类别" style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getStrDictOptions(DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="产品内容" prop="productContent">
|
||||||
|
<el-input v-model="formData.productContent" type="textarea" :rows="3" placeholder="请输入产品内容" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="产品备注" prop="remark">
|
||||||
|
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { RenewalProductApi, RenewalProductVO } from '@/api/car/renewalproduct'
|
||||||
|
|
||||||
|
/** 车辆续保产品信息 表单 */
|
||||||
|
defineOptions({ name: 'RenewalProductForm' })
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
|
const formData = ref({
|
||||||
|
id: undefined,
|
||||||
|
productName: undefined,
|
||||||
|
productContent: undefined,
|
||||||
|
productType: undefined,
|
||||||
|
remark: undefined
|
||||||
|
})
|
||||||
|
const formRules = reactive({
|
||||||
|
productName: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
|
||||||
|
productType: [{ required: true, message: '产品类别不能为空', trigger: 'change' }]
|
||||||
|
})
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = async (type: string, id?: number) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
dialogTitle.value = t('action.' + type)
|
||||||
|
formType.value = type
|
||||||
|
resetForm()
|
||||||
|
// 修改时,设置数据
|
||||||
|
if (id) {
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
formData.value = await RenewalProductApi.getRenewalProduct(id)
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
/** 提交表单 */
|
||||||
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
const submitForm = async () => {
|
||||||
|
// 校验表单
|
||||||
|
await formRef.value.validate()
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
const data = formData.value as unknown as RenewalProductVO
|
||||||
|
if (formType.value === 'create') {
|
||||||
|
await RenewalProductApi.createRenewalProduct(data)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await RenewalProductApi.updateRenewalProduct(data)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.value = {
|
||||||
|
id: undefined,
|
||||||
|
productName: undefined,
|
||||||
|
productContent: undefined,
|
||||||
|
productType: undefined,
|
||||||
|
remark: undefined
|
||||||
|
}
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
211
src/views/car/renewalproduct/index.vue
Normal file
211
src/views/car/renewalproduct/index.vue
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
class="-mb-15px"
|
||||||
|
:model="queryParams"
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
label-width="68px"
|
||||||
|
>
|
||||||
|
<el-form-item label="产品名称" prop="productName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productName"
|
||||||
|
placeholder="请输入产品名称"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
class="!w-240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品类别" prop="productType">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.productType"
|
||||||
|
placeholder="请选择产品类别"
|
||||||
|
clearable
|
||||||
|
class="!w-240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getStrDictOptions(DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.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 @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-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="openForm('create')"
|
||||||
|
v-hasPermi="['car:renewal-product:create']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
plain
|
||||||
|
@click="handleExport"
|
||||||
|
:loading="exportLoading"
|
||||||
|
v-hasPermi="['car:renewal-product:export']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap>
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column type="index" label="序号" align="center" width="60" />
|
||||||
|
<el-table-column label="产品名称" align="center" prop="productName" />
|
||||||
|
<el-table-column label="产品内容" align="center" prop="productContent" />
|
||||||
|
<el-table-column label="产品类别" align="center" prop="productType">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE" :value="scope.row.productType" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
|
<el-table-column
|
||||||
|
label="创建时间"
|
||||||
|
align="center"
|
||||||
|
prop="createTime"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column label="操作" align="center" min-width="120px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['car:renewal-product:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row.id)"
|
||||||
|
v-hasPermi="['car:renewal-product:delete']"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
|
<RenewalProductForm ref="formRef" @success="getList" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
|
import download from '@/utils/download'
|
||||||
|
import { RenewalProductApi, RenewalProductVO } from '@/api/car/renewalproduct'
|
||||||
|
import RenewalProductForm from './RenewalProductForm.vue'
|
||||||
|
|
||||||
|
/** 车辆续保产品信息 列表 */
|
||||||
|
defineOptions({ name: 'RenewalProduct' })
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const list = ref<RenewalProductVO[]>([]) // 列表的数据
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
productName: undefined,
|
||||||
|
productType: undefined,
|
||||||
|
createTime: []
|
||||||
|
})
|
||||||
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
const exportLoading = ref(false) // 导出的加载中
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await RenewalProductApi.getRenewalProductPage(queryParams)
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
const resetQuery = () => {
|
||||||
|
queryFormRef.value.resetFields()
|
||||||
|
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 RenewalProductApi.deleteRenewalProduct(id)
|
||||||
|
message.success(t('common.delSuccess'))
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出按钮操作 */
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
// 导出的二次确认
|
||||||
|
await message.exportConfirm()
|
||||||
|
// 发起导出
|
||||||
|
exportLoading.value = true
|
||||||
|
const data = await RenewalProductApi.exportRenewalProduct(queryParams)
|
||||||
|
download.excel(data, '车辆续保产品信息.xls')
|
||||||
|
} catch {
|
||||||
|
} finally {
|
||||||
|
exportLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user