feat(数据表格): 实现详细数据表格的筛选和排序功能

- 添加筛选器变化事件处理,支持按中心领导、团队领导和组长筛选数据
- 修改表格排序逻辑以适配新数据结构
- 更新表格显示字段以匹配后端返回的数据格式
- 移除不再需要的格式化函数
This commit is contained in:
2025-08-18 13:27:03 +08:00
parent 8f709aa1f5
commit 57069e3a01
2 changed files with 144 additions and 47 deletions

View File

@@ -25,14 +25,14 @@
</select>
</div>
<div class="filter-group">
<label>经理:</label>
<select v-model="filters.manager" :disabled="!filters.advancedManager">
<option value="">全部经理</option>
<option v-for="manager in availableManagers" :key="manager.name" :value="manager.name">
{{ manager.name }}
</option>
</select>
</div>
<label>经理:</label>
<select v-model="filters.manager" @change="onManagerChange" :disabled="!filters.advancedManager">
<option value="">全部经理</option>
<option v-for="manager in availableManagers" :key="manager.name" :value="manager.name">
{{ manager.name }}
</option>
</select>
</div>
</div>
<!-- 数据表格 -->
@@ -41,34 +41,36 @@
<thead>
<tr>
<th>人员</th>
<th @click="sortBy('dealRate')" class="sortable">成交率 <span class="sort-icon" :class="{ active: sortField === 'dealRate' }">{{ 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('conversion_rate')" class="sortable">成交率 <span class="sort-icon" :class="{ active: sortField === 'conversion_rate' }">{{ 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>
</tr>
</thead>
<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>
<div class="person-info">
<div class="person-avatar">{{ person.name.charAt(0) }}</div>
<div>
<div class="person-name">{{ person.name }}</div>
<div class="person-position">{{ person.position }}</div>
<div class="person-avatar">{{ person.leader_name?.charAt(0) }}</div>
<div>
<div class="person-name">{{ person.leader_name}}</div>
</div>
</div>
</td>
<td>
<div class="deal-rate">
<span class="rate-value" :class="getRateClass(person.dealRate)">{{ person.dealRate }}%</span>
<div class="rate-bar"><div class="rate-fill" :style="{ width: person.dealRate + '%', backgroundColor: getRateColor(person.dealRate) }"></div></div>
<span class="rate-value" :class="getRateClass(parseFloat(person.conversion_rate))">{{ person.conversion_rate }}</span>
<div class="rate-bar"><div class="rate-fill" :style="{ width: parseFloat(person.conversion_rate) + '%', backgroundColor: getRateColor(parseFloat(person.conversion_rate)) }"></div></div>
</div>
</td>
<td>{{ formatDuration(person.callDuration) }}</td>
<td>{{ person.callCount }}</td>
<td>¥{{ person.dealAmount.toLocaleString() }}</td>
<td>{{ person.department }}</td>
<td>{{ person.total_deals }}</td>
<td>{{ person.plus_v_rate }}</td>
<td>{{ person.group_rate }}</td>
<td>{{ person.form_filling_rate }}</td>
</tr>
</tbody>
</table>
@@ -85,10 +87,10 @@ const props = defineProps({
selectedPerson: { type: Object, default: null },
levelTree: { type: Object, default: () => ({}) }
});
defineEmits(['update:selectedPerson']);
const emit = defineEmits(['update:selectedPerson', 'filter-change']);
const filters = ref({ centerLeader: '', advancedManager: '', manager: '', dealStatus: '' });
const sortField = ref('dealRate');
const sortField = ref('conversion_rate');
const sortOrder = ref('desc');
const centerLeaders = computed(() => {
@@ -107,25 +109,54 @@ const centerLeaders = computed(() => {
return manager ? manager.managers || [] : [];
});
const filteredTableData = computed(() => {
let filtered = props.tableData;
if (filters.value.centerLeader) filtered = filtered.filter(item => item.centerLeader === filters.value.centerLeader);
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 filtered.sort((a, b) => {
const aValue = a[sortField.value], bValue = b[sortField.value];
// 显示表格数据直接使用API返回的数据
const displayTableData = computed(() => {
if (!props.tableData || !Array.isArray(props.tableData)) return [];
return props.tableData.sort((a, b) => {
let aValue = a[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;
});
});
const onCenterLeaderChange = () => {
filters.value.advancedManager = '';
filters.value.manager = '';
};
filters.value.advancedManager = '';
filters.value.manager = '';
emitFilterChange();
};
const onAdvancedManagerChange = () => {
filters.value.manager = '';
};
const onAdvancedManagerChange = () => {
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) => {
if (sortField.value === field) sortOrder.value = sortOrder.value === 'desc' ? 'asc' : 'desc';
@@ -137,10 +168,7 @@ const getRateClass = (rate) => {
const getRateColor = (rate) => {
if (rate >= 80) return '#4CAF50'; if (rate >= 60) return '#FF9800'; return '#f44336';
};
const formatDuration = (minutes) => {
const h = Math.floor(minutes / 60), m = minutes % 60;
return h > 0 ? `${h}h${m}m` : `${m}m`;
};
// 移除formatDuration函数,不再需要
</script>
<style scoped>

View File

@@ -69,9 +69,10 @@
<!-- 第五行 -->
<div class="dashboard-row row-4">
<DetailedDataTable
:table-data="tableData"
:table-data="detailData"
:level-tree="levelTree"
v-model:selected-person="selectedPerson"
@filter-change="handleFilterChange"
/>
<DataDetailCard :selected-person="selectedPerson" />
</div>
@@ -1006,17 +1007,83 @@ async function CusotomGetLevelTree() {
}
// 获取详细数据表格
const detailData = ref({});
async function getDetailData(level) {
const params={
level,
}
async function getDetailData(params) {
if(params?.center_leader){
// alert(11111)
try {
const res = await getDetailedDataTable(params)
console.log(1222222,res)
console.log('详细数据表格:', res)
detailData.value = res.data
} catch (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() => {
@@ -1029,6 +1096,8 @@ onMounted(async() => {
// await getCustomerTypeRatio('child_education')
// await getCustomerUrgency()
await CusotomGetLevelTree()
await getDetailData()
});
</script>