Merge branch 'master' into rescue

This commit is contained in:
sunhaoyuan 2025-10-31 10:17:24 +08:00
commit 659ed62893
14 changed files with 3172 additions and 10 deletions

View File

@ -0,0 +1,44 @@
<template>
<div class="common-time-select">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
@change="handleChange"
/>
</div>
</template>
<script>
export default {
name: 'CommonTimeSelect',
props: {
value: {
type: Array,
default: () => []
}
},
data() {
return {
dateRange: []
}
},
watch: {
value: {
immediate: true,
handler(newVal) {
this.dateRange = [...newVal]
}
}
},
methods: {
handleChange(value) {
this.$emit('input', value)
this.$emit('change', value)
}
}
}
</script>

598
src/utils/utils.js Normal file
View File

@ -0,0 +1,598 @@
import request from '@/utils/request';
import {
setStorageWithExpiry,
getStorageWithExpiry
} from '@/utils/auth'
export function getWXStatusHeight() {
// #ifdef MP-WEIXIN
// 获取距上
const barTop = wx.getSystemInfoSync().statusBarHeight
// 获取胶囊按钮位置信息
const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
// 获取导航栏高度
const barHeight = menuButtonInfo.height + (menuButtonInfo.top - barTop) * 2
let barWidth = menuButtonInfo.width
console.log('menuButtonInfo', menuButtonInfo)
let barLeftPosition = 375 - menuButtonInfo.right + menuButtonInfo.width
let menuButtonLeft = menuButtonInfo.left
let menuButtonRight = menuButtonInfo.right
return {
barHeight,
barTop,
barWidth,
barLeftPosition,
menuButtonLeft,
menuButtonRight
}
// #endif
}
/**
* 根据订单的状态获取订单的文字展示状态(这个状态范围少是按李总意思整理出来的状态)
* @param ticketsStatus 订单状态
* @param isHandover 是否交车
*/
export function getOrderStatusText(ticketsStatus, isHandover) {
let str = "已进厂";
if ("04" == ticketsStatus) {
//待派工
str = "待维修"
} else if ("05" == ticketsStatus) {
//维修中
str = "维修中"
} else if ("01" == ticketsStatus) {
//待取车结算
if ("1" == isHandover) {
//已交车
str = "已交车未结算"
} else {
//未交车
str = "未交车未结算"
}
} else if ("06" == ticketsStatus) {
//挂单/记账
if ("1" == isHandover) {
//已交车
str = "已交车已结算"
} else {
//未交车
str = "已结算未交车"
}
} else if ("07" == ticketsStatus) {
//待通知客户取车
str = "已竣工"
} else if ("02" == ticketsStatus) {
//已结账
if ("1" == isHandover) {
//已交车
str = "已交车已结算"
} else {
//未交车
str = "已结算未交车"
}
} else if ("03" == ticketsStatus) {
//已作废
str = "已作废"
} else if ("08" == ticketsStatus) {
//已作废
str = "已完成"
}
return str;
}
/**
* 根据订单的状态获取订单的文字展示状态(这个状态多就是系统初始状态)
* @param ticketsStatus 订单状态
* @param isHandover 是否交车
*/
export function getOrderStatusTextAll(ticketsStatus, isHandover) {
let str = "已进厂";
if ("04" == ticketsStatus) {
//待派工
str = "待派工"
} else if ("05" == ticketsStatus) {
//维修中
str = "维修中"
} else if ("01" == ticketsStatus) {
//待取车结算
if ("1" == isHandover) {
//已交车
str = "已交车未结算"
} else {
//未交车
str = "未交车未结算"
}
} else if ("06" == ticketsStatus) {
//挂单/记账
str = "已挂单/记账待交车"
if ("1" == isHandover) {
//已交车
str = "已交车已结算"
} else {
//未交车
str = "已结算未交车"
}
} else if ("07" == ticketsStatus) {
//待通知客户取车
str = "待通知客户取车"
} else if ("02" == ticketsStatus) {
//已结账
if ("1" == isHandover) {
//已交车
str = "已交车已结算"
} else {
//未交车
str = "已结算未交车"
}
} else if ("03" == ticketsStatus) {
//已作废
str = "已作废"
} else if ("08" == ticketsStatus) {
//已作废
str = "已完成"
}
return str;
}
/**
* 查询字典可选值
* @param dictCode
*/
export function getDictByCode(dictCode) {
let dictArray = getStorageWithExpiry(dictCode);
if (null == dictArray || undefined == dictArray) {
request({
url: '/admin-api/system/dict-data/type',
method: 'get',
params: {
type: dictCode
}
}).then((res) => {
console.log(res)
if (res.code == 200) {
setStorageWithExpiry(dictCode, res.data, 3600)
return res.data
}
})
} else {
return dictArray
}
}
/**
* 工单记录操作日志
* @param id 工单主表id
* @param ticketsWorkStatus 工单主表状态
* @param itemId 工单子表id
* @param itemStatus 工单子表状态
* @param recordType 操作类型 对应后端枚举RecordTypeEnum
* @param remark 备注
* @param image 图片相对路径多个英文逗号隔开
* @param finishType 完成类型 01完成并移交下一班组02完成并移交总检03完成工单
* @param nextName 下一班组名称
*/
export function saveTicketsRecords(id, ticketsWorkStatus, itemId, itemStatus, recordType, remark, image, finishType,
nextName) {
return new Promise((resolve, reject) => {
let dataObj = {
id: id,
ticketsWorkStatus: ticketsWorkStatus,
item: {
id: itemId,
itemStatus: itemStatus
},
recordType: recordType,
remark: remark,
finishType: finishType,
nextName: nextName,
image: image
}
request({
url: '/admin-api/repair/tickets/updateStatus',
method: 'POST',
data: dataObj
}).then((res) => {
console.log(res)
if (res.code == 200) {
uni.showToast({
title: '操作成功',
icon: 'none'
})
resolve(1);
} else {
uni.showToast({
title: '操作失败,请联系管理员',
icon: 'none'
})
reject(0);
}
})
});
}
/**
* 翻译字典
* @param dictCode
*/
export async function getDictTextByCodeAndValue(dictCode, value) {
let dictArray = getStorageWithExpiry(dictCode);
if (null == dictArray || undefined == dictArray) {
let res = await request({
url: '/admin-api/system/dict-data/type',
method: 'get',
params: {
type: dictCode
}
})
if (res.code == 200) {
setStorageWithExpiry(dictCode, res.data, 3600)
dictArray = res.data
let dictObj = dictArray.find(item => item.value == value)
if (dictObj) {
return dictObj.label
} else {
return "未知数据"
}
}
} else {
let dictObj = dictArray.find(item => item.value == value)
if (dictObj) {
return dictObj.label
} else {
return "未知数据"
}
}
}
export function formatTimestamp(timestamp) {
// 将时间戳转换为Date对象
const date = new Date(timestamp);
// 获取年月日时分秒
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const seconds = date.getSeconds().toString().padStart(2, '0');
// 组合成日期时间字符串
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
/**
* 将时间戳转换为指定格式的日期字符串
* @param {number} timestamp - 时间戳毫秒
* @param {string} [format='YYYY-MM-DD'] - 日期格式默认为 'YYYY-MM-DD'
* @returns {string} - 格式化的日期字符串
*/
export function formatTimestampCustom(timestamp, format = 'YYYY-MM-DD') {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hour = date.getHours().toString().padStart(2, '0');
const minute = date.getMinutes().toString().padStart(2, '0');
const second = date.getSeconds().toString().padStart(2, '0');
const replaceMap = {
'YYYY': year,
'MM': month,
'DD': day,
'HH': hour,
'mm': minute,
'ss': second
};
return format.replace(/YYYY|MM|DD|HH|mm|ss/g, match => replaceMap[match]);
}
/**
* 格式化金额
* @param {string|number} amount - 金额
* @returns {string} - 格式化后的金额
*/
export function formatCurrency(amount) {
if (amount === undefined || amount === null || amount === '') {
return '0.00';
}
// 处理可能的字符串类型金额
const num = typeof amount === 'string' ? parseFloat(amount) : amount;
// 检查是否是有效数字
if (isNaN(num)) {
return '0.00';
}
// 格式化金额,保留两位小数
return num.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
numberOfDigits: 2
});
}
/**
* 组装订单对象
* @param order
*/
export function builderOrder(order) {
return {
id: order.id,
orderNo: order.ticketNo,
flag: 1,
ticketsStatus: order.ticketsStatus,
ticketsWorkStatus: order.ticketsWorkStatus,
flagStr: getOrderStatusTextAll(order.ticketsStatus, order.isHandover),
carNum: order.carNo,
nowRepairId: order.nowRepairId,
carModel: order.carBrandName,
userName: order.userName,
userPhone: order.userMobile,
counselorName: order.adviserName,
canOperate: order.canOperate,
...order,
}
}
export function formatDate(timestamp) {
// 将时间戳转换为Date对象
const date = new Date(timestamp);
// 获取年月日时分秒
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
// 组合成日期时间字符串
return `${year}-${month}-${day}`;
}
//转换为double
// utils.js
export function convertToDouble(value, decimalPlaces = 1) {
if (value !== undefined && value !== null) {
const parsedValue = parseFloat(value);
if (!isNaN(parsedValue)) {
return parsedValue.toFixed(decimalPlaces);
} else {
console.error('转换失败,值不是有效的数字');
return '0.0'; // 可以设置一个默认值
}
} else {
console.error('值不存在');
return '0.0'; // 可以设置一个默认值
}
}
/**
* 生成一个16位的纯数字的唯一ID
* 生成策略 head + 当前时间戳 + 随机数
* @param head 前缀
*/
export function createUniqueCodeByHead(head = '') {
const min = 100; // 最小值
const max = 999; // 最大值
return head.toString() + Date.now().toString() + Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 构造树
* @param items 原对象数组
* @param idKey 主标识
* @param parentKey 父标识
* @param mapping 映射Map
*/
export function buildTree(items, idKey, parentKey, mapping = null) {
const result = []; // 存储最终的树形结构
const itemMap = {}; // 用于快速查找对象
// 首先将所有项放入map中便于后续快速查找
for (const item of items) {
itemMap[item[idKey]] = {
...item,
children: []
};
}
// 遍历每个项,构建树形结构
for (const item of items) {
const id = item[idKey];
const parentId = item[parentKey];
const node = itemMap[id];
if (parentId !== null && itemMap[parentId]) {
// 如果有父ID并且父节点存在则将当前节点加入到其父节点的children数组中
itemMap[parentId].children.push(node);
} else {
// 如果没有父ID或者找不到对应的父节点则认为这是根节点
result.push(node);
}
}
if (mapping) {
return mapTree(result, mapping)
}
return result;
}
// 映射函数
function mapTree(tree, mapping) {
if (!tree || !Array.isArray(tree)) {
return tree;
}
const mappedTree = tree.map(item => {
const mappedItem = {};
for (const key in item) {
if (key === 'children') {
// 递归处理 children 数组
if (mapping.children) {
mappedItem[mapping.children] = mapTree(item[key], mapping);
} else {
mappedItem[key] = mapTree(item[key], mapping);
}
} else if (key in mapping) {
// 根据映射规则转换属性
const targetKey = mapping[key];
mappedItem[targetKey] = item[key];
}
}
return mappedItem;
});
return mappedTree;
}
export function formatDateChinese(timestamp) {
// 将时间戳转换为Date对象
const date = new Date(timestamp);
// 获取年月日时分秒
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
// 组合成日期时间字符串
return `${year}${month}${day}`;
}
export function formatDateTimeToMinute(timestamp) {
// 将时间戳转换为 Date 对象
const date = new Date(timestamp);
// 获取年月日时分
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
// 组合成日期时间字符串格式yyyy-MM-dd hh:mm
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
export function getDateRange(type) {
const now = new Date()
let start, end
if (type === 'week') {
// 获取本周的开始(周一)和结束(周日)
const day = now.getDay() || 7 // Sunday 为 0设为 7
start = new Date(now)
start.setDate(now.getDate() - day + 1)
end = new Date(start)
end.setDate(start.getDate() + 6)
} else if (type === 'month') {
// 获取本月的开始和结束
start = new Date(now.getFullYear(), now.getMonth(), 1)
end = new Date(now.getFullYear(), now.getMonth() + 1, 0) // 本月最后一天
} else if (type === 'day') {
// 获取当天的开始和结束00:00:00 到 23:59:59
start = new Date(now)
start.setHours(0, 0, 0, 0)
end = new Date(now)
end.setHours(23, 59, 59, 999)
} else {
return []
}
return [
formatDateCus(start),
formatDateCus(end)
]
}
export function formatDateCus(date) {
const y = date.getFullYear()
const m = (date.getMonth() + 1).toString().padStart(2, '0')
const d = date.getDate().toString().padStart(2, '0')
return `${y}-${m}-${d}`
}
/**
* 将扁平数组转换为树形结构并支持按 sort 字段排序
* @param {Array} data 源数据
* @param {String} id 节点ID字段名默认为 'id'
* @param {String} parentId 父节点ID字段名默认为 'parentId'
* @param {String} children 子节点字段名默认为 'children'
* @param {Number|String} rootId 根节点ID值默认为最小的parentId或0
* @param {String} sortKey 排序字段名默认为 'sort'
* @returns {Array} 树形结构数据
*/
export function handleTree(data, id, parentId, children, rootId, sortKey) {
// 参数默认值处理
id = id || 'id';
parentId = parentId || 'parentId';
children = children || 'children';
sortKey = sortKey || 'sort';
// 自动计算根节点ID取最小的parentId若无则用0
rootId = rootId || Math.min(...data.map(item => item[parentId])) || 0;
// 深拷贝源数据以避免污染原数组
const cloneData = JSON.parse(JSON.stringify(data));
// 先对所有数据进行排序(确保父节点在前)
cloneData.sort((a, b) => {
const aSort = a[sortKey] ?? 0; // 使用空值合并运算符处理undefined
const bSort = b[sortKey] ?? 0;
return aSort - bSort; // 升序排序
});
// 构建哈希表加速查找
const nodeMap = {};
cloneData.forEach(item => {
nodeMap[item[id]] = item;
item[children] = []; // 初始化children数组
});
// 构建树形结构
const tree = [];
cloneData.forEach(item => {
if (item[parentId] === rootId) {
// 根节点直接加入结果
tree.push(item);
} else {
// 非根节点找到父节点并插入
const parent = nodeMap[item[parentId]];
parent?.[children]?.push(item);
}
});
// 递归排序所有子节点
const sortChildren = (nodes) => {
nodes.forEach(node => {
if (node[children]?.length) {
node[children].sort((a, b) => {
const aSort = a[sortKey] ?? 0;
const bSort = b[sortKey] ?? 0;
return aSort - bSort;
});
sortChildren(node[children]);
}
});
};
sortChildren(tree);
return tree.length ? tree : data; // 空树时返回原数据
}
/**
* 将树形结构数据转成 uniapp multiSelector picker 可用的二维数组
* @param {Array} tree 树形结构数据
* @param {String} labelKey 节点显示字段默认为 'name'
* @param {String} childrenKey 子节点字段默认为 'children'
* @returns {Array} picker二维数组例如 [[一级], [二级], [三级]...]
*/
export function toPickerData(tree, labelKey = 'name', childrenKey = 'children') {
const result = []
function buildColumns(nodes, level = 0) {
if (!nodes || !nodes.length) return
// 当前层级的所有 name
result[level] = nodes.map(n => n[labelKey])
// 默认取第一个子节点继续递归
if (nodes[0][childrenKey] && nodes[0][childrenKey].length) {
buildColumns(nodes[0][childrenKey], level + 1)
}
}
buildColumns(tree, 0)
return result
}

View File

@ -0,0 +1,346 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="保险公司名称" prop="company">
<el-input
v-model="queryParams.company"
placeholder="请输入保险公司名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="车牌号" prop="carNo">
<el-input
v-model="queryParams.carNo"
placeholder="请输入车牌号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="insuranceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="负责人1" align="center" prop="userName" />
<el-table-column label="负责人2" align="center" prop="userNames" />
<!-- <el-table-column label="车辆" align="center" prop="carName" />-->
<el-table-column label="车牌号" align="carNo" prop="carNo" />
<el-table-column label="保险公司名称" align="center" prop="company" />
<el-table-column label="内容" align="center" prop="content">
<template slot-scope="scope">
<el-button @click="viewContentFun(scope.row.content)" type="text" slot="reference">查看</el-button>
</template>
</el-table-column>
<el-table-column label="保险金额" align="center" prop="money" />
<el-table-column label="保险到期时间" align="center" prop="insuranceTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.insuranceTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改保险管理对话框 -->
<el-dialog :title="title" :visible.sync="open" width="750px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="负责人1" prop="userName">
<el-input v-model="form.userName" placeholder="请输入负责人1" />
</el-form-item>
<el-form-item label="负责人2" prop="userNames">
<el-input v-model="form.userNames" placeholder="请输入负责人2" />
</el-form-item>
<!-- <el-form-item label="车辆" prop="carId">
<template>
<el-select v-model="form.carId" placeholder="请选择车辆">
<el-option
v-for="item in carLists"
:key="item.id"
:label="item.carModel"
:value="item.id">
</el-option>
</el-select>
</template>
</el-form-item>-->
<el-form-item label="车牌号" prop="carNo">
<el-input v-model="form.carNo" placeholder="请输入车牌号" />
</el-form-item>
<el-form-item label="保险公司名称" prop="company">
<el-input v-model="form.company" placeholder="请输入保险公司名称" />
</el-form-item>
<el-form-item label="保险内容" prop="content">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
<el-form-item label="保险金额" prop="money">
<el-input v-model="form.money" placeholder="请输入保险金额" />
</el-form-item>
<el-form-item label="保险到期时间" prop="insuranceTime">
<el-date-picker clearable
v-model="form.insuranceTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择保险到期时间">
</el-date-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<el-dialog title="查看" :visible.sync="viewFlag" width="65%" append-to-body>
<div style="box-sizing: border-box;padding: 15px;overflow: hidden" v-html="viewContent"></div>
</el-dialog>
</div>
</template>
<script>
import { listInsurance, getInsurance, delInsurance, addInsurance, updateInsurance ,listCards} from "./api/insurance";
import editor from '@/components/Editor/index.vue'
export default {
name: "Insurance",
components: {editor},
data() {
return {
viewContent:"",
viewFlag:false,
carLists:[],
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
insuranceList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
deptId: null,
userId: null,
carId: null,
company: null,
content: null,
money: null,
insuranceTime: null,
},
//
form: {},
//
rules: {
userName: [
{ required: true, message: "负责人不能为空", trigger: "blur" }
],
carId: [
{ required: true, message: "车辆id不能为空", trigger: "blur" }
],
company: [
{ required: true, message: "保险公司名称不能为空", trigger: "blur" }
],
content: [
{ required: true, message: "保险内容不能为空", trigger: "blur" }
],
money: [
{ required: true, message: "保险金额不能为空", trigger: "blur" }
],
insuranceTime: [
{ required: true, message: "保险到期时间不能为空", trigger: "blur" }
],
carNo: [
{ required: true, message: "保险到期时间不能为空", trigger: "blur" }
],
}
};
},
created() {
this.getList();
this.listCards();
},
methods: {
viewContentFun(data){
this.viewContent =data;
this.viewFlag = true;
},
listCards(){
listCards().then(response => {
this.carLists = response.data;
});
},
/** 查询保险管理列表 */
getList() {
this.loading = true;
listInsurance(this.queryParams).then(response => {
this.insuranceList = response.data.records;
this.total = response.data.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
deptId: null,
userId: null,
carId: null,
company: null,
content: null,
money: null,
insuranceTime: null,
createTime: null,
createBy: null,
updateTime: null,
updateBy: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加保险管理";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getInsurance(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改保险管理";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateInsurance(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addInsurance(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除保险管理编号为"' + ids + '"的数据项?').then(function() {
return delInsurance(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('drivingSchool/system/insurance/export', {
...this.queryParams
}, `insurance_${new Date().getTime()}.xlsx`)
}
}
};
</script>

View File

@ -0,0 +1,315 @@
<template>
<el-dialog
:title="dialogTitle"
:visible.sync="visible"
width="80%"
top="5vh"
:before-close="handleClose"
>
<div class="statistics-dialog">
<div class="time-selector">
<common-time-select v-model="currentRange" @change="handleTimeChange" />
</div>
<!-- 加载状态 -->
<div class="loading-state" v-if="loading">
<i class="el-icon-loading"></i>
<span>数据加载中...</span>
</div>
<!-- 空状态 -->
<div class="empty-state" v-else-if="datas.length === 0">
<div class="empty-text">暂无数据</div>
</div>
<!-- 根据不同ID显示不同内容 -->
<div class="content-area" v-else>
<!-- 客户来源统计 -->
<div v-if="(id == 1 || id == 5)" class="customer-source">
<el-table :data="datas" style="width: 100%">
<el-table-column prop="name" label="客户来源" />
<el-table-column prop="theNum" label="数量" />
<el-table-column prop="theAmount" label="公示金额" />
</el-table>
</div>
<!-- 检测车型统计 -->
<div v-if="id == 2" class="car-model-stats">
<el-table :data="datas" style="width: 100%">
<el-table-column prop="goodsTitle" label="车型" />
<el-table-column prop="theNum" label="数量" />
</el-table>
</div>
<!-- 待收款统计 -->
<div v-if="id == 3 || id == 7" class="pending-payment">
<el-table
:data="datas"
style="width: 100%"
@row-click="handlePendingPaymentRowClick"
>
<el-table-column prop="sourceName" label="渠道" />
<el-table-column prop="pendingCount" label="台次" />
<el-table-column prop="pendingAmountYuan" label="金额" />
</el-table>
</div>
<!-- 检测类型统计 -->
<div v-if="id == 4" class="inspection-type">
<el-table :data="datas" style="width: 100%">
<el-table-column prop="skuName" label="检测类型" />
<el-table-column prop="orderCount" label="订单数量" />
</el-table>
</div>
<!-- 资料统计 -->
<div v-if="id == 6" class="document-stats">
<el-table :data="datas" style="width: 100%">
<el-table-column prop="fileName" label="文件名称" />
<el-table-column prop="createTime" label="上传时间" />
</el-table>
</div>
</div>
</div>
<!-- WorkOrderList 弹窗 -->
<el-dialog
title="工单详情"
:visible.sync="workOrderListVisible"
width="80%"
top="5vh"
append-to-body
v-if="workOrderListVisible"
>
<WorkOrderList
:show-search="true"
:show-summary="true"
:external-query-params="workOrderListQueryParams"
ref="workOrderList"
/>
<span slot="footer" class="dialog-footer">
<el-button @click="workOrderListVisible = false">关闭</el-button>
</span>
</el-dialog>
</el-dialog>
</template>
<script>
import * as statisticsApi from './api/statistics'
import CommonTimeSelect from '@/components/CommonTimeSelect'
import WorkOrderList from '@/views/partner/components/WorkOrderList.vue'
export default {
name: 'StatisticsDialog',
components: {
CommonTimeSelect,
WorkOrderList
},
props: {
visible: {
type: Boolean,
default: false
},
id: {
type: [String, Number],
required: true
},
dataId: {
type: [String, Number],
default: null
},
type: {
type: String,
default: ''
},
range: {
type: Array,
default: () => []
},
extraParams: {
type: Object,
default: () => ({})
}
},
data() {
return {
currentRange: [],
datas: [],
loading: false,
// WorkOrderList
workOrderListVisible: false,
workOrderListQueryParams: {}
}
},
computed: {
dialogTitle() {
const titles = {
1: '客户来源统计',
2: '检测车型统计',
3: '代收款',
4: '检测类型统计',
5: '客户来源统计',
6: '资料统计',
7: '来源-待收款'
}
return titles[this.id] || '统计详情'
}
},
watch: {
visible(val) {
if (val) {
this.currentRange = [...this.range]
this.loadData()
}
}
},
mounted() {
//
if (this.visible) {
this.currentRange = [...this.range]
this.loadData()
}
},
methods: {
handleClose() {
this.$emit('update:visible', false)
},
handleTimeChange(range) {
this.currentRange = range
this.loadData()
},
async loadData() {
this.loading = true
try {
const params = {
startTime: this.currentRange[0],
endTime: this.currentRange[1],
...this.extraParams
}
let response
switch (this.id) {
case 1:
response = await statisticsApi.getStaticsTable3Detail({ ...params, remark: this.type })
break
case 2:
response = await statisticsApi.getStaticsTable4(params)
break
case 3:
response = await statisticsApi.getStaticsTable5(params)
break
case 4:
response = await statisticsApi.queryInspectionSkuList(params)
break
case 5:
response = await statisticsApi.customerSourceCount({ ...params, businessId: this.dataId })
break
case 6:
response = await statisticsApi.getFileByType({ ...params, servicePackageId: 'jiance' })
this.processFileData(response.data)
break
case 7:
response = await statisticsApi.channelMoneyStaticsByBusi({ ...params, busi: this.type })
break
default:
console.warn('Unknown statistics type:', this.id)
}
if (response && response.data) {
//
if (this.id === 4) {
//
this.datas = Array.isArray(response.data) ? response.data : []
} else if (this.id === 6) {
// processFileData
} else {
// list data
this.datas = response.data.list || response.data.data || response.data || []
}
} else {
this.datas = []
}
} catch (error) {
this.$message.error('数据加载失败: ' + (error.message || ''))
console.error(error)
this.datas = []
} finally {
this.loading = false
}
},
processFileData(data) {
if (this.type === 'add') {
this.datas = data.insertFile || []
} else if (this.type === 'update') {
this.datas = data.updateFile || []
} else {
this.datas = []
}
},
//
handlePendingPaymentRowClick(row) {
// WorkOrderList
this.workOrderListQueryParams = {
datetimeRange: this.currentRange,
customerSource: row.sourceName,
...this.extraParams
};
// WorkOrderList
this.workOrderListVisible = true;
}
}
}
</script>
<style lang="scss" scoped>
.statistics-dialog {
.time-selector {
margin-bottom: 20px;
}
.loading-state {
text-align: center;
padding: 60px 0;
i {
font-size: 24px;
color: #409EFF;
margin-right: 10px;
}
span {
font-size: 14px;
color: #666;
}
}
.empty-state {
text-align: center;
padding: 60px 0;
.empty-text {
font-size: 14px;
color: #999;
}
}
.content-area {
max-height: 60vh;
overflow-y: auto;
}
// hover
::v-deep .el-table__row {
cursor: pointer;
&:hover {
background-color: #f5f7fa;
}
}
}
</style>

View File

@ -0,0 +1,119 @@
import request from '@/utils/request'
// 检测数量统计
export function getStaticsTable2(params) {
return request({
url: '/partnerOwn/partner/staticsTable2',
method: 'get',
params
})
}
// 营业额统计
export function getStaticsTable1(params) {
return request({
url: '/partnerOwn/partner/staticsTable1',
method: 'get',
params
})
}
// 业务渠道统计
export function getStaticsTable3(params) {
return request({
url: '/partnerOwn/partner/staticsTable3',
method: 'get',
params
})
}
// 检测车型统计
export function getStaticsTable4(params) {
return request({
url: '/partnerOwn/partner/staticsTable4',
method: 'get',
params
})
}
// 待收款统计
export function getStaticsTable5(params) {
return request({
url: '/partnerOwn/partner/staticsTable5',
method: 'get',
params
})
}
// 资料统计
export function getFileStatistics(params) {
return request({
url: '/partnerOwn/partner/fileStatistics',
method: 'get',
params
})
}
// 检测类型统计
export function queryInspectionSkuList(params) {
return request({
url: '/partnerOwn/partner/queryInspectionSkuList',
method: 'get',
params
})
}
// 成交金额图表
export function chartLineInspectionAmount(params) {
return request({
url: '/partnerOwn/partner/chartLineInspectionAmount',
method: 'get',
params
})
}
// 检测数量图表
export function chartLineInspectionNum(params) {
return request({
url: '/partnerOwn/partner/chartLineInspectionNum',
method: 'get',
params
})
}
// 客户来源统计详情
export function getStaticsTable3Detail(params) {
return request({
url: '/partnerOwn/partner/staticsTable3Detail',
method: 'get',
params
})
}
// 根据类型获取文件
export function getFileByType(params) {
return request({
url: '/partnerOwn/partner/getFileByType',
method: 'get',
params
})
}
// 渠道金额统计
export function channelMoneyStaticsByBusi(params) {
return request({
url: '/partnerOwn/partner/channelMoneyStaticsByBusi',
method: 'get',
params
})
}
// 渠道金额统计
export function customerSourceCount(params) {
return request({
url: '/partnerOwn/partner/customerSourceCount',
method: 'get',
params
})
}

View File

@ -0,0 +1,620 @@
<template>
<div class="business-statistics">
<div class="header">
<h1>{{ titles }}</h1>
</div>
<div class="container">
<!-- 检测数量统计 -->
<el-card class="stat-card">
<div class="title_">检测数量统计</div>
<div class="card-content">
<common-time-select v-model="ranges" @change="slectRangeInspectionCount"></common-time-select>
<div class="stats-grid">
<div class="stat-item" @click="openWorkOrderList({ chooseStatus: '1' })">
<div class="text_">订单数量</div>
<div class="value">{{ data2.allNum || 0 }}</div>
</div>
<div class="stat-item" @click="openWorkOrderList({ chooseStatus: '3' })">
<div class="text_">完成数量</div>
<div class="value">{{ data2.ywcNum || 0 }}</div>
</div>
<div class="stat-item" @click="openWorkOrderList({ status: '2' })">
<div class="text_">检测中数量</div>
<div class="value">{{ data2.jxzNum || 0 }}</div>
</div>
<div class="stat-item" @click="openWorkOrderList({ chooseStatus: '5' })">
<div class="text_">重检数量</div>
<div class="value">{{ data2.reinspectNum || 0 }}</div>
</div>
<div class="stat-item" @click="openWorkOrderList({ chooseStatus: '6' })">
<div class="text_">复检数量</div>
<div class="value">{{ data2.recheckNum || 0 }}</div>
</div>
<div class="stat-item" @click="openWorkOrderList({ chooseStatus: '4' })">
<div class="text_">退办理数量</div>
<div class="value">{{ data2.tblNum || 0 }}</div>
</div>
</div>
</div>
</el-card>
<!-- 营业额统计 -->
<el-card class="stat-card">
<div class="title_">营业额统计</div>
<div class="card-content">
<common-time-select v-model="rangeYYE" @change="slectRangeYYECount"></common-time-select>
<div class="stats-grid">
<div class="stat-item" @click="showOrderStatisticDialog('')">
<div class="text_">公示价格</div>
<div class="value">{{ data1.gsAmount || 0 }}</div>
</div>
<div class="stat-item" @click="showOrderStatisticDialog('')">
<div class="text_">应收款</div>
<div class="value">{{ data1.yskAmount || 0 }}</div>
</div>
<div class="stat-item" @click="showOrderStatisticDialog('1')">
<div class="text_">已收款</div>
<div class="value">{{ data1.yjsAmount || 0 }}</div>
</div>
<div class="stat-item" @click="showOrderStatisticDialog('0')">
<div class="text_">待收款</div>
<div class="value">{{ data1.dsAmount || 0 }}</div>
</div>
</div>
</div>
</el-card>
<!-- 业务渠道统计 -->
<el-card class="stat-card">
<div class="title_">业务渠道统计</div>
<div class="card-content">
<common-time-select v-model="rangeBusiness" @change="slectRangeBusinessCount"></common-time-select>
<div v-if="data3.length > 0" class="channel-table">
<div class="table-header">
<div class="col l_">业务渠道</div>
<div class="col n_">数量</div>
<div class="col r_">公示金额</div>
</div>
<div class="table-body">
<div
v-for="(item,index) in data3"
:key="index"
class="table-row"
@click="showDetailDialog(5, item.id)"
>
<div class="col l_">
<span :class="'channel-tag channel-tag--' + (index % 4)">{{ item.name }}</span>
</div>
<div class="col n_">{{ item.theNum }}</div>
<div class="col r_">{{ item.theAmount }}</div>
</div>
</div>
</div>
<div v-else class="no-data">暂无数据</div>
</div>
</el-card>
<!-- 资料统计 -->
<el-card class="stat-card">
<div class="title_">资料统计</div>
<div class="card-content">
<common-time-select v-model="rangeZL" @change="slectRangeZLCount"></common-time-select>
<div class="stats-grid">
<div class="stat-item" @click="showDetailDialog2(6, 'add')">
<div class="text_">新增</div>
<div class="value">{{ fileRes.addCount || 0 }}</div>
</div>
<div class="stat-item" @click="showDetailDialog2(6, 'update')">
<div class="text_">修改</div>
<div class="value">{{ fileRes.updateCount || 0 }}</div>
</div>
</div>
</div>
</el-card>
<!-- 检测车型统计 -->
<el-card class="stat-card">
<div class="title-header">
<div class="title_">检测车型统计</div>
<el-button type="text" @click="showDetailDialog(2)">更多 ></el-button>
</div>
<div class="card-content">
<common-time-select v-model="rangeGoods" @change="slectRangeGoodsCount"></common-time-select>
<div v-if="data4.length > 0" class="stats-grid">
<div class="stat-item" v-for="(item,index) in data4" :key="index">
<div class="text_">{{ item.goodsTitle || '' }}</div>
<div class="value">{{ item.theNum || '' }}</div>
</div>
</div>
<div v-else class="no-data">暂无数据</div>
</div>
</el-card>
<!-- 检测类型统计 -->
<el-card class="stat-card">
<div class="title-header">
<div class="title_">检测类型统计</div>
<el-button type="text" @click="showDetailDialog(4)">更多 ></el-button>
</div>
<div class="card-content">
<common-time-select v-model="rangeSku" @change="slectRangeSkuCount"></common-time-select>
<div v-if="skuList.length > 0" class="stats-grid">
<div class="stat-item" v-for="(skuData, index) in skuList" :key="index">
<div class="text_">{{ skuData.skuName }}</div>
<div class="value">{{ skuData.orderCount || 0 }}</div>
</div>
</div>
<div v-else class="no-data">暂无数据</div>
</div>
</el-card>
<!-- 待收款 -->
<el-card class="stat-card">
<div class="title-header">
<div class="title_">待收款</div>
</div>
<div class="card-content">
<common-time-select v-model="rangeDsk" @change="slectRangeDskCount"></common-time-select>
<div v-if="data5.length === 0" class="no-data">暂无数据</div>
<div v-else class="stats-grid">
<div
class="stat-item multi-line"
v-for="(item, index) in data5"
:key="index"
@click="showDetailDialog2(7, item.channel)"
>
<div class="text_">渠道{{ item.channel || '' }}</div>
<div class="sub-text">金额{{ item.theAmount || '' }}</div>
<div class="sub-text">台次{{ item.order_count || '' }}</div>
</div>
</div>
</div>
</el-card>
<!-- 统计详情弹窗 -->
<statistics-dialog
v-if="detailDialog.visible"
:visible.sync="detailDialog.visible"
:id="detailDialog.id"
:data-id="detailDialog.dataId"
:type="detailDialog.type"
:range="detailDialog.range"
/>
<!-- WorkOrderList 弹窗 -->
<el-dialog
title="工单详情"
:visible.sync="workOrderListVisible"
width="80%"
top="5vh"
append-to-body
v-if="workOrderListVisible"
>
<WorkOrderList
:show-search="true"
:show-summary="true"
:external-query-params="workOrderListQueryParams"
ref="workOrderList"
/>
<span slot="footer" class="dialog-footer">
<el-button @click="workOrderListVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
import * as statisticsApi from './api/statistics'
import CommonTimeSelect from '@/components/CommonTimeSelect'
import StatisticsDialog from './StatisticsDialog'
import WorkOrderList from '@/views/partner/components/WorkOrderList.vue'
export default {
name: 'BusinessStatistics',
components: {
CommonTimeSelect,
StatisticsDialog,
WorkOrderList
},
data() {
return {
titles: "业务统计",
//
data1: {},
data2: {},
data3: [],
data4: [],
data5: [],
fileRes: {},
skuList: [],
//
ranges: [],
rangeYYE: [],
rangeBusiness: [],
rangeZL: [],
rangeGoods: [],
rangeSku: [],
rangeDsk: [],
//
detailDialog: {
visible: false,
id: null,
dataId: null,
type: null,
range: []
},
orderStatisticDialog: {
visible: false,
status: null
},
// WorkOrderList
workOrderListVisible: false,
workOrderListQueryParams: {}
}
},
async mounted() {
await this.initTimeRange()
await this.loadAllData()
},
methods: {
//
initTimeRange() {
const currentTime = this.$dayjs().format('YYYY-MM-DD')
const ranges = [
'ranges', 'rangeYYE', 'rangeBusiness', 'rangeZL',
'rangeGoods', 'rangeSku', 'rangeDsk'
]
ranges.forEach(range => {
this[range] = [currentTime, currentTime]
})
},
//
async loadAllData() {
try {
await Promise.all([
this.getServerData1(),
this.getServerData2(),
this.getServerData3(),
this.getInspectionSku(),
this.getfive()
])
} catch (error) {
this.$message.error('数据加载失败')
console.error(error)
}
},
// WorkOrderList
openWorkOrderList(filters = {}) {
return;
//
this.workOrderListQueryParams = {
datetimeRange: this.ranges,
...filters
};
//
this.workOrderListVisible = true;
},
//
showDetailDialog(id, dataId = null) {
this.detailDialog = {
visible: true,
id,
dataId,
range: this.getRangeByType(id)
}
},
showDetailDialog2(id, type) {
this.detailDialog = {
visible: true,
id,
type,
range: this.getRangeByType(id)
}
},
//
showOrderStatisticDialog(status) {
// this.orderStatisticDialog = {
// visible: true,
// status
// }
},
showOrderCountDialog(type, data) {
//
this.showDetailDialog(type, data)
},
//
getRangeByType(id) {
const rangeMap = {
1: this.ranges,
2: this.rangeGoods,
3: this.rangeDsk,
4: this.rangeSku,
5: this.rangeBusiness,
6: this.rangeZL,
7: this.rangeDsk
}
return rangeMap[id] || this.ranges
},
//
async slectRangeInspectionCount(e) {
this.ranges = e
await this.getServerData2()
},
async slectRangeYYECount(e) {
this.rangeYYE = e
await this.getServerData1()
},
async slectRangeBusinessCount(e) {
this.rangeBusiness = e
await this.getServerData3()
},
async slectRangeZLCount(e) {
this.rangeZL = e
await this.getFileStatistics()
},
async slectRangeGoodsCount(e) {
this.rangeGoods = e
await this.getStaticsTable4()
},
async slectRangeSkuCount(e) {
this.rangeSku = e
await this.getInspectionSku()
},
async slectRangeDskCount(e) {
this.rangeDsk = e
await this.getStaticsTable5()
},
// API
async getServerData1() {
const params = {
startTime: this.rangeYYE[0],
endTime: this.rangeYYE[1]
}
const res = await statisticsApi.getStaticsTable1(params)
this.data1 = res.data
},
async getServerData2() {
const params = {
startTime: this.ranges[0],
endTime: this.ranges[1]
}
const res = await statisticsApi.getStaticsTable2(params)
this.data2 = res.data
},
async getServerData3() {
const params = {
startTime: this.rangeBusiness[0],
endTime: this.rangeBusiness[1]
}
const res = await statisticsApi.getStaticsTable3(params)
this.data3 = res.data
},
async getStaticsTable4() {
const params = {
startTime: this.rangeGoods[0],
endTime: this.rangeGoods[1]
}
const res = await statisticsApi.getStaticsTable4(params)
this.data4 = res.data
},
async getStaticsTable5() {
const params = {
startTime: this.rangeDsk[0],
endTime: this.rangeDsk[1]
}
const res = await statisticsApi.getStaticsTable5(params)
this.data5 = res.data
},
async getFileStatistics() {
const params = {
servicePackageId: 'jiance',
startTime: this.rangeZL[0],
endTime: this.rangeZL[1]
}
const res = await statisticsApi.getFileStatistics(params)
this.fileRes = res.data
},
async getInspectionSku() {
const params = {
startTime: this.rangeSku[0],
endTime: this.rangeSku[1]
}
const res = await statisticsApi.queryInspectionSkuList(params)
this.skuList = res.data
},
async getfive() {
await Promise.all([
this.getServerData1(),
this.getServerData2(),
this.getServerData3(),
this.getStaticsTable4(),
this.getStaticsTable5(),
this.getFileStatistics()
])
}
}
}
</script>
<style lang="scss" scoped>
.business-statistics {
padding: 20px;
background: #f4f5f6;
min-height: 100vh;
.header {
margin-bottom: 20px;
h1 {
font-size: 24px;
color: #101A3E;
margin: 0;
}
}
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
}
.stat-card {
margin-bottom: 20px;
.title_ {
font-size: 16px;
color: #101A3E;
margin-bottom: 15px;
font-weight: 600;
}
.title-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
}
.card-content {
margin-top: 15px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-top: 20px;
}
.stat-item {
background: #F7F8FC;
padding: 20px;
text-align: center;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: #e8f4ff;
transform: translateY(-2px);
}
&.multi-line {
text-align: left;
}
.text_ {
font-size: 14px;
color: #101A3E;
margin-bottom: 10px;
}
.value {
font-size: 20px;
font-weight: bold;
color: #0D2E8D;
}
.sub-text {
font-size: 12px;
color: #666;
margin-top: 5px;
}
}
.channel-table {
margin-top: 20px;
.table-header {
display: flex;
background: #F7F8FC;
padding: 15px;
border-radius: 8px 8px 0 0;
font-weight: 600;
}
.table-body {
border: 1px solid #F7F8FC;
border-top: none;
}
.table-row {
display: flex;
padding: 15px;
border-bottom: 1px solid #E3ECFB;
cursor: pointer;
transition: background 0.3s;
&:hover {
background: #f8f9fa;
}
&:last-child {
border-bottom: none;
}
}
.col {
&.l_ { width: 33%; }
&.n_ { width: 33%; text-align: center; }
&.r_ { width: 33%; text-align: right; }
}
}
.channel-tag {
display: inline-block;
padding: 4px 12px;
border-radius: 8px;
font-size: 12px;
&--0 { background: #f6f0e5; }
&--1 { background: #e6e6f9; }
&--2 { background: #e2ebfb; }
&--3 { background: #e4eff1; }
}
.no-data {
text-align: center;
color: #999;
padding: 40px 0;
}
}
@media (max-width: 768px) {
.business-statistics {
.container {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
}
</style>

View File

@ -120,6 +120,45 @@
</tbody>
</table>
</div>
<!-- 新增产值统计打印部分 -->
<div class="print-section" v-if="baseDataInfo.outputMoneyStatistics">
<h2>检测产值统计</h2>
<table class="print-table">
<thead>
<tr>
<th>类型</th>
<th>产值</th>
</tr>
</thead>
<tbody>
<tr>
<td>初检公示产值</td>
<td>
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.initialInspectionOutputValue) || 0 }}
</td>
</tr>
<tr>
<td>复检公示产值</td>
<td>
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.recheckInspectionOutputValue) || 0 }}
</td>
</tr>
<tr>
<td>初检合格产值</td>
<td>
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.initialInspectionPassOutputValue) || 0 }}
</td>
</tr>
<tr>
<td>复检合格产值</td>
<td>
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.recheckInspectionPassOutputValue) || 0 }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
</PrintButton>
@ -204,6 +243,48 @@
</div>
</div>
</div>
<!-- 新增的产值统计 -->
<div class="db_">
<div class="min_box">
<div class="ds_" style="margin-bottom: 0px;">
<img src="./assets/icon3.png" />
<div>初检公示产值</div>
</div>
<div class="num_2">
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.initialInspectionOutputValue) || 0 }}
</div>
</div>
<div class="min_box">
<div class="ds_" style="margin-bottom: 0px;">
<img src="./assets/icon4.png" />
<div>复检公示产值</div>
</div>
<div class="num_2">
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.recheckInspectionOutputValue) || 0 }}
</div>
</div>
</div>
<div class="db_">
<div class="min_box">
<div class="ds_" style="margin-bottom: 0px;">
<img src="./assets/icon3.png" />
<div>初检合格产值</div>
</div>
<div class="num_2">
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.initialInspectionPassOutputValue) || 0 }}
</div>
</div>
<div class="min_box">
<div class="ds_" style="margin-bottom: 0px;">
<img src="./assets/icon4.png" />
<div>复检合格产值</div>
</div>
<div class="num_2">
{{ (baseDataInfo.outputMoneyStatistics && baseDataInfo.outputMoneyStatistics.recheckInspectionPassOutputValue) || 0 }}
</div>
</div>
</div>
</div>
</el-dialog>
</template>
@ -241,6 +322,7 @@ export default {
staffInfo: {},
goodsStatistics: [],
inspectionStatistics: {},
outputMoneyStatistics: {}, //
},
projectCount: {
children: [],

View File

@ -0,0 +1,526 @@
<template>
<div class="work-order-list">
<el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="车牌号" prop="carNum">
<el-input
v-model="queryParams.carModelOrCarYear"
placeholder="请输入车牌号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="车龄" prop="carYear">
<el-input
v-model="queryParams.carYear"
placeholder="请输入车龄"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="检测类型" prop="goodsTitle">
<el-select
v-model="queryParams.skuName"
placeholder="请选择检测类型"
clearable
>
<el-option
v-for="dict in skuNames"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="业务渠道" prop="businessChannel">
<el-select
v-model="queryParams.businessChannel"
filterable
clearable
placeholder="请选择业务渠道"
>
<el-option
v-for="dict in businessList"
:key="dict.id"
:label="dict.name"
:value="dict.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="客户来源" prop="customerSource">
<el-select
v-model="queryParams.customerSource"
filterable
clearable
placeholder="请选择客户来源"
>
<el-option
v-for="dict in customerData"
:key="dict.id"
:label="dict.name"
:value="dict.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="支付方式" prop="payType">
<el-select
v-model="queryParams.payType"
placeholder="支付方式"
clearable
style="width: 240px"
>
<el-option
v-for="dict in payTypeOptions"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="筛选类型" prop="dateType">
<el-select
v-model="queryParams.dateType"
placeholder="请选择日期类型"
clearable
style="width: 180px"
>
<el-option label="检测时间" value="jcTime"/>
<el-option label="保险到期时间(交强)" value="bxTime"/>
<el-option label="保险到期时间(商业)" value="bxVehicleTime"/>
<el-option label="下次年检时间" value="nextInspectionTime"/>
</el-select>
</el-form-item>
<el-form-item prop="datetimeRange">
<el-date-picker
v-model="queryParams.datetimeRange"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
>
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索
</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<div class="summary-info" v-if="showSummary">
<span>公示应收价格汇总{{ moneyData.goodsPriceSum / 100 }} </span>
<span style="margin-left: 3%">实付金额汇总{{ moneyData.payMoneySum / 100 }} </span>
</div>
<el-table
v-loading="loading"
:data="infoList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center"/>
<el-table-column label="车牌号" align="center">
<template slot-scope="scope">
{{ scope.row.carNum || scope.row.certificateNum || "----" }}
</template>
</el-table-column>
<el-table-column label="业务渠道" align="center" prop="businessChannel"></el-table-column>
<el-table-column label="客户来源" align="center" prop="customerSource"></el-table-column>
<el-table-column label="车主姓名" prop="buyName"></el-table-column>
<el-table-column label="车主手机号" prop="buyPhone"></el-table-column>
<el-table-column label="检测结果" align="center" prop="isPass">
<template slot-scope="scope">
<span v-if="scope.row.isPass == '0'">不合格</span>
<span v-else-if="scope.row.isPass == '1'">合格</span>
<span v-else>进行中</span>
</template>
</el-table-column>
<el-table-column label="车辆类型" align="center" prop="goodsTitle"/>
<el-table-column label="检测项目" align="center" prop="skuName"/>
<el-table-column label="公示应收价格" align="center" prop="realPayMoney" width="100">
<template slot-scope="scope">
<span>{{ scope.row.goodsPrice / 100 }}</span>
</template>
</el-table-column>
<el-table-column label="实收金额" align="center" prop="realPayMoney">
<template slot-scope="scope">
<span>{{ scope.row.realPayMoney / 100 }}</span>
</template>
</el-table-column>
<el-table-column label="收款时间" align="center" prop="payTime"/>
<el-table-column label="接待员手机号" align="center" prop="workerPhone"/>
<el-table-column label="支付方式" align="center" prop="payType">
<template slot-scope="scope">
<span>{{ getDictLabel(payTypeOptions, scope.row.payType) }}</span>
</template>
</el-table-column>
<el-table-column label="承保公司" align="center" prop="insuranceCompanyName"/>
<el-table-column label="下次年检时间" align="center" prop="nextInspectionDate"/>
<el-table-column label="交强险到期日期" align="center" prop="insuranceExpiryDate"/>
<el-table-column label="商业险到期日期" align="center" prop="vehicleInsuranceExpiryDate"/>
<el-table-column label="开始时间" align="center" prop="startTime"/>
<el-table-column label="结束时间" align="center" prop="endTime"/>
<!-- <slot name="table-actions" slot-scope="scope" :row="scope.row"></slot> -->
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import { listWorkOrder, workOrderData, getCustomerSource } from '@/views/partner/api/workOrder.js'
import Pagination from '@/components/Pagination/index.vue'
export default {
name: 'WorkOrderList',
components: {
Pagination
},
props: {
showSearch: {
type: Boolean,
default: true
},
showSummary: {
type: Boolean,
default: true
},
showStatisticsBtn: {
type: Boolean,
default: true
},
payTypeOptions: {
type: Array,
default: () => []
},
//
externalQueryParams: {
type: Object,
default: () => ({})
}
},
data() {
return {
//
loading: true,
//
ids: [],
//
total: 0,
//
infoList: [{
"createTime": 1760160863000,
"updateTime": 1760160863000,
"creator": "4561",
"updater": "4561",
"deleted": false,
"tenantId": 180,
"id": 8517,
"inspectionOrderId": 9557,
"userId": 10247,
"buyName": "刘剑峰",
"buyPhone": "15884197348",
"userAddress": "四川省泸州市龙马潭区安宁镇柏杨村二十社4号",
"unitName": "",
"customerSourceId": 190,
"businessChannelId": 188,
"partnerId": null,
"workId": 4561,
"workerName": "何梅芳",
"workerPhone": "19161426141",
"workerAvatar": null,
"categoryId": 10,
"carNum": "川EAQ001",
"certificateNum": "",
"carModel": "江淮牌HFC1037DKLSR",
"carNature": "非营运",
"carStatus": "",
"carIdNo": "LJ11PABDXPC021805",
"carRegisterDate": 1691942400000,
"isPass": null,
"isRetrial": null,
"remark": null,
"recheckCount": 0,
"reinspectCount": 0,
"businessChannel": "合作商家",
"otherName": "何梅芳",
"status": "0",
"startTime": "2025-10-11 13:34",
"endTime": null,
"makeCert": null,
"year": "2025",
"month": "2025-10",
"day": "2025-10-11",
"customerSource": "三益汽贸",
"leadManId": null,
"meetManId": 4561,
"isMeetCar": "1",
"lastTitle": null,
"goodsName": null,
"realPayMoney": null,
"reduceMoney": 0,
"orderNo": null,
"goodsId": 912,
"goodsPrice": 45000,
"goodsTitle": "3.5T以下轻型货车(含皮卡)",
"partnerName": null,
"skuName": "年审",
"skuId": "71",
"payType": null,
"orderStatus": "0",
"nextMaintenanceDate": null,
"nextInspectionDate": null,
"roleId": null,
"payTime": null,
"otherPhone": "19161426141",
"driverLicenesImg": null,
"isPayOnline": null,
"isPickCar": "0",
"nowOrderNum": 1,
"returnCarUserId": null,
"isReturnCar": 0,
"returnType": null,
"inspectionWorkNodes": null,
"workNodeId": null,
"dealUserId": null,
"workNodeStatus": null,
"selectType": null,
"projectName": null,
"leadManName": null,
"additionalRecording": null,
"orderId": null,
"appointmentId": null,
"sourceType": null,
"meetCarId": null,
"meetAddress": null,
"latitude": null,
"longitude": null,
"appointmentDay": null,
"appointmentTime": null,
"meetType": null,
"content": null,
"inspectionInfoId": null,
"isMeetPickCar": null,
"pickCarAddress": null,
"cashierConfirm": null,
"cashierConfirmRemark": null,
"cashierConfirmTime": null,
"accountingConfirm": null,
"accountingConfirmRemark": null,
"insuranceExpiryDate": null,
"vehicleInsuranceExpiryDate": null,
"insuranceCompanyName": null,
"datetimeRange": null,
"settlementAmount": null,
"settlementUser": null,
"settlementDiscount": null,
"receivablesAccount": null
}],
//
customerData: [],
//
businessList: [],
//
skuNames: [
{ label: "年审", value: "年审" },
{ label: "上户", value: "上户" },
{ label: "非定检", value: "非定检" },
{ label: "双燃料", value: "双燃料" },
{ label: "其他检测", value: "其他检测" }
],
//
queryParams: {
pageNum: 1,
pageSize: 10,
dateType: 'jcTime',
...this.externalQueryParams
},
//
moneyData: {
payMoneySum: 0,
goodsPriceSum: 0
},
//
statisticsDialogVisible: false,
statisticsId: 1, //
statisticsRange: [], //
statisticsExtraParams: {}, //
statisticsDialogComponent: null //
}
},
created() {
this.getList()
this.customerSource()
this.business()
},
watch: {
externalQueryParams: {
handler(newVal) {
this.queryParams = {
...this.queryParams,
...newVal
}
this.getList()
},
deep: true
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true
listWorkOrder(this.queryParams).then(response => {
console.log('API Response:', response);
//
if (response && response.data) {
this.infoList = response.data.records || response.data.list || response.data || []
this.total = response.data.total || 0
} else {
this.infoList = []
this.total = 0
}
this.loading = false
console.log('Processed infoList:', this.infoList);
}).catch(error => {
console.error('Error fetching work orders:', error)
this.infoList = []
this.total = 0
this.loading = false
})
workOrderData(this.queryParams).then(response => {
this.moneyData = response.data
}).catch(error => {
console.error('Error fetching work order data:', error)
})
},
/** 获取客户来源 */
customerSource() {
const data = { type: 1 }
getCustomerSource(data).then(res => {
this.customerData = res.data
})
},
/** 获取业务渠道 */
business() {
const data = { type: 0 }
getCustomerSource(data).then(res => {
this.businessList = res.data
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
this.$emit('query-change', this.queryParams)
},
/** 重置按钮操作 */
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
dateType: 'jcTime'
}
this.handleQuery()
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.$emit('selection-change', selection)
},
/** 获取字典标签 */
getDictLabel(dictOptions, value) {
const dict = dictOptions.find(item => item.value === value)
return dict ? dict.label : ''
},
/** 刷新列表 */
refresh() {
this.getList()
},
/** 打开统计弹窗 */
openStatisticsDialog() {
//
if (this.queryParams.datetimeRange && this.queryParams.datetimeRange.length === 2) {
this.statisticsRange = [...this.queryParams.datetimeRange];
} else {
// 30
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 30 * 24 * 60 * 60 * 1000);
this.statisticsRange = [this.formatDate(start), this.formatDate(end)];
}
//
this.statisticsExtraParams = {
businessChannel: this.queryParams.businessChannel,
customerSource: this.queryParams.customerSource,
skuName: this.queryParams.skuName,
payType: this.queryParams.payType
};
// 1
this.statisticsId = 1;
//
this.loadStatisticsDialogComponent();
//
this.statisticsDialogVisible = true;
},
/** 格式化日期 */
formatDate(date) {
const year = date.getFullYear();
const month = ('0' + (date.getMonth() + 1)).slice(-2);
const day = ('0' + date.getDate()).slice(-2);
return year + '-' + month + '-' + day;
},
/** 动态加载统计弹窗组件 */
async loadStatisticsDialogComponent() {
if (!this.statisticsDialogComponent) {
const { default: StatisticsDialog } = await import('@/views/inspection/businessStatistics/StatisticsDialog.vue');
this.statisticsDialogComponent = StatisticsDialog;
}
}
}
}
</script>
<style scoped>
.summary-info {
margin-bottom: 10px;
font-weight: bold;
}
</style>

