lanan-app/pages/my/StatisticsInfo.vue
2025-10-17 15:25:04 +08:00

1738 lines
43 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="statistics-container">
<view class="top—1"></view>
<!-- 顶部导航栏 -->
<view class="nav-bar">
<uni-icons @click="goBack" size="24" type="left" color="#fff"></uni-icons>
<view class="title-tabs">
<text :class="['tab-item', activeTab === 'order' ? 'active' : '']" @click="switchTab('order')">
业务统计
</text>
<text :class="['tab-item', activeTab === 'category' ? 'active' : '']" @click="switchTab('category')">
员工统计
</text>
</view>
<view class="filter-btn" style="margin-left: 30rpx;"></view>
</view>
<!-- 工单统计 -->
<view v-if="activeTab === 'order'">
<!-- <view class="stat-cards">
<view class="stat-card">
<text class="stat-num">{{ countNum.yjdNum }}</text>
<text class="stat-label">已接单</text>
</view>
<view class="stat-card">
<text class="stat-num">{{ countNum.jyzNum }}</text>
<text class="stat-label">救援中</text>
</view>
<view class="stat-card">
<text class="stat-num">{{ countNum.ywcNum }}</text>
<text class="stat-label">已完成</text>
</view>
</view> -->
<view class="stat-cards-container">
<view class="stat-cards">
<view class="stat-card">
<text class="stat-num">{{ countNum.yjdNum }}</text>
<text class="stat-label">已接单</text>
</view>
<view class="stat-card">
<text class="stat-num">{{ countNum.jyzNum }}</text>
<text class="stat-label">救援中</text>
</view>
<view class="stat-card">
<text class="stat-num">{{ countNum.ywcNum }}</text>
<text class="stat-label">已完成</text>
</view>
</view>
<!-- 展开收起按钮 -->
<view class="expand-btn" @click="toggleExpand">
<uni-icons :type="isExpanded ? 'top' : 'bottom'" size="20" color="#327DFB"></uni-icons>
<!-- <text class="expand-text">{{ isExpanded ? '收起' : '展开' }}</text> -->
</view>
</view>
<!-- 展开后的额外统计块 -->
<view v-if="isExpanded" class="expanded-cards">
<view class="stat-cards" style="margin-bottom: 20rpx;">
<view class="stat-card">
<text class="stat-num">{{ countNum.yingskNum }}</text>
<text class="stat-label">应收款(元)</text>
</view>
<view class="stat-card">
<text class="stat-num">{{ countNum.yiskNum }}</text>
<text class="stat-label">已收款(元)</text>
</view>
</view>
<view class="stat-cards">
<view class="stat-card">
<text class="stat-num">{{ countNum.dskNum }}</text>
<text class="stat-label">待收款(元)</text>
</view>
<view class="stat-card">
<text class="stat-num">{{ countNum.dqcNum }}</text>
<text class="stat-label">待取车</text>
</view>
</view>
</view>
<!-- 搜索和时间筛选区域 -->
<view class="search-filter-area">
<!-- 全部、筛选选项卡 -->
<view class="tab-choose-class">
<view class="tab-content" @click="tabChoose(item, index)" v-for='(item, index) in tabTwoList'
:key='index'>
<view class='tab-name-class' :style='{ color: index + 1 == tabValue ? "#101A3E" : "#8D90A6" }'>
{{ item }}
<view class="icon-tab" v-if='index >= 1'>
<image src="@/static/icons/homeOrderCard/xialajiantou.svg" mode="scaleToFill"></image>
</view>
</view>
<view class="tab-icon"
:style='{ background: index + 1 == tabValue ? "linear-gradient( 180deg, #054DF3 0%, #55A3FF 100%)" : "" }'>
</view>
</view>
</view>
<!-- 筛选弹窗 -->
<u-popup :show="isShowPop" ref="popup" round="55" @close="closePop" @open="openPop">
<view class="popup-content" :style="{'height':windowsHeight + 'px'}">
<view class="popup-header">
<text>选择筛选项</text>
<view style="display: flex;justify-content: space-between" @click="clearSelection">
<u-icon name="close-circle" size="18" color="#327DFB"></u-icon>
<text style="color:#327DFB;">清除筛选项</text>
</view>
</view>
<scroll-view scroll-y="true" class="scroll_view_style" style="height: 80%">
<view class="popup-body">
<view style="">
<view class="filter-section" style="padding: 0 10rpx;">
<text>时间范围</text>
<view style="margin-top: 10rpx">
<uni-datetime-picker v-model="queryParams.rangeTime" type="daterange"
rangeSeparator="至" />
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;">
<text>客户车牌号</text>
<view class="options">
<u-search placeholder="请输入客户车牌号" v-model="queryParams.licenseNum"
@custom="refreshList" @search="refreshList"
:showAction="false"></u-search>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;">
<text>客户姓名</text>
<view class="options">
<u-search placeholder="请输入客户姓名" v-model="queryParams.connectionName"
@custom="refreshList" @search="refreshList"
:showAction="false"></u-search>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;">
<text>客户手机号</text>
<view class="options">
<u-search placeholder="请输入客户手机号" v-model="queryParams.connectionPhone"
@custom="refreshList" @search="refreshList"
:showAction="false"></u-search>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;">
<text>救援区域(省/市/区县/街道乡镇)</text>
<view class="options">
<u-search placeholder="请输入救援区域" v-model="queryParams.rescuePosition"
@custom="refreshList" @search="refreshList"
:showAction="false"></u-search>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;" v-if="hasRole('ddzx')">
<text>渠道</text>
<view class="options">
<view class="select-field" @click="showPicker('channel')">
<text
class="select-text">{{ queryParams.channel ? getSelectedLabel('channel', queryParams.channel) : '请选择渠道' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;" v-if="hasRole('ddzx')">
<text>来源</text>
<view class="options">
<view class="select-field" @click="showPicker('source')">
<text
class="select-text">{{ queryParams.source ? getSelectedLabel('source', queryParams.source) : '请选择来源' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;" v-if="hasRole('ddzx')">
<text>调度</text>
<view class="options">
<view class="select-field" @click="showPicker('secondDispatchId')">
<text
class="select-text">{{ queryParams.secondDispatchId ? getSelectedLabel('secondDispatchId', queryParams.secondDispatchId) : '请选择调度' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;">
<text>救援司机</text>
<view class="options">
<view class="select-field" @click="showPicker('rescueDriver')">
<text
class="select-text">{{ queryParams.rescueDriver ? getSelectedLabel('rescueDriver', queryParams.rescueDriver) : '请选择救援司机' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
<view class="filter-section" style="padding: 0 10rpx;">
<text>救援车牌号</text>
<view class="options">
<view class="select-field" @click="showPicker('rescueCar')">
<text
class="select-text">{{ queryParams.rescueCar ? getSelectedLabel('rescueCar', queryParams.rescueCar) : '请选择救援车牌号' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
<view class="filter-section">
<view
style="display: flex; flex-direction: row; justify-content: space-between; width: 100%;">
<view style="width: 50%; padding: 10rpx;">
<text>救援状态</text>
<view class="options">
<view class="select-field" @click="showPicker('rescueStatus')">
<text
class="select-text">{{ queryParams.rescueStatus ? getSelectedLabel('rescueStatus', queryParams.rescueStatus) : '请选择救援状态' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
<view style="width: 50%; padding: 10rpx;">
<text>救援类型</text>
<view class="options">
<view class="select-field" @click="showPicker('rescueType')">
<text
class="select-text">{{ queryParams.rescueType ? getSelectedLabel('rescueType', queryParams.rescueType) : '请选择救援类型' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
</view>
</view>
<view class="filter-section">
<view
style="display: flex; flex-direction: row; justify-content: space-between; width: 100%;">
<view style="width: 50%; padding: 10rpx;">
<text>故障类型</text>
<view class="options">
<view class="select-field" @click="showPicker('faultType')">
<text
class="select-text">{{ queryParams.faultType ? getSelectedLabel('faultType', queryParams.faultType) : '请选择故障类型' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
<view style="width: 50%; padding: 10rpx;">
<text>故障现象</text>
<view class="options">
<view class="select-field" @click="showPicker('phenomenon')">
<text
class="select-text">{{ queryParams.phenomenon ? getSelectedLabel('phenomenon', queryParams.phenomenon) : '请选择故障现象' }}</text>
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<view class="popup-footer">
<u-button @click="closePop" style="background: #F7F8FC;color: black">取消</u-button>
<u-button @click="submitPop">确定</u-button>
</view>
</view>
</u-popup>
</view>
<!-- 订单列表 -->
<view class="dil">
<!-- 初始加载状态 -->
<view class="ques" v-if="initialLoading">
<view class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中,请稍候...</text>
</view>
</view>
<!-- 非加载中且无数据状态 -->
<view class="ques" v-else-if="!initialLoading && orderList.length == 0">
<image src="@/static/quesheng.png" mode=""></image>
</view>
<view v-else>
<order-card-vue @refresh="getlist" :status="gindex" v-for="(item,index) in orderList" :key="index"
:orderData="item" :brandList="brandList" :staffList="staffList"
:activeTab="activeInfoTab"></order-card-vue>
<!-- 底部加载更多状态 -->
<view v-if="loadingMore" class="loading-more">
<view class="loading-spinner mini"></view>
<text>正在加载更多...</text>
</view>
<view v-if="!hasMore" class="no-more-data">
<text>没有更多数据了</text>
</view>
</view>
<view style="width: 100%; height: 60px;"></view>
</view>
</view>
<!-- 分类统计 -->
<view v-else>
<!-- 二级Tab -->
<!-- <view class="sub-tabs">
<text v-for="(tab, index) in subTabs" :key="index"
:class="['sub-tab-item', activeSubTab === tab.value ? 'active' : '']"
@click="switchSubTab(tab.value)">
{{ tab.label }}
</text>
</view> -->
<view class="sub-tabs">
<text v-for="(tab, index) in filteredSubTabs" :key="index"
:class="['sub-tab-item', activeSubTab === tab.value ? 'active' : '']"
@click="switchSubTab(tab.value)">
{{ tab.label }}
</text>
</view>
<!-- 列表部分 -->
<category-list :type="activeSubTab" :list="currentList" />
</view>
<lzc-picker ref="lzcPickerRescueStatus" :pickerList="jyStatusOptions" pickerTittle="选择救援状态"
@change="onPickerConfirm('rescueStatus', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerChannel" :pickerList="channelOptions" pickerTittle="选择渠道"
@change="onPickerConfirm('channel', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerSource" :pickerList="sourceOptions" pickerTittle="选择来源"
@change="onPickerConfirm('source', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerDispatcher" :pickerList="dispatcherOptions" pickerTittle="选择调度"
@change="onPickerConfirm('secondDispatchId', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerRescueType" :pickerList="jyTypeOptions" pickerTittle="选择救援类型"
@change="onPickerConfirm('rescueType', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerFaultType" :pickerList="faultTypeOptions" pickerTittle="选择故障类型"
@change="onPickerConfirm('faultType', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerPhenomenon" :pickerList="phenomenonOptions" pickerTittle="选择故障现象"
@change="onPickerConfirm('phenomenon', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerRescueDriver" :pickerList="rescueDriverOptions" pickerTittle="选择救援司机"
@change="onPickerConfirm('rescueDriver', $event)"></lzc-picker>
<lzc-picker ref="lzcPickerRescueCar" :pickerList="rescueCarOptions" pickerTittle="选择救援车牌号"
@change="onPickerConfirm('rescueCar', $event)"></lzc-picker>
</view>
</template>
<script>
// import OrderCardVue from '../../components/orderCard/StatisticsInfoOrderCard.vue';
import request from '../../utils/request';
import CategoryList from '../../components/categoryList/CategoryList.vue';
import lzcPicker from '@/components/lzc-picker/lzc-picker.vue';
import {
hasRole,
hasRoleNew
} from "@/utils/auth";
export default {
name: "StatisticsInfo",
data() {
return {
activeTab: "order", // 默认进入工单统计
activeSubTab: "driver", // 默认进入救援司机
// 工单统计相关数据
isExpanded: false,
tabValue: 1,
timeType: "all",
range: [],
startTimeStr: null,
endTimeStr: null,
searchText: '',
searchKeyword: null,
gindex: 1,
brandList: [],
staffList: [],
orderList: [],
total: 0,
pageNum: 1,
pageSize: 10,
totalPages: 0,
activeInfoTab: 4, // 信息选项卡
initialLoading: true,
loadingMore: false,
hasMore: true,
scrollTop: 0,
totalNum: null,
// 筛选相关数据
isShowPop: false,
windowsHeight: 700,
tabTwoList: ["全部", "筛选"],
queryParams: {
pageNum: 1,
pageSize: 10,
timeType: null,
rescueStatus: null,
rescueType: null,
licenseNum: null,
rangeTime: [],
startTimeStr: null,
endTimeStr: null,
connectionName: null,
connectionPhone: null,
rescuePosition: null,
secondDispatchId: null,
driverId: null,
rescueDriver: null,
rescueCar: null,
driverCarNum: null,
faultType: null,
phenomenon: null,
channel: null,
channelId: null,
source: null
},
rescueStatusList: [{
text: "已接单",
value: "1"
},
{
text: "救援中",
value: "2"
},
{
text: "已完成",
value: "3"
}
],
sortList: [{
text: "正序",
value: "asc"
},
{
text: "倒序",
value: "desc"
}
],
// 分类统计相关数据
subTabs: [{
label: "救援司机",
value: "driver"
},
{
label: "救援车辆",
value: "vehicle"
},
{
label: "调度",
value: "dispatch"
},
],
driverList: [],
vehicleList: [],
dispatchList: [],
orderStats: {
received: 235,
rescuing: 152,
completed: 39,
},
// 调度人员信息
dispatcherColumns: [],
userinfo: {},
jyStatusOptions: [],
jyTypeOptions: [],
faultTypeOptions: [],
phenomenonOptions: [],
currentPickerType: '', // 当前选择器类型
dispatcherOptions: [], // 调度选项
rescueDriverOptions: [],
rescueCarOptions: [],
channelOptions: [],
sourceOptions: [],
countNum: {
jyzNum: 0,
dzfNum: 0,
dqcNum: 0,
ywcNum: 0,
zwxNum: 0,
yjdNum: 0,
yingskNum: 0,
yiskNum: 0,
dskNum: 0,
},
};
},
components: {
// OrderCardVue,
CategoryList,
lzcPicker,
},
onLoad(option) {
this.gindex = option.id || 1
this.getOptions()
this.getlist()
this.getBrandList()
this.getStaffList()
uni.getSystemInfo({
success: function(res) {
this.windowsHeight = res.windowHeight - 100;
console.log('屏幕高度:', res.windowHeight);
}
});
},
created() {
},
onReachBottom() {
console.log('触底加载');
if (this.pageNum >= this.totalPages) {
this.hasMore = false;
} else if (!this.loadingMore) {
this.loadingMore = true;
this.pageNum++;
this.getlist();
}
},
onPageScroll(e) {
this.scrollTop = e.scrollTop;
},
watch: {
range(newval) {
if (newval && newval.length === 2) {
this.startTimeStr = newval[0];
this.endTimeStr = newval[1];
this.pageNum = 1;
this.orderList = [];
this.hasMore = true;
this.initialLoading = true;
this.getlist();
}
},
'queryParams.rangeTime': {
handler(newval) {
if (newval && newval.length === 2) {
this.queryParams.startTimeStr = newval[0];
this.queryParams.endTimeStr = newval[1];
this.queryParams.timeType = 'more';
} else {
this.queryParams.startTimeStr = null;
this.queryParams.endTimeStr = null;
}
},
deep: true
}
},
computed: {
currentList() {
switch (this.activeSubTab) {
case "driver":
return this.driverList;
case "vehicle":
return this.vehicleList;
case "dispatch":
return this.dispatchList;
default:
return [];
}
},
filteredSubTabs() {
const tabs = [{
label: "救援司机",
value: "driver"
},
{
label: "救援车辆",
value: "vehicle"
}
];
if (this.hasRole('ddzx')) {
tabs.push({
label: "调度",
value: "dispatch"
});
}
return tabs;
},
},
methods: {
hasRole,
hasRoleNew,
switchTab(tab) {
this.activeTab = tab;
if (tab == 'category') {
this.switchSubTab('driver')
}
},
switchSubTab(tab) {
this.activeSubTab = tab;
let data = {}
if (tab == 'driver') {
data.queryType = 'driver'
this.getStatisticsList(data)
} else if (tab == 'vehicle') {
data.queryType = 'car'
this.getStatisticsList(data)
} else if (tab == 'dispatch') {
data.queryType = 'dispatch'
this.getStatisticsList(data)
}
console.log('切换到tab:', tab);
},
switchInfoTab(index) {
this.activeInfoTab = index;
console.log('切换到信息选项卡:', index);
},
toggleExpand() {
this.isExpanded = !this.isExpanded;
},
goBack() {
uni.navigateBack();
},
showPicker(type) {
this.currentPickerType = type;
// 根据类型选择对应的选择器引用
let pickerRef = '';
switch (type) {
case 'channel':
pickerRef = 'lzcPickerChannel';
break;
case 'source':
pickerRef = 'lzcPickerSource';
break;
case 'rescueStatus':
pickerRef = 'lzcPickerRescueStatus';
break;
case 'secondDispatchId':
pickerRef = 'lzcPickerDispatcher';
break;
case 'rescueType':
pickerRef = 'lzcPickerRescueType';
break;
case 'faultType':
pickerRef = 'lzcPickerFaultType';
break;
case 'phenomenon':
pickerRef = 'lzcPickerPhenomenon';
break;
case 'rescueCar':
pickerRef = 'lzcPickerRescueCar';
break;
case 'rescueDriver':
pickerRef = 'lzcPickerRescueDriver';
break;
default:
console.error('未知的选择器类型:', type);
return;
}
// 确保选择器引用存在
if (!this.$refs[pickerRef]) {
console.error(`选择器引用 ${pickerRef} 未找到`);
return;
}
// 如果选项为空,先加载数据
this.loadPickerOptions(type).then(() => {
this.$refs[pickerRef].handleShow();
}).catch(error => {
console.error('加载选项失败:', error);
});
},
async loadPickerOptions(type) {
switch (type) {
case 'channel':
if (this.channelOptions.length === 0) {
await this.loadChannelOptions();
}
break;
case 'source':
if (this.sourceOptions.length === 0) {
await this.loadSourceOptions();
}
break;
case 'rescueStatus':
if (this.jyStatusOptions.length === 0) {
await this.loadjyStatusOptions();
}
break;
case 'secondDispatchId':
if (this.dispatcherOptions.length === 0) {
await this.loadDispatcherOptions();
}
break;
case 'rescueType':
if (this.jyTypeOptions.length === 0) {
await this.loadjyTypeOptions();
}
break;
case 'faultType':
if (this.faultTypeOptions.length === 0) {
await this.loadFaultTypeOptions();
}
break;
case 'phenomenon':
if (this.phenomenonOptions.length === 0) {
await this.loadPhenomenonOptions();
}
break;
case 'rescueDriver':
if (this.rescueDriverOptions.length === 0) {
await this.loadRescueDriverOptions();
}
break;
case 'rescueCar':
if (this.rescueCarOptions.length === 0) {
await this.loadRescueCarOptions();
}
break;
}
},
// 选择器确认回调
onPickerConfirm(field, selectedItem) {
if (selectedItem && selectedItem.value) {
if (field == 'rescueDriver') {
this.queryParams.driverId = selectedItem.id
} else if (field == 'rescueCar') {
this.queryParams.driverCarNum = selectedItem.value
}else if(field == 'channel') {
this.queryParams.channelId = selectedItem.id
}
// 根据字段名设置对应的值
this.queryParams[field] = selectedItem.value;
// 强制更新视图
this.$forceUpdate();
console.log(`选择了 ${field}:`, selectedItem);
}
},
// 获取选中项的显示文本
getSelectedLabel(type, value) {
let options = [];
switch (type) {
case 'channel':
options = this.channelOptions;
break;
case 'source':
options = this.sourceOptions;
break;
case 'rescueStatus':
options = this.jyStatusOptions;
break;
case 'secondDispatchId':
options = this.dispatcherOptions;
break;
case 'rescueType':
options = this.jyTypeOptions;
break;
case 'faultType':
options = this.faultTypeOptions;
break;
case 'phenomenon':
options = this.phenomenonOptions;
break;
case 'rescueDriver':
options = this.rescueDriverOptions;
break;
case 'rescueCar':
options = this.rescueCarOptions;
break;
default:
options = [];
}
const selected = options.find(item => item.value === value);
return selected ? selected.nickname : '请选择';
},
openFilter() {
uni.showToast({
title: "筛选功能待实现",
icon: "none",
});
},
// 选项卡选择
tabChoose(item, index) {
this.tabValue = index + 1;
if (1 == this.tabValue) {
// 全部 - 清除筛选条件
this.clearSelection();
this.refreshList();
} else if (2 == this.tabValue) {
// 筛选 - 打开弹窗
this.isShowPop = true;
}
},
maskClick(e) {
console.log('maskClick事件:', e);
},
// 处理搜索
handleSearch() {
this.searchKeyword = this.searchText.trim();
this.pageNum = 1;
this.orderList = [];
this.getlist();
},
getBrandList() {
let data = {
pageSize: 1000,
pageNum: 1,
}
request({
url: '/base/carBrand/page',
method: 'get',
params: data
}).then((res) => {
console.log('list', res);
if (res.code == 200) {
this.brandList = res.data.records
}
})
},
getStaffList() {
let data = {
pageSize: 1000,
pageNum: 1,
}
request({
url: '/company/staff/list',
method: 'get',
params: data
}).then((res) => {
console.log('list', res);
if (res.code == 200) {
this.staffList = res.data
}
})
},
getlist() {
console.log('queryParams', this.queryParams)
request({
url: '/app/rescueInfo/getRescueStatisticsInfoList',
method: 'get',
params: this.queryParams
}).then((res) => {
console.log('list', res);
if (res.code == 200) {
this.getCountNum()
if (this.pageNum != 1) {
const currentScrollTop = this.scrollTop;
this.orderList = this.orderList.concat(res.rows)
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: currentScrollTop,
duration: 0
});
});
} else {
this.orderList = res.rows
}
this.totalNum = res.total
let total = res.total
this.totalPages = Math.ceil(total / this.pageSize);
this.hasMore = this.pageNum < this.totalPages;
}
}).catch((error) => {
console.error('获取数据失败', error);
uni.showToast({
title: '数据加载失败',
icon: 'none'
});
}).finally(() => {
this.initialLoading = false;
this.loadingMore = false;
});
},
// 刷新列表
refreshList() {
this.pageNum = 1;
this.orderList = [];
this.hasMore = true;
this.initialLoading = true;
this.getlist();
},
// 关闭弹窗
closePop() {
this.isShowPop = false;
document.body.style.overflow = ''
uni.pageScrollTo({
scrollTop: 0,
duration: 0
});
},
// 提交筛选
submitPop() {
this.refreshList();
this.isShowPop = false;
document.body.style.overflow = '';
// 对于uni-app可能需要使用uni API
uni.pageScrollTo({
scrollTop: 0,
duration: 0
});
},
// 打开弹窗
openPop() {
this.isShowPop = true;
document.body.style.overflow = 'hidden'
},
// 清除筛选条件
clearSelection() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
timeType: null,
rescueStatus: null,
rescueType: null,
licenseNum: null,
rangeTime: [],
startTimeStr: null,
endTimeStr: null,
connectionName: null,
connectionPhone: null,
rescuePosition: null,
secondDispatchId: null,
rescueCar: null,
rescueDriver: null,
driverId: null,
driverCarNum: null,
faultType: null,
phenomenon: null,
channel: null,
source: null
};
this.searchText = '';
this.searchKeyword = null;
this.$forceUpdate();
},
getStatisticsList(data) {
let url = '';
if (this.hasRole('ddzx')) {
url = '/app/rescueInfo/statisticsAll';
} else if (this.hasRole('second_dispatcher')) {
url = '/app/rescueInfo/statisticsAllSecond';
} else {
this.driverList = [];
this.vehicleList = [];
return;
}
request({
url: url,
method: 'get',
params: data
}).then((res) => {
console.log('list', res);
if (res.code == 200) {
const processedData = res.data.map(item => {
if (item.money !== undefined && item.money !== null) {
return {
...item,
money: Math.trunc(item.money / 100),
mileage: Math.trunc(item.mileage)
};
}
return item;
});
switch (data.queryType) {
case 'driver':
this.driverList = processedData;
break;
case 'car':
this.vehicleList = processedData;
break;
case 'dispatch':
this.dispatchList = processedData;
break;
}
}
})
},
// 获取所有渠道信息
async loadChannelOptions() {
return new Promise((resolve, reject) => {
request({
url: '/rescue-channel-source/channelList',
method: 'get',
}).then((res) => {
this.channelOptions = res.data.map(item => ({
nickname: item.name, // 显示文本
value: item.name, // 实际值
id: item.id, // ID
label: item.name // 保留原字段
}));
resolve();
}).catch(reject);
});
},
// 获取所有来源信息
async loadSourceOptions() {
return new Promise((resolve, reject) => {
request({
url: '/rescue-channel-source/sourceList',
method: 'get',
}).then((res) => {
this.sourceOptions = res.data.map(item => ({
nickname: item.name, // 显示文本
value: item.name, // 实际值
id: item.id, // ID
label: item.name // 保留原字段
}));
resolve();
}).catch(reject);
});
},
// 获取调度人员信息
loadDispatcherOptions() {
return new Promise((resolve, reject) => {
this.dispatcherColumns = [];
const tenantId = this.userinfo.tenantId + '';
const request1 = request({
url: `/company/staff/staffListByRoleCode?tenantId=${tenantId}&code=second_dispatcher`,
method: 'get',
});
const request2 = request({
url: `/company/staff/staffListByRoleCode?tenantId=${tenantId}&code=ddzx`,
method: 'get',
});
Promise.all([request1, request2])
.then(([res1, res2]) => {
const combinedData = [...(res1.data || []), ...(res2.data || [])];
const uniqueMap = new Map();
combinedData.forEach(item => {
if (item && item.id) {
uniqueMap.set(item.id, item);
}
});
const uniqueData = Array.from(uniqueMap.values());
this.dispatcherColumns.push(uniqueData);
this.dispatcherOptions = this.dispatcherColumns[0].map(item => ({
nickname: item.name || item.nickname,
value: item.id,
id: item.id,
name: item.id // 存储实际值
}));
console.log('this.dispatcherColumns', this.dispatcherColumns)
console.log('this.dispatcherOptions', this.dispatcherOptions)
resolve(uniqueData);
})
.catch(error => {
console.error('获取调度人员数据失败:', error);
reject(error);
});
});
},
// 获取救援状态 字典
async loadjyStatusOptions() {
return new Promise((resolve, reject) => {
request({
url: '/rescue/dict/data/type/jy_status',
method: 'get',
}).then((res) => {
this.jyStatusOptions = res.data.map(item => ({
nickname: item.label,
value: item.value,
id: item.id,
name: item.value // 存储实际值
}));
resolve();
}).catch(reject);
});
},
// 获取救援类型 字典
async loadjyTypeOptions() {
return new Promise((resolve, reject) => {
request({
url: '/rescue/dict/data/type/dljy_type',
method: 'get',
}).then((res) => {
this.jyTypeOptions = res.data.map(item => ({
nickname: item.label, // 显示文本
value: item.value, // 实际值
id: item.id, // ID
label: item.label // 保留原字段
}));
resolve();
}).catch(reject);
});
},
// 加载故障类型选项
async loadFaultTypeOptions() {
return new Promise((resolve, reject) => {
request({
url: '/rescue-type-phenomenon/typeList',
method: 'get',
}).then((res) => {
this.faultTypeOptions = res.data.map(item => ({
nickname: item.name, // 显示文本
value: item.name, // 实际值
id: item.id, // ID
label: item.name // 保留原字段
}));
resolve();
}).catch(reject);
});
},
// 加载故障现象选项方法
async loadPhenomenonOptions() {
return new Promise((resolve, reject) => {
request({
url: '/rescue-type-phenomenon/listPhenomenonByPid',
method: 'get',
}).then((res) => {
this.phenomenonOptions = res.data.map(item => ({
nickname: item.name, // 显示文本
value: item.name, // 实际值
id: item.id, // ID
label: item.name // 保留原字段
}));
resolve();
}).catch(reject);
});
},
// 加载司机选项方法
async loadRescueDriverOptions() {
return new Promise((resolve, reject) => {
let url = '';
if (this.hasRole('ddzx')) {
url = '/system/DriverInfo/listDriverInfo';
} else if (this.hasRole('second_dispatcher')) {
url = '/system/DriverInfo/listDriverInfoSecond';
} else {
this.rescueDriverOptions = [];
resolve();
return;
}
request({
url: url,
method: 'get',
}).then((res) => {
this.rescueDriverOptions = res.data.map(item => ({
nickname: item.nickName,
value: item.userId,
id: item.id,
label: item.nickName
}));
resolve();
}).catch(reject);
});
},
// 加载救援车辆选项方法
async loadRescueCarOptions() {
return new Promise((resolve, reject) => {
let url = '';
if (this.hasRole('ddzx')) {
url = '/system/DriverInfo/listCarInfo';
} else if (this.hasRole('second_dispatcher')) {
url = '/system/DriverInfo/listCarInfoSecond';
} else {
this.rescueCarOptions = [];
resolve();
return;
}
request({
url: url,
method: 'get',
}).then((res) => {
this.rescueCarOptions = res.data.map(item => ({
nickname: item.rescueCarNum, // 显示文本
value: item.rescueCarNum,
id: item.id,
label: item.rescueCarNum
}));
resolve();
}).catch(reject);
});
},
async getOptions() {
this.userinfo = uni.getStorageSync('userInfo')
// await this.getDispatcher()
await this.loadDispatcherOptions() // 新增
await this.loadjyStatusOptions()
await this.loadjyTypeOptions()
await this.loadFaultTypeOptions()
await this.loadPhenomenonOptions()
await this.loadRescueDriverOptions()
await this.loadRescueCarOptions()
await this.loadChannelOptions()
},
getCountNum() {
request({
url: '/app/rescueInfo/getRescueStatisticsInfoNum',
method: 'get',
params: this.queryParams
}).then((res) => {
console.log('四个', res);
this.countNum.dqcNum = res.data.dqcNum
this.countNum.dzfNum = res.data.dzfNum
this.countNum.jyzNum = res.data.jyzNum
this.countNum.ywcNum = res.data.ywcNum
this.countNum.yjdNum = res.data.yjdNum
this.countNum.zwxNum = res.data.zwxNum
this.countNum.yingskNum = res.data.yingskNum ? res.data.yingskNum / 100 : 0
this.countNum.yiskNum = res.data.yiskNum ? res.data.yiskNum / 100 : 0
this.countNum.dskNum = res.data.dskNum ? res.data.dskNum / 100 : 0
})
},
}
};
</script>
<style scoped>
/* 原有样式保持不变,只添加弹窗相关样式 */
.popup-content {
padding: 20rpx 20rpx 20rpx 20rpx;
overflow-y: scroll;
background: white;
border-radius: 20rpx;
}
.popup-header {
font-size: 32rpx;
color: #101A3E;
margin-bottom: 40rpx;
display: flex;
justify-content: space-between;
}
.popup-body {
font-size: 28rpx;
color: #101A3E;
}
.filter-section {
margin-top: 30rpx;
}
.filter-section text {
display: block;
margin-bottom: 10rpx;
}
.options {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.popup-footer {
display: flex;
justify-content: space-between;
margin-top: 60rpx;
margin-bottom: 30rpx;
}
.popup-footer button {
padding: 10rpx 20rpx;
margin: 0 10rpx;
border-radius: 5px;
background-color: #327DFB;
color: #fff;
border: none;
}
/* 搜索和时间筛选区域 */
/* .search-filter-area {
width: 100%;
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
padding: 6rpx 0 10rpx 0;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
} */
.tab-icon {
width: 66rpx;
height: 8rpx;
margin-top: 8rpx;
}
.statistics-container {
background: #f6f8fc;
min-height: 100vh;
}
.top1 {
width: 100%;
height: 100rpx;
background: #3a8dff;
}
/* 顶部导航 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(180deg, #3a8dff, #579dff);
color: #fff;
padding: 10px 16px;
height: 100rpx;
box-sizing: border-box;
}
.title-tabs {
display: flex;
flex: 1;
justify-content: center;
}
.tab-item {
font-size: 16px;
margin: 0 15px;
padding-bottom: 4px;
opacity: 0.8;
}
.tab-item.active {
font-weight: bold;
border-bottom: 3px solid #fff;
opacity: 1;
}
.filter-btn {
font-size: 14px;
}
/* 统计卡片 */
/* .stat-cards {
display: flex;
justify-content: space-around;
margin: 14rpx 0;
} */
.stat-card {
background: #fff;
border-radius: 12rpx;
padding: 4rpx;
flex: 1;
margin: 0 16rpx;
text-align: center;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.05);
}
.stat-num {
font-size: 20px;
font-weight: bold;
color: #333;
}
.stat-label {
display: block;
margin-top: 6px;
color: #666;
font-size: 14px;
}
/* 搜索和时间筛选区域 */
.search-filter-area {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
/* padding: 6rpx 0 10rpx 0; */
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.top-icon {
box-sizing: border-box;
width: 100%;
padding: 5px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.h-text {
width: 124rpx;
height: 56rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 28rpx;
font-size: 28rpx;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
background: #327DFB;
}
.s-input {
width: 78%;
height: 56rpx;
background: #f5f5f5;
border-radius: 50px;
box-sizing: border-box;
display: flex;
align-items: center;
padding: 0px 10px;
input {
margin-left: 5px;
flex: 1;
width: 0;
font-size: 14px;
color: #999999;
}
}
/* 时间筛选选项卡样式 */
.tab-choose-class {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0 16rpx 0;
background-color: #fff;
/* border-bottom: 1rpx solid #F5F5F5; */
width: 100%;
/* 新增:让容器占满宽度 */
}
.tab-content {
font-size: 28rpx;
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
text-align: center;
}
.tab-name-class {
display: flex;
align-items: center;
}
.icon-tab {
display: flex;
align-items: center;
justify-content: center;
width: 32rpx;
height: 32rpx;
}
.icon-tab image {
width: 100%;
height: 100%;
}
.example-body {
background-color: #fff;
padding: 0 20rpx;
}
/* 信息选项卡样式 */
.four-box-header_2 {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
}
.four-box-header_2_right {
width: 60%;
display: flex;
justify-content: space-around;
align-items: center;
}
.four-box-header_2_right .tab-item {
font-size: 28rpx;
font-weight: normal;
color: #a1a6aa;
position: relative;
padding: 0rpx 20rpx;
cursor: pointer;
transition: all 0.3s ease;
}
.four-box-header_2_right .tab-item.active {
font-weight: bold;
color: #000000;
}
.four-box-header_2_right .tab-item.active::after {
content: '';
position: absolute;
bottom: -10rpx;
left: 50%;
transform: translateX(-50%);
width: 33.33%;
height: 6rpx;
background: linear-gradient(90deg, #054DF3 0%, #55A3FF 100%);
border-radius: 3rpx 3rpx 0 0;
}
/* 订单列表区域 */
.dil {
background-color: #F7F8FC;
box-sizing: border-box;
padding: 8px 12px 15px 12px;
display: flex;
flex-direction: column;
row-gap: 10px;
}
.loading-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loading-spinner {
position: relative;
width: 60px;
height: 60px;
border: 4px solid #f3f3f3;
border-top: 4px solid #327DFB;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 15px;
display: flex;
justify-content: center;
align-items: center;
}
.loading-text {
font-size: 16px;
color: #666;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.ques {
margin: 0px auto;
margin-top: 40px;
width: 80%;
}
.ques image {
width: 100%;
}
.loading-more {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx;
color: #999;
font-size: 26rpx;
}
.loading-more .mini {
width: 30px;
height: 30px;
border-width: 2px;
margin-right: 10rpx;
}
.no-more-data {
text-align: center;
padding: 20rpx;
color: #999;
font-size: 26rpx;
}
/* 分类统计样式 */
.sub-tabs {
display: flex;
background: #fff;
padding: 10px 0;
justify-content: space-around;
border-bottom: 1px solid #eee;
}
.sub-tab-item {
font-size: 15px;
color: #666;
padding: 4px 10px;
}
.sub-tab-item.active {
font-weight: bold;
color: #3a8dff;
border-bottom: 2px solid #3a8dff;
}
.list-container {
margin: 10px;
}
.table-header,
.table-row {
display: flex;
align-items: center;
background: #fff;
border-radius: 8px;
padding: 10px;
margin-bottom: 6px;
}
.table-header {
font-weight: bold;
background: #f0f3f8;
}
.col-rank {
width: 40px;
text-align: center;
}
.col-driver {
flex: 1;
display: flex;
align-items: center;
}
.avatar {
width: 28px;
height: 28px;
border-radius: 50%;
margin-right: 6px;
}
.col-num,
.col-distance,
.col-money {
width: 80px;
text-align: center;
}
.scroll_view_style {
flex: 1;
}
/* 新增选择字段样式 */
.select-field {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background: #fff;
border-radius: 8rpx;
border: 1rpx solid #e9ecef;
min-height: 80rpx;
box-sizing: border-box;
}
.select-text {
font-size: 28rpx;
color: #333;
flex: 1;
}
.select-field:active {
background: #e9ecef;
}
.stat-cards-container {
position: relative;
display: flex;
align-items: center;
margin: 14rpx 0;
}
.stat-cards {
display: flex;
justify-content: space-around;
flex: 1;
}
.expand-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 60rpx;
height: 80rpx;
margin-right: 20rpx;
background: #fff;
border-radius: 12rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.05);
}
.expand-text {
font-size: 20rpx;
color: #327DFB;
margin-top: 4rpx;
}
.expanded-cards {
margin-top: 10rpx;
margin-bottom: 20rpx;
}
</style>