cxc-szcx-uniapp/pages/views/renliziyuan/renyuanxinxi/qttongji.vue
ldeyun 60ce182912 Merge branch 'master' of http://10.75.166.171/cxcxt/cxc-szcx-uniapp
# Conflicts:
#	.env.development
#	pages/views/renliziyuan/renyuanxinxi/index.vue
2025-02-12 13:58:11 +08:00

511 lines
13 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>
<view class="container" id="top1">
<uni-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<uni-col :span="24"><uni-title :title="'所选单位ID'" align="left" type="h4"></uni-title></uni-col>
</uni-row>
<uni-row style="margin-bottom: 20rpx; margin-left: 30rpx; margin-right: 30rpx">
<uni-col :span="24">
<trq-depart-select v-model="selectedOrgCode" returnCodeOrID="orgCode"
@change="onOrgCodeChange"></trq-depart-select>
</uni-col>
</uni-row>
<uni-row style="margin-bottom: 20rpx; margin-left: 30rpx; margin-right: 30rpx">
<uni-col :span="24">
<picker mode="selector" :range="fieldList" range-key="label" @change="onFieldChange">
<view class="picker">选择字段: {{ selectedFieldLabel }}</view>
</picker>
</uni-col>
</uni-row>
</view>
<!-- ECharts图表 -->
<view class="chart-container">
<l-echart ref="chart" @finished="initChart" />
</view>
<!-- 数据表格 -->
<uni-row style="margin-top: 10px; margin-left: 30rpx; margin-right: 30rpx" v-if="personnelList.length > 0">
<uni-col :span="3">
<view class="titleStyle">序号</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">姓名</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">性别</view>
</uni-col>
<uni-col :span="5">
<view class="titleStyle">年龄</view>
</uni-col>
<uni-col :span="6">
<view class="titleStyle">操作</view>
</uni-col>
</uni-row>
<scroll-view scroll-y :style="{ height: bottomHeight + 'px' }">
<uni-row style="margin-bottom: 10rpx; margin-left: 30rpx; margin-right: 30rpx">
<view v-for="(item, index) in personnelList">
<uni-col :span="3">
<view class="dataStyle">
{{ index + 1 }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.xm }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.xb_dictText }}
</view>
</uni-col>
<uni-col :span="5">
<view class="dataStyle">
{{ item.nl }}
</view>
</uni-col>
<uni-col :span="6">
<view class="dataStyle">
<button size="mini" type="primary" @click="detail(item)">详情</button>
</view>
</uni-col>
</view>
</uni-row>
</scroll-view>
</view>
</template>
<script setup>
import {
ref,
reactive,
onMounted
} from 'vue';
import * as echarts from 'echarts';
import {
cxcRyDatAstatistics,
cxcRyDatAstatisticsDetails
} from '@/api/renyuan.js';
// 存储下方组件的高度 tableData
const bottomHeight = ref(0);
// 新增加载状态
const chart = ref(null);
const fieldList = ref([{
label: '性别',
value: 'xb'
},
{
label: '年龄',
value: 'nl'
},
{
label: '学历',
value: 'rylb1'
}
]); // 字段列表
const selectedOrgCode = ref(''); // 当前选择的单位 orgCode
const selectedOrgCodeLabel = ref('请选择单位'); // 当前选择的单位名称
const selectedField = ref(''); // 当前选择的字段
const selectedFieldLabel = ref('请选择字段'); // 当前选择的字段名称
const orgCodeGroupData = ref([]); //按照orgcode进行分组的数据
const chartData = ref({}); // 图表数据
const personnelList = ref([]); // 人员列表 initChart
const chartOption = ref({});
function detail(record) {
// console.log(record)
uni.navigateTo({
url: '/pages/views/renliziyuan/renyuanxinxi/detail?data=' + encodeURIComponent(JSON.stringify(record))
});
}
onMounted(() => {
// #ifdef APP
getHeight();
// #endif
});
// #ifdef APP
const getHeight = () => {
// 获取屏幕高度
const systemInfo = uni.getSystemInfoSync();
const screenHeight = systemInfo.screenHeight;
// 创建选择器查询对象
const query = uni.createSelectorQuery();
// 获取上方组件的高度
query
.select('#top1')
.boundingClientRect((rect1) => {
// 计算上方组件高度总和
const topComponentsHeight = rect1.height;
// 计算下方组件的高度
bottomHeight.value = screenHeight - topComponentsHeight - 415;
})
.exec();
};
// #endif
// 初始化 ECharts length departChange
// 初始化图表
const initChart = () => {
setTimeout(async () => {
if (!chart.value) return;
const myChart = await chart.value.init(echarts);
myChart.setOption(chartOption.value);
}, 300);
};
// 更新图表
const updateChart = () => {
// 初始化图表
setTimeout(async () => {
if (!chart.value) return;
const myChart = await chart.value.init(echarts);
chartOption.value = transformDataForEcharts(chartData.value, selectedOrgCode.value);
myChart.setOption({
xAxis: {
type: 'category',
data: chartOption.value.categories.map((code) =>
`${code}${chartOption.value.children ? ' ▶' : ''}`)
},
yAxis: {
type: 'value'
},
series: chartOption.value.series,
tooltip: {
trigger: 'axis'
},
legend: {
data: chartOption.value.series.map((s) => s.name)
}
});
// 点击钻取事件
myChart.on('click', (params) => {
const clickedCode = params.name.split(' ')[0];
const nextData = transformDataForEcharts(originalData, clickedCode);
if (nextData.categories.length > 0) {
historyStack.push(currentLevel);
currentLevel = clickedCode;
renderChart(nextData);
} else if (nextData.children) {
historyStack.push(currentLevel);
currentLevel = clickedCode;
chartData = transformDataForEcharts(originalData, currentLevel);
renderChart(chartData);
} else {
console.log('已是最末级节点');
}
});
}, 300);
};
/**
* 转换数据为支持钻取的ECharts格式
* @param {Array} data 原始数据
* @param {string} currentOrgCode 当前组织编码
* @returns {Object} 包含当前层级数据和子节点信息的对象
*/
function transformDataForEcharts(data, currentOrgCode = '') {
// 计算当前层级的长度(每级+3 当前层级的作为标签显示再页面上,图上显示所有下一级的数据
const currentLevel = currentOrgCode.length;
const nextLevel = currentLevel + 3;
console.log(currentLevel, nextLevel, currentOrgCode, data);
// 过滤当前层级数据
let currentLevelData = {}
currentLevelData.orgCode = currentOrgCode
currentLevelData.data = data.fieldValues;
console.log(1, data.children);
// 存储所有的 orgCode
const xAxisData = [];
// 存储所有的 fieldValue 作为图例
const legendData = [];
// 存储每个 fieldValue 对应的数据系列
const seriesData = [];
// 递归处理数据
const processNode = (node) => {
try {
const orgCode = node.orgCode;
if (!xAxisData.includes(orgCode)) {
xAxisData.push(orgCode);
}
let tempseries = {}
tempseries.name = node.orgCode
tempseries.type = 'bar'
tempseries.data = []
node.fieldValues.forEach(fieldValueObj => {
let number = 0;
if (!fieldValueObj) {
const fieldValue = fieldValueObj.fieldValue;
if (!legendData.includes(fieldValue)) {
legendData.push(fieldValue);
}
number = fieldValueObj.number;
tempseries.data.push(number)
} else {
tempseries.data.push(0)
}
console.log(22, node.orgCode, number)
})
seriesData.push(tempseries)
};
// console.log(2, seriesData)
} catch (error) {
console.log(error)
}
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
processNode(child);
});
}
}
console.log(3, seriesData)
// 从根节点开始处理
data.children.forEach(node => {
processNode(node);
});
// 转换 seriesData 为 ECharts 所需的数组格式
const series = Array.from(legendData).map(fieldValue => ({
name: fieldValue,
type: 'bar',
data: seriesData[fieldValue]
}));
console.log(2, xAxisData)
console.log(3, JSON.stringify(legendData))
console.log(4, series)
return {
xAxis: {
type: 'category',
data: xAxisData
},
legend: {
data: Array.from(legendData)
},
series: series
};
}
// 使用示例
// let currentLevel = 'A01A01'; // 初始层级
// let chartData = transformDataForEcharts(originalData, currentLevel);
//根据 orgCode 的分级格式,递归地汇总本级及其所有下级的数据,并将下级数据放在本级的 children 属性中。 deepseek
const groupByOrgCode = (orgCode, data) => {
// 过滤出本级和所有下级的数据
const filteredData = data.filter((item) => item.orgCode.startsWith(orgCode));
// 如果过滤后的数据为空,返回 null
if (filteredData.length === 0) {
return null;
}
// 按照 fieldValue 分组
const groupedByFieldValue = {};
filteredData.forEach((item) => {
if (!groupedByFieldValue[item.fieldValue]) {
groupedByFieldValue[item.fieldValue] = {
number: 0,
ldhth: []
};
}
groupedByFieldValue[item.fieldValue].number += item.number;
groupedByFieldValue[item.fieldValue].ldhth.push(...item.ldhth.split(','));
});
// 构建本级结果
const result = {
orgCode: orgCode,
fieldValues: Object.keys(groupedByFieldValue).map((fieldValue) => ({
fieldValue: fieldValue,
number: groupedByFieldValue[fieldValue].number,
ldhth: [...new Set(groupedByFieldValue[fieldValue].ldhth)] // 去重
})),
children: []
};
// 获取所有下一级的 orgCode
const nextLevelOrgCodes = new Set();
filteredData.forEach((item) => {
if (item.orgCode !== orgCode && item.orgCode.startsWith(orgCode)) {
const nextLevelOrgCode = item.orgCode.substring(0, orgCode.length + 3);
nextLevelOrgCodes.add(nextLevelOrgCode);
}
});
// 递归处理下一级数据
nextLevelOrgCodes.forEach((nextLevelOrgCode) => {
const child = groupByOrgCode(nextLevelOrgCode, data);
if (child) {
result.children.push(child);
}
});
return result;
};
//-----------------------------------------------------------------------------------------
// 获取统计数据 then
const fetchStatisticsData = async () => {
if (!selectedOrgCode.value || !selectedField.value) return;
try {
const res = await cxcRyDatAstatistics({
orgCode: selectedOrgCode.value,
field: selectedField.value
});
// console.log(res); //deepseek
orgCodeGroupData.value = groupByOrgCode(selectedOrgCode.value, res);
console.log(orgCodeGroupData.value);
chartData.value = orgCodeGroupData.value;
updateChart();
} catch (error) {
console.error('获取统计数据失败:', error);
}
};
// 获取人员列表 delimiter
const fetchPersonnelList = async (ldhthList) => {
try {
const res = await cxcRyDatAstatisticsDetails({
ldhth: ldhthList
});
console.log(res);
personnelList.value = res.data;
} catch (error) {
console.error('获取人员列表失败:', error);
}
};
// 事件处理
const onOrgCodeChange = (e, data) => {
selectedOrgCode.value = e;
console.log(data.value.title);
selectedOrgCodeLabel.value = data.value.title;
fetchStatisticsData();
};
const onFieldChange = (e) => {
const index = e.detail.value;
selectedField.value = fieldList.value[index].value;
selectedFieldLabel.value = fieldList.value[index].label;
fetchStatisticsData();
};
const onChartClick = (e) => {
const {
ldhth
} = chartData.value;
if (ldhth && ldhth.length > 0) {
fetchPersonnelList(ldhth);
}
};
</script>
<style scoped>
.container {
margin: 20, 20, 20, 20rpx;
}
.input-group {
display: flex;
gap: 20rpx;
margin-bottom: 30rpx;
}
.input {
flex: 1;
border: 1rpx solid #ddd;
padding: 15rpx;
border-radius: 8rpx;
}
.query-btn {
background: #007aff;
color: white;
padding: 0 40rpx;
border-radius: 8rpx;
}
.stats-box {
display: flex;
justify-content: space-around;
margin: 30rpx 0;
padding: 20rpx;
background: #f8f8f8;
border-radius: 12rpx;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.label {
font-size: 24rpx;
color: #666;
}
.value {
font-size: 36rpx;
font-weight: bold;
color: #0000ff;
}
.chart-container {
height: 800rpx;
margin-top: 20rpx;
}
.titleStyle {
font-size: 12px;
color: #747474;
line-height: 30px;
height: 30px;
background: #f2f9fc;
text-align: center;
vertical-align: middle;
border-left: 1px solid #919191;
border-bottom: 1px solid #919191;
}
/* 内容样式 */
.dataStyle {
max-font-size: 14px;
/* 最大字体限制 */
min-font-size: 10px;
/* 最小字体限制 */
font-size: 12px;
color: #00007f;
line-height: 30px;
height: 30px;
font-weight: 500;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid #919191;
border-left: 1px solid #919191;
text-overflow: ellipsis;
}
</style>