View File

@ -0,0 +1,190 @@
<template>
<el-dialog title="添加车辆信息" :visible.sync="dialogVisible" width="800px" @close="handleClose">
<el-form ref="carForm" :model="car" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="车牌号" prop="licenseNumber">
<el-input v-model="car.licenseNumber" placeholder="请输入车牌号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="发动机号码" prop="engineNumber">
<el-input v-model="car.engineNumber" placeholder="请输入发动机号码" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="车架号" prop="vin">
<el-input v-model="car.vin" placeholder="请输入车架号" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="车辆品牌" prop="carBrand">
<CarBrandSelector v-model="car.carBrand" ref="brandSelector" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="车辆型号" prop="carModel">
<el-input v-model="car.carModel" placeholder="请输入车辆型号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="车辆类别" prop="carCategory">
<el-select v-model="car.carCategory" placeholder="请选择车辆类别">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.DICT_CAR_CATEGORY)" :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="8">
<el-form-item label="车辆性质" prop="carNature">
<el-select v-model="car.carNature" placeholder="请选择车辆性质">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.DICT_CAR_NATURE)" :key="dict.value"
:label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="注册日期" prop="carRegisterDate">
<el-date-picker clearable v-model="car.carRegisterDate" type="date" value-format="timestamp"
placeholder="选择车辆注册日期" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="车辆颜色" prop="carColor">
<el-input v-model="car.carColor" placeholder="请输入车辆颜色" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="下次年检日期" prop="nextInspectionDate">
<el-date-picker clearable v-model="car.nextInspectionDate" type="date" value-format="timestamp"
placeholder="选择下次年检日期" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="保险到期日期" prop="insuranceExpiryDate">
<el-date-picker clearable v-model="car.insuranceExpiryDate" type="date" value-format="timestamp"
placeholder="选择保险到期日期" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</div>
</el-dialog>
</template>
<script>
import request from '@/utils/request';
import { Message } from 'element-ui';
import CarBrandSelector from '@/layout/components/CarBrandSelector/index.vue';
import { DICT_TYPE, getDictDatas } from '@/utils/dict';
import { getCarBrand } from '@/api/base/carbrand';
export default {
name: "AddCarForm",
components: {
CarBrandSelector
},
props: {
customerInfo: {
type: Object,
required: true
}
},
data() {
return {
dialogVisible: false,
car: {
licenseNumber: '',
engineNumber: '',
vin: '',
brandAndModel: [],
carModel: '',
carCategory: '',
carNature: '',
carRegisterDate: '',
carColor: '',
nextInspectionDate: '',
insuranceExpiryDate: ''
},
rules: {
licenseNumber: [
{ required: true, message: '请输入车牌号', trigger: 'blur' }
],
carBrand: [
{ required: true, message: '请选择车辆品牌', trigger: 'change' }
],
carModel: [
{ required: false, message: '请输入车辆型号', trigger: 'blur' }
],
carCategory: [
{ required: true, message: '车辆类别不能为空', trigger: 'blur' }
],
carNature: [
{ required: true, message: '车辆性质不能为空', trigger: 'blur' }
]
}
};
},
methods: {
open() {
this.dialogVisible = true;
},
handleClose() {
this.$refs.carForm.resetFields();
this.dialogVisible = false;
},
async submitForm() {
try {
// 使Promisevalidate
await new Promise((resolve, reject) => {
this.$refs.carForm.validate((valid) => {
if (valid) {
resolve();
} else {
reject(new Error('表单验证失败'));
}
});
});
this.customerInfo.car=this.car;
const createResult = await request({
url: '/base/custom/saveCustomerAndCar',
method: 'post',
data: this.customerInfo
});
//
Message.success('添加车辆成功');
this.$emit('success', {
id: createResult.carId,
...this.car,
frameNumber: this.car.vin //
});
this.handleClose();
} catch (error) {
// Element UI
if (error.message !== '表单验证失败') {
console.error('添加车辆失败:', error);
Message.error('添加车辆失败,请重试!');
}
}
},
getDictDatas(type) {
return getDictDatas(type);
}
}
};
</script>
<style scoped></style>

