初步完成可视化页面
parent
050d4f9fcf
commit
510fca9b8e
273
cs_chart.html
273
cs_chart.html
|
|
@ -720,6 +720,54 @@
|
|||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #9e9e9e;
|
||||
}
|
||||
|
||||
/* 司机搜索下拉框样式 */
|
||||
.driver-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.driver-item {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.driver-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.driver-item:hover {
|
||||
background-color: var(--light-green);
|
||||
}
|
||||
|
||||
.driver-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.driver-name {
|
||||
font-weight: 600;
|
||||
color: var(--dark-green);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.driver-phone {
|
||||
color: #666;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -756,8 +804,17 @@
|
|||
<input type="text" id="order-number" v-model="searchParams.orderNumber" placeholder="订单号">
|
||||
</div>
|
||||
|
||||
<div class="search-field" style="min-width: 180px;">
|
||||
<input type="text" id="fuzzy-search" v-model="searchParams.fuzzySearch" placeholder="输入司机姓名">
|
||||
<div class="search-field" style="min-width: 180px; position: relative;">
|
||||
<input type="text" id="fuzzy-search" v-model="searchParams.fuzzySearch" @input="handleDriverSearch" placeholder="输入司机姓名">
|
||||
<div v-if="uiState.showDriverList && driverSearchResults.length > 0" class="driver-dropdown">
|
||||
<div v-for="driver in driverSearchResults" :key="driver.uid"
|
||||
class="driver-item" @click="selectDriver(driver)">
|
||||
<div class="driver-info">
|
||||
<span class="driver-name">{{ driver.realname }}</span>
|
||||
<span class="driver-phone">{{ driver.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="date-range">
|
||||
|
|
@ -826,13 +883,13 @@
|
|||
|
||||
<!-- 归属客服下拉框 -->
|
||||
<div style="display: flex; flex-direction: column; min-width: 220px; flex-shrink: 0;">
|
||||
<select style="padding: 7px 9px; border: 1px solid var(--border-color); border-radius: 3px; font-size: 0.85rem; height: 32px; margin-bottom: 6px;" v-model="searchParams.customerService">
|
||||
<select style="padding: 7px 9px; border: 1px solid var(--border-color); border-radius: 3px; font-size: 0.85rem; height: 32px; margin-bottom: 6px;" v-model="searchParams.belongKefu">
|
||||
<option value="">全部客服</option>
|
||||
<option v-for="item in staffOptions.customers" :key="item.id" :value="item.id">
|
||||
{{ item.name }}
|
||||
</option>
|
||||
</select>
|
||||
<select style="padding: 7px 9px; border: 1px solid var(--border-color); border-radius: 3px; font-size: 0.85rem; height: 32px;" v-model="searchParams.salesman">
|
||||
<select style="padding: 7px 9px; border: 1px solid var(--border-color); border-radius: 3px; font-size: 0.85rem; height: 32px;" v-model="searchParams.belongSale">
|
||||
<option value="">全部业务员</option>
|
||||
<option v-for="item in staffOptions.salesmen" :key="item.id" :value="item.id">
|
||||
{{ item.name }}
|
||||
|
|
@ -1057,8 +1114,9 @@
|
|||
timeType: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
customerService: '',
|
||||
salesman: ''
|
||||
belongKefu: '',
|
||||
belongSale: '',
|
||||
driverUid: ''
|
||||
});
|
||||
|
||||
// 界面状态
|
||||
|
|
@ -1066,6 +1124,7 @@
|
|||
showQueryBar: false,
|
||||
showKpiBar: false,
|
||||
isLoading: false,
|
||||
showDriverList: false,
|
||||
modals: {
|
||||
chat: false,
|
||||
review: false,
|
||||
|
|
@ -1084,11 +1143,15 @@
|
|||
salesmen: []
|
||||
});
|
||||
|
||||
// 司机搜索结果
|
||||
const driverSearchResults = ref([]);
|
||||
let driverSearchTimer = null;
|
||||
|
||||
const kpiList = ref([
|
||||
{ value: '-', label: '司机数', isDriver: true },
|
||||
{ value: '156', label: '续费数' },
|
||||
{ value: '45', label: '流失数' },
|
||||
{ value: '987', subValue: '98.7%', label: '接单数/率' },
|
||||
{ value: '-', subValue: '-', label: '接单数/率' },
|
||||
{ value: '2,541', subValue: '95.2%', label: '订单数/率' },
|
||||
{ value: '56', subValue: '2.2%', label: '不符数/率' },
|
||||
{ value: '78', subValue: '3.1%', label: '超时数/率' },
|
||||
|
|
@ -1156,6 +1219,14 @@
|
|||
|
||||
// 获取初始数据
|
||||
fetchInitialData();
|
||||
|
||||
// 点击页面其他区域关闭司机下拉列表
|
||||
document.addEventListener('click', (e) => {
|
||||
const fuzzySearch = document.getElementById('fuzzy-search');
|
||||
if (fuzzySearch && !fuzzySearch.contains(e.target)) {
|
||||
uiState.showDriverList = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const normalizeProvinces = (list) => {
|
||||
|
|
@ -1261,9 +1332,14 @@
|
|||
if (Array.isArray(d.province)) {
|
||||
areaList.provinces = normalizeProvinces(d.province);
|
||||
}
|
||||
if (d.salerss) {
|
||||
// 填充客服列表
|
||||
if (d.customers) {
|
||||
staffOptions.customers = normalizeStaffMap(d.customers);
|
||||
} else if (d.salerss) {
|
||||
staffOptions.customers = normalizeStaffMap(d.salerss);
|
||||
}
|
||||
|
||||
// 填充业务员列表
|
||||
if (d.salers) {
|
||||
staffOptions.salesmen = normalizeStaffMap(d.salers);
|
||||
}
|
||||
|
|
@ -1331,6 +1407,12 @@
|
|||
|
||||
// 获取在线沟通数据
|
||||
await fetchOnlineChatData();
|
||||
|
||||
// 默认选中待回访按钮
|
||||
const revisitTask = taskList.value.find(task => task.type === 'revisit');
|
||||
if (revisitTask) {
|
||||
handleTaskClick(revisitTask);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching initial data:', error);
|
||||
}
|
||||
|
|
@ -1387,12 +1469,126 @@
|
|||
uiState.isLoading = true;
|
||||
console.log('正在搜索数据...');
|
||||
|
||||
// 重新请求接口数据
|
||||
await fetchInitialData();
|
||||
// 构建请求参数
|
||||
const params = {
|
||||
time_start: searchParams.startDate,
|
||||
time_end: searchParams.endDate
|
||||
};
|
||||
|
||||
// 添加省市区参数
|
||||
if (searchParams.province) {
|
||||
params.province = searchParams.province;
|
||||
}
|
||||
if (searchParams.city) {
|
||||
params.city = searchParams.city;
|
||||
}
|
||||
if (searchParams.district) {
|
||||
params.county = searchParams.district;
|
||||
}
|
||||
|
||||
// 添加其他参数
|
||||
if (searchParams.belongKefu) {
|
||||
params.belong_kefu = searchParams.belongKefu;
|
||||
}
|
||||
if (searchParams.belongSale) {
|
||||
params.belong_sale = searchParams.belongSale;
|
||||
}
|
||||
if (searchParams.driverUid) {
|
||||
params.driver_uid = searchParams.driverUid;
|
||||
}
|
||||
if (searchParams.timeType) {
|
||||
params.apptimes = searchParams.timeType;
|
||||
}
|
||||
|
||||
// 清空当前选中的任务类型和案件列表
|
||||
activeTaskType.value = '';
|
||||
caseList.value = [];
|
||||
console.log('搜索参数:', params);
|
||||
|
||||
const res = await axios.get(BASE_URL + 'third-api/cs-chart-info', {
|
||||
params
|
||||
});
|
||||
|
||||
if (res.data && res.data.code === 200 && res.data.data) {
|
||||
const d = res.data.data;
|
||||
|
||||
// 更新KPI数据
|
||||
if (kpiList.value.length > 0 && typeof d.driverNum !== 'undefined') {
|
||||
kpiList.value[0].value = String(d.driverNum);
|
||||
fetchFollowUpCount(d.driverNum);
|
||||
}
|
||||
if (kpiList.value.length > 1 && typeof d.subscribeNum !== 'undefined') {
|
||||
kpiList.value[1].value = String(d.subscribeNum);
|
||||
}
|
||||
if (kpiList.value.length > 2 && typeof d.lostNum !== 'undefined') {
|
||||
kpiList.value[2].value = String(d.lostNum);
|
||||
}
|
||||
// Update '接单数/率'
|
||||
if (kpiList.value.length > 3 && typeof d.driverAcceptNum !== 'undefined') {
|
||||
kpiList.value[3].value = String(d.driverAcceptNum);
|
||||
}
|
||||
if (kpiList.value.length > 3 && typeof d.driverAcceptPer !== 'undefined') {
|
||||
kpiList.value[3].subValue = formatPercent(d.driverAcceptPer);
|
||||
}
|
||||
if (kpiList.value.length > 4 && typeof d.orderNum !== 'undefined') {
|
||||
kpiList.value[4].value = String(d.orderNum);
|
||||
}
|
||||
if (kpiList.value.length > 4 && typeof d.orderPer !== 'undefined') {
|
||||
kpiList.value[4].subValue = formatPercent(d.orderPer);
|
||||
}
|
||||
if (kpiList.value.length > 5 && typeof d.reconsiderNum !== 'undefined') {
|
||||
kpiList.value[5].value = String(d.reconsiderNum);
|
||||
}
|
||||
if (kpiList.value.length > 5 && typeof d.reconsiderPer !== 'undefined') {
|
||||
kpiList.value[5].subValue = formatPercent(d.reconsiderPer);
|
||||
}
|
||||
if (kpiList.value.length > 6 && typeof d.overNum !== 'undefined') {
|
||||
kpiList.value[6].value = String(d.overNum);
|
||||
}
|
||||
if (kpiList.value.length > 6 && typeof d.overPer !== 'undefined') {
|
||||
kpiList.value[6].subValue = formatPercent(d.overPer);
|
||||
}
|
||||
if (kpiList.value.length > 8 && typeof d.satisfiedNum !== 'undefined') {
|
||||
kpiList.value[8].value = String(d.satisfiedNum);
|
||||
}
|
||||
if (kpiList.value.length > 8 && typeof d.satisfiedPer !== 'undefined') {
|
||||
kpiList.value[8].subValue = formatPercent(d.satisfiedPer);
|
||||
}
|
||||
if (kpiList.value.length > 9 && typeof d.complainNum !== 'undefined') {
|
||||
kpiList.value[9].value = String(d.complainNum);
|
||||
}
|
||||
if (kpiList.value.length > 9 && typeof d.complainPer !== 'undefined') {
|
||||
kpiList.value[9].subValue = formatPercent(d.complainPer);
|
||||
}
|
||||
|
||||
// 更新任务栏数据
|
||||
const taskMap = {
|
||||
'revisit': { numKey: 'revisitNum', listKey: 'revisitList' },
|
||||
'reconsider': { numKey: 'reconsiderNum', listKey: 'reconsiderList' },
|
||||
'over': { numKey: 'overNum', listKey: 'overList' },
|
||||
'complain': { numKey: 'complainNum', listKey: 'complainList' }
|
||||
};
|
||||
|
||||
taskList.value.forEach(task => {
|
||||
if (taskMap[task.type]) {
|
||||
const { numKey, listKey } = taskMap[task.type];
|
||||
if (typeof d[numKey] !== 'undefined') {
|
||||
task.count = d[numKey];
|
||||
}
|
||||
if (Array.isArray(d[listKey])) {
|
||||
task.listData = d[listKey];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 更新在线沟通数据
|
||||
await fetchOnlineChatData();
|
||||
|
||||
// 如果当前有选中的任务类型,刷新对应列表
|
||||
if (activeTaskType.value) {
|
||||
const currentTask = taskList.value.find(task => task.type === activeTaskType.value);
|
||||
if (currentTask) {
|
||||
handleTaskClick(currentTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('数据刷新成功');
|
||||
} catch (error) {
|
||||
|
|
@ -1519,6 +1715,52 @@
|
|||
currentInput.complaintNotes = '';
|
||||
};
|
||||
|
||||
// 司机搜索功能
|
||||
const handleDriverSearch = () => {
|
||||
const keyword = searchParams.fuzzySearch.trim();
|
||||
|
||||
// 清除之前的定时器
|
||||
if (driverSearchTimer) {
|
||||
clearTimeout(driverSearchTimer);
|
||||
}
|
||||
|
||||
if (!keyword) {
|
||||
uiState.showDriverList = false;
|
||||
driverSearchResults.value = [];
|
||||
searchParams.driverUid = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 防抖处理,延迟300ms后发送请求
|
||||
driverSearchTimer = setTimeout(async () => {
|
||||
try {
|
||||
const res = await axios.get(BASE_URL + 'third-api/search-driver', {
|
||||
params: { keyword }
|
||||
});
|
||||
|
||||
if (res.data && res.data.code === 200 && Array.isArray(res.data.data)) {
|
||||
driverSearchResults.value = res.data.data;
|
||||
uiState.showDriverList = driverSearchResults.value.length > 0;
|
||||
} else {
|
||||
driverSearchResults.value = [];
|
||||
uiState.showDriverList = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('司机搜索失败:', error);
|
||||
driverSearchResults.value = [];
|
||||
uiState.showDriverList = false;
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// 选择司机
|
||||
const selectDriver = (driver) => {
|
||||
searchParams.fuzzySearch = driver.realname;
|
||||
searchParams.driverUid = driver.uid;
|
||||
uiState.showDriverList = false;
|
||||
driverSearchResults.value = [];
|
||||
};
|
||||
|
||||
return {
|
||||
searchParams,
|
||||
uiState,
|
||||
|
|
@ -1533,6 +1775,7 @@
|
|||
currentInput,
|
||||
chatMessages,
|
||||
chatBoxRef,
|
||||
driverSearchResults,
|
||||
handleProvinceChange,
|
||||
handleCityChange,
|
||||
handleSearch,
|
||||
|
|
@ -1546,7 +1789,9 @@
|
|||
closeModal,
|
||||
sendMessage,
|
||||
submitReview,
|
||||
submitComplaint
|
||||
submitComplaint,
|
||||
handleDriverSearch,
|
||||
selectDriver
|
||||
};
|
||||
}
|
||||
}).mount('#app');
|
||||
|
|
|
|||
Loading…
Reference in New Issue