feat(数据表格): 实现详细数据表格的筛选和排序功能
- 添加筛选器变化事件处理,支持按中心领导、团队领导和组长筛选数据 - 修改表格排序逻辑以适配新数据结构 - 更新表格显示字段以匹配后端返回的数据格式 - 移除不再需要的格式化函数
This commit is contained in:
@@ -26,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label>经理:</label>
|
<label>经理:</label>
|
||||||
<select v-model="filters.manager" :disabled="!filters.advancedManager">
|
<select v-model="filters.manager" @change="onManagerChange" :disabled="!filters.advancedManager">
|
||||||
<option value="">全部经理</option>
|
<option value="">全部经理</option>
|
||||||
<option v-for="manager in availableManagers" :key="manager.name" :value="manager.name">
|
<option v-for="manager in availableManagers" :key="manager.name" :value="manager.name">
|
||||||
{{ manager.name }}
|
{{ manager.name }}
|
||||||
@@ -41,34 +41,36 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>人员</th>
|
<th>人员</th>
|
||||||
<th @click="sortBy('dealRate')" class="sortable">成交率 <span class="sort-icon" :class="{ active: sortField === 'dealRate' }">{{ sortOrder === 'desc' ? '↓' : '↑' }}</span></th>
|
<th @click="sortBy('conversion_rate')" class="sortable">成交率 <span class="sort-icon" :class="{ active: sortField === 'conversion_rate' }">{{ sortOrder === 'desc' ? '↓' : '↑' }}</span></th>
|
||||||
<th @click="sortBy('callDuration')" class="sortable">成交单数 <span class="sort-icon" :class="{ active: sortField === 'callDuration' }">{{ sortOrder === 'desc' ? '↓' : '↑' }}</span></th>
|
<th @click="sortBy('total_deals')" class="sortable">成交单数 <span class="sort-icon" :class="{ active: sortField === 'total_deals' }">{{ sortOrder === 'desc' ? '↓' : '↑' }}</span></th>
|
||||||
<th>加微率</th>
|
<th>加微率</th>
|
||||||
<th>入群率</th>
|
<th>入群率</th>
|
||||||
<th>表单填写率</th>
|
<th>表单填写率</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="person in filteredTableData" :key="person.id" @click="$emit('update:selectedPerson', person)" :class="{ selected: selectedPerson && selectedPerson.id === person.id }">
|
<tr v-for="(person, index) in displayTableData" :key="index" @click="$emit('update:selectedPerson', person)" :class="{ selected: selectedPerson && selectedPerson.leader_name === person.leader_name }">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<div class="person-info">
|
<div class="person-info">
|
||||||
<div class="person-avatar">{{ person.name.charAt(0) }}</div>
|
<div class="person-avatar">{{ person.leader_name?.charAt(0) }}</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="person-name">{{ person.name }}</div>
|
<div class="person-name">{{ person.leader_name}}</div>
|
||||||
<div class="person-position">{{ person.position }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="deal-rate">
|
<div class="deal-rate">
|
||||||
<span class="rate-value" :class="getRateClass(person.dealRate)">{{ person.dealRate }}%</span>
|
<span class="rate-value" :class="getRateClass(parseFloat(person.conversion_rate))">{{ person.conversion_rate }}</span>
|
||||||
<div class="rate-bar"><div class="rate-fill" :style="{ width: person.dealRate + '%', backgroundColor: getRateColor(person.dealRate) }"></div></div>
|
<div class="rate-bar"><div class="rate-fill" :style="{ width: parseFloat(person.conversion_rate) + '%', backgroundColor: getRateColor(parseFloat(person.conversion_rate)) }"></div></div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ formatDuration(person.callDuration) }}</td>
|
<td>{{ person.total_deals }}</td>
|
||||||
<td>{{ person.callCount }}次</td>
|
<td>{{ person.plus_v_rate }}</td>
|
||||||
<td>¥{{ person.dealAmount.toLocaleString() }}</td>
|
<td>{{ person.group_rate }}</td>
|
||||||
<td>{{ person.department }}</td>
|
<td>{{ person.form_filling_rate }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -85,10 +87,10 @@ const props = defineProps({
|
|||||||
selectedPerson: { type: Object, default: null },
|
selectedPerson: { type: Object, default: null },
|
||||||
levelTree: { type: Object, default: () => ({}) }
|
levelTree: { type: Object, default: () => ({}) }
|
||||||
});
|
});
|
||||||
defineEmits(['update:selectedPerson']);
|
const emit = defineEmits(['update:selectedPerson', 'filter-change']);
|
||||||
|
|
||||||
const filters = ref({ centerLeader: '', advancedManager: '', manager: '', dealStatus: '' });
|
const filters = ref({ centerLeader: '', advancedManager: '', manager: '', dealStatus: '' });
|
||||||
const sortField = ref('dealRate');
|
const sortField = ref('conversion_rate');
|
||||||
const sortOrder = ref('desc');
|
const sortOrder = ref('desc');
|
||||||
|
|
||||||
const centerLeaders = computed(() => {
|
const centerLeaders = computed(() => {
|
||||||
@@ -107,13 +109,22 @@ const centerLeaders = computed(() => {
|
|||||||
return manager ? manager.managers || [] : [];
|
return manager ? manager.managers || [] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredTableData = computed(() => {
|
// 显示表格数据(直接使用API返回的数据)
|
||||||
let filtered = props.tableData;
|
const displayTableData = computed(() => {
|
||||||
if (filters.value.centerLeader) filtered = filtered.filter(item => item.centerLeader === filters.value.centerLeader);
|
if (!props.tableData || !Array.isArray(props.tableData)) return [];
|
||||||
if (filters.value.advancedManager) filtered = filtered.filter(item => item.advancedManager === filters.value.advancedManager);
|
|
||||||
if (filters.value.manager) filtered = filtered.filter(item => item.manager === filters.value.manager);
|
return props.tableData.sort((a, b) => {
|
||||||
return filtered.sort((a, b) => {
|
let aValue = a[sortField.value];
|
||||||
const aValue = a[sortField.value], bValue = b[sortField.value];
|
let bValue = b[sortField.value];
|
||||||
|
|
||||||
|
// 处理百分比字符串
|
||||||
|
if (typeof aValue === 'string' && aValue.includes('%')) {
|
||||||
|
aValue = parseFloat(aValue.replace('%', ''));
|
||||||
|
}
|
||||||
|
if (typeof bValue === 'string' && bValue.includes('%')) {
|
||||||
|
bValue = parseFloat(bValue.replace('%', ''));
|
||||||
|
}
|
||||||
|
|
||||||
return sortOrder.value === 'desc' ? bValue - aValue : aValue - bValue;
|
return sortOrder.value === 'desc' ? bValue - aValue : aValue - bValue;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -121,11 +132,31 @@ const filteredTableData = computed(() => {
|
|||||||
const onCenterLeaderChange = () => {
|
const onCenterLeaderChange = () => {
|
||||||
filters.value.advancedManager = '';
|
filters.value.advancedManager = '';
|
||||||
filters.value.manager = '';
|
filters.value.manager = '';
|
||||||
};
|
emitFilterChange();
|
||||||
|
};
|
||||||
|
|
||||||
const onAdvancedManagerChange = () => {
|
const onAdvancedManagerChange = () => {
|
||||||
filters.value.manager = '';
|
filters.value.manager = '';
|
||||||
};
|
emitFilterChange();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onManagerChange = () => {
|
||||||
|
emitFilterChange();
|
||||||
|
};
|
||||||
|
|
||||||
|
const emitFilterChange = () => {
|
||||||
|
const filterParams = {};
|
||||||
|
if (filters.value.centerLeader) {
|
||||||
|
filterParams.center_leader = filters.value.centerLeader;
|
||||||
|
}
|
||||||
|
if (filters.value.advancedManager) {
|
||||||
|
filterParams.team_leader = filters.value.advancedManager;
|
||||||
|
}
|
||||||
|
if (filters.value.manager) {
|
||||||
|
filterParams.group_leader = filters.value.manager;
|
||||||
|
}
|
||||||
|
emit('filter-change', filterParams);
|
||||||
|
};
|
||||||
|
|
||||||
const sortBy = (field) => {
|
const sortBy = (field) => {
|
||||||
if (sortField.value === field) sortOrder.value = sortOrder.value === 'desc' ? 'asc' : 'desc';
|
if (sortField.value === field) sortOrder.value = sortOrder.value === 'desc' ? 'asc' : 'desc';
|
||||||
@@ -137,10 +168,7 @@ const getRateClass = (rate) => {
|
|||||||
const getRateColor = (rate) => {
|
const getRateColor = (rate) => {
|
||||||
if (rate >= 80) return '#4CAF50'; if (rate >= 60) return '#FF9800'; return '#f44336';
|
if (rate >= 80) return '#4CAF50'; if (rate >= 60) return '#FF9800'; return '#f44336';
|
||||||
};
|
};
|
||||||
const formatDuration = (minutes) => {
|
// 移除formatDuration函数,不再需要
|
||||||
const h = Math.floor(minutes / 60), m = minutes % 60;
|
|
||||||
return h > 0 ? `${h}h${m}m` : `${m}m`;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -69,9 +69,10 @@
|
|||||||
<!-- 第五行 -->
|
<!-- 第五行 -->
|
||||||
<div class="dashboard-row row-4">
|
<div class="dashboard-row row-4">
|
||||||
<DetailedDataTable
|
<DetailedDataTable
|
||||||
:table-data="tableData"
|
:table-data="detailData"
|
||||||
:level-tree="levelTree"
|
:level-tree="levelTree"
|
||||||
v-model:selected-person="selectedPerson"
|
v-model:selected-person="selectedPerson"
|
||||||
|
@filter-change="handleFilterChange"
|
||||||
/>
|
/>
|
||||||
<DataDetailCard :selected-person="selectedPerson" />
|
<DataDetailCard :selected-person="selectedPerson" />
|
||||||
</div>
|
</div>
|
||||||
@@ -1006,17 +1007,83 @@ async function CusotomGetLevelTree() {
|
|||||||
}
|
}
|
||||||
// 获取详细数据表格
|
// 获取详细数据表格
|
||||||
const detailData = ref({});
|
const detailData = ref({});
|
||||||
async function getDetailData(level) {
|
async function getDetailData(params) {
|
||||||
const params={
|
if(params?.center_leader){
|
||||||
level,
|
// alert(11111)
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const res = await getDetailedDataTable(params)
|
const res = await getDetailedDataTable(params)
|
||||||
console.log(1222222,res)
|
console.log('详细数据表格:', res)
|
||||||
detailData.value = res.data
|
detailData.value = res.data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取详细数据表格失败:", error);
|
console.error("获取详细数据表格失败:", error);
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
// alert(22222)
|
||||||
|
try {
|
||||||
|
const res = await getDetailedDataTable()
|
||||||
|
console.log('详细数据表格:', res)
|
||||||
|
/**
|
||||||
|
* data
|
||||||
|
:
|
||||||
|
[{leader_name: "郭可英", conversion_rate: "0.00%", total_deals: 0, plus_v_rate: "59.75%",…},…]
|
||||||
|
0
|
||||||
|
:
|
||||||
|
{leader_name: "郭可英", conversion_rate: "0.00%", total_deals: 0, plus_v_rate: "59.75%",…}
|
||||||
|
conversion_rate
|
||||||
|
:
|
||||||
|
"0.00%"
|
||||||
|
form_filling_rate
|
||||||
|
:
|
||||||
|
"59.75%"
|
||||||
|
group_rate
|
||||||
|
:
|
||||||
|
"54.09%"
|
||||||
|
leader_name
|
||||||
|
:
|
||||||
|
"郭可英"
|
||||||
|
plus_v_rate
|
||||||
|
:
|
||||||
|
"59.75%"
|
||||||
|
total_deals
|
||||||
|
:
|
||||||
|
0
|
||||||
|
1
|
||||||
|
:
|
||||||
|
{leader_name: "刘瑞", conversion_rate: "1.32%", total_deals: 7, plus_v_rate: "47.17%",…}
|
||||||
|
conversion_rate
|
||||||
|
:
|
||||||
|
"1.32%"
|
||||||
|
form_filling_rate
|
||||||
|
:
|
||||||
|
"47.17%"
|
||||||
|
group_rate
|
||||||
|
:
|
||||||
|
"39.25%"
|
||||||
|
leader_name
|
||||||
|
:
|
||||||
|
"刘瑞"
|
||||||
|
plus_v_rate
|
||||||
|
:
|
||||||
|
"47.17%"
|
||||||
|
total_deals
|
||||||
|
:
|
||||||
|
7
|
||||||
|
message
|
||||||
|
:
|
||||||
|
"获取详细数据表格成功"
|
||||||
|
*/
|
||||||
|
detailData.value = res.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取详细数据表格失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理筛选器变化
|
||||||
|
const handleFilterChange = (filterParams) => {
|
||||||
|
console.log('筛选器变化:', filterParams)
|
||||||
|
getDetailData(filterParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
@@ -1029,6 +1096,8 @@ onMounted(async() => {
|
|||||||
// await getCustomerTypeRatio('child_education')
|
// await getCustomerTypeRatio('child_education')
|
||||||
// await getCustomerUrgency()
|
// await getCustomerUrgency()
|
||||||
await CusotomGetLevelTree()
|
await CusotomGetLevelTree()
|
||||||
|
await getDetailData()
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user