View File

@ -1,17 +1,27 @@
<template>
<div>
<el-select v-model="carSelected" clearable>
<el-option v-for="car in carList" :key="car.id" :label="car.licenseNumber" :value="car.id"/>
</el-select>
<div style="display: flex; align-items: center;">
<el-select v-model="carSelected" clearable style="width: 200px;">
<el-option v-for="car in carList" :key="car.id" :label="car.licenseNumber" :value="car.id"/>
</el-select>
<el-button type="primary" size="small" @click="addNewCar" style="margin-left: 8px;">
新增车辆
</el-button>
</div>
<AddCarForm ref="addCarForm" :customer-info="customerInfo" @success="handleCarAddSuccess" />
</div>
</template>
<script>
import {remindCarMainPage} from "@/api/base/carmain";
import AddCarForm from "./AddCarForm.vue";
export default {
name: "CarChoose",
components: {
AddCarForm
},
props: {
value: {
type: Object
@ -19,6 +29,10 @@ export default {
cusName: {
type: String,
},
customerInfo: {
type: Object,
required: true
},
inList:{
type: Object,
default: null,
@ -79,6 +93,16 @@ export default {
}
} catch {
}
},
addNewCar() {
//
this.$refs.addCarForm.open();
},
handleCarAddSuccess(carInfo) {
//
this.getCarList();
//
this.carSelected = carInfo.id;
}
}
}

View File

@ -62,8 +62,10 @@ export default {
}
},
getWorkType(val){
if (!val) return ''
const data = this.getDictDatas(DICT_TYPE.REPAIR_WORK_TYPE)
return data.find(item => item.value === val).label
const item = data.find(item => item.value === val)
return item ? item.label : ''
}
}
}

View File

@ -0,0 +1,287 @@
<template>
<div class="detail-container">
<!-- 顶部导航 -->
<!-- <el-row class="navbar" type="flex" align="middle">
<el-col :span="2">
<el-button icon="el-icon-arrow-left" @click="goBack" circle></el-button>
</el-col>
<el-col :span="20" class="navbar-title">详情页</el-col>
</el-row> -->
<!-- 选项卡 -->
<el-tabs v-model="currentTab" @tab-click="switchTab">
<el-tab-pane
v-for="(item, index) in tapList"
:key="index"
:label="item.label"
:name="index.toString()"
></el-tab-pane>
</el-tabs>
<!-- 日期选择 -->
<el-date-picker
v-model="queryParams.dateRange"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="loadData"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="margin-bottom: 20px"
></el-date-picker>
<!-- 统计卡片 -->
<el-row :gutter="20">
<el-col :span="6" v-for="(sKey, idx) in statsData" :key="idx">
<!-- 外层卡片点击 -->
<el-card
shadow="hover"
class="section-card"
@click.native="goList(sKey.selectType)"
>
<div slot="header" class="section-header">
<span class="section-tag">{{ sKey.name }}</span>
<span class="section-total">{{ sKey.total }} 台次</span>
</div>
<el-row :gutter="10" class="section-body">
<el-col :span="12" v-for="(item, i) in sKey.children" :key="i">
<!-- 内层点击绑定到 div 并阻止冒泡 -->
<div
class="kv"
@click.stop="goList(sKey.selectType, item.repairType)"
>
<div class="kv-number">{{ item.count }}</div>
<div class="kv-label">{{ dictData[item.repairType] }}</div>
<dict-tag :type="'repair_type'" :value="item.repairType" />
</div>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<!-- 财务信息 -->
<div class="finance">
<el-card shadow="hover" class="fin-card receivable">
<div class="fin-top">
<span class="fin-title">应收款</span>
<el-button
type="text"
icon="el-icon-view"
@click.stop="toggleFinance('receivable')"
></el-button>
</div>
<div class="fin-amount" @click="goListByPayStatus('receivable')">
{{
financeVisibility.receivable
? formatCurrency(otherInfo.receivableAmount || 0)
: "*****"
}}
</div>
<div class="fin-bottom" @click="goListByPayStatus('receivable')">
<span>{{ otherInfo.receivableCount }} 台次</span>
</div>
</el-card>
<el-card shadow="hover" class="fin-card collected">
<div class="fin-top">
<span class="fin-title">已收款</span>
<el-button
type="text"
icon="el-icon-view"
@click.stop="toggleFinance('collected')"
></el-button>
</div>
<div class="fin-amount" @click="goListByPayStatus('receivedAmount')">
{{
financeVisibility.collected
? formatCurrency(otherInfo.receivedAmount)
: "*****"
}}
</div>
<div class="fin-bottom" @click="goListByPayStatus('receivedAmount')">
<span>{{ otherInfo.receivedCount }} 台次</span>
</div>
</el-card>
<el-card shadow="hover" class="fin-card pending">
<div class="fin-top">
<span class="fin-title">待收款</span>
<el-button
type="text"
icon="el-icon-view"
@click.stop="toggleFinance('pending')"
></el-button>
</div>
<div class="fin-amount" @click="goListByPayStatus('pendingAmount')">
{{
financeVisibility.pending
? formatCurrency(otherInfo.pendingAmount)
: "*****"
}}
</div>
<div class="fin-bottom" @click="goListByPayStatus('pendingAmount')">
<span>{{ otherInfo.pendingCount }} 台次</span>
</div>
</el-card>
</div>
</div>
</template>
<script>
import request from "@/utils/request";
import { formatCurrency, getDictByCode, getDateRange } from "@/utils/utils";
// import { checkPermi } from "@/utils/permission";
export default {
name: "DetailPagePC",
dicts: ["repair_type"],
data() {
return {
currentTab: "0",
queryParams: {
dateRange: [],
},
tapList: [
{ label: "本日", value: "day" },
{ label: "本月", value: "month" },
{ label: "全部", value: "all" },
],
statsData: [],
financeVisibility: {
receivable: false,
collected: false,
pending: false,
},
dictData: {},
otherInfo: {},
};
},
async mounted() {
this.queryParams.dateRange = getDateRange("day");
// this.loadFinanceVisibility();
this.loadData();
},
methods: {
// checkPermi,
switchTab(tab) {
this.currentTab = tab.name;
const { value } = this.tapList[tab.name];
this.queryParams.dateRange = getDateRange(value);
this.loadData();
},
async getDict() {
const list = await getDictByCode("repair_type");
this.dictData =
list?.reduce((map, item) => {
map[item.value] = item.label;
return map;
}, {}) ?? {};
},
loadData() {
request({
url: "/repair/tickets/getBossNumStatistics",
method: "get",
params: this.queryParams,
}).then((res) => {
if (res.data) {
this.statsData = res.data.stats;
this.otherInfo = res.data;
}
});
},
goList(selectType, repairType) {
console.log(selectType, repairType, 6666);
//
this.$router.push({
path: "/repair/tickets/ticket-manager",
query: {
selectType: selectType,
repairType: repairType,
},
});
},
goListByPayStatus(payStatus) {
//
this.$router.push({
path: "/repair/tickets/ticket-manager",
query: { payStatus: payStatus },
});
},
formatCurrency(amount) {
return formatCurrency(amount);
},
toggleFinance(key) {
this.financeVisibility[key] = !this.financeVisibility[key];
localStorage.setItem(
"financeVisibility",
JSON.stringify(this.financeVisibility)
);
},
loadFinanceVisibility() {
try {
const saved = JSON.parse(localStorage.getItem("financeVisibility"));
if (saved) this.financeVisibility = saved;
} catch (e) {
console.error(e);
}
},
goBack() {
this.$router.back();
},
},
};
</script>
<style scoped>
.detail-container {
padding: 20px;
}
.navbar-title {
text-align: center;
font-size: 20px;
font-weight: 600;
}
.section-card {
cursor: pointer;
}
.section-tag {
font-weight: 600;
color: #2a6cff;
}
.section-total {
float: right;
}
.kv-number {
font-weight: 700;
font-size: 18px;
}
.kv-label {
color: #7f8fa4;
}
.finance {
display: flex;
gap: 20px;
margin-top: 20px;
}
.fin-card {
flex: 1;
cursor: pointer;
padding: 20px;
}
.fin-title {
font-size: 14px;
color: #7f8fa4;
}
.fin-amount {
font-size: 20px;
font-weight: 700;
margin: 10px 0;
}
.fin-bottom {
font-size: 14px;
color: #7f8fa4;
}
</style>

View File

@ -269,16 +269,24 @@ export default {
if (!row.id || property !== 'goods') {
// cell
this.saveCellClick(row, cell)
cell.querySelector('.item__txt').style.display = 'none'
cell.querySelector('.item__input').style.display = 'inline'
cell.querySelector('input').focus()
// DOM
const itemTxt = cell.querySelector('.item__txt')
const itemInput = cell.querySelector('.item__input')
const inputElement = cell.querySelector('input')
if (itemTxt) itemTxt.style.display = 'none'
if (itemInput) itemInput.style.display = 'inline'
if (inputElement) inputElement.focus()
}
}
},
/** 取消编辑状态 */
cancelEditable(cell) {
cell.querySelector('.item__txt').style.display = 'inline'
cell.querySelector('.item__input').style.display = 'none'
const itemTxt = cell.querySelector('.item__txt')
const itemInput = cell.querySelector('.item__input')
if (itemTxt) itemTxt.style.display = 'inline'
if (itemInput) itemInput.style.display = 'none'
},
/** 保存进入编辑的cell */
saveCellClick(row, cell) {

View File

@ -24,7 +24,8 @@
<template slot="label">
车辆选择
</template>
<CarChoose v-model="selectCar" :cus-name="selectUser.cusName" :in-list="carInData"/>
<CarChoose v-if="selectUser && selectUser.id" v-model="selectCar" :cus-name="selectUser.cusName" :customer-info="selectUser" :in-list="carInData"/>
<span v-else>请先选择客户</span>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">