+
+
+
+
-
-

-
在岗人
-
- {{ peopleCount }}
-
人
+
+
+
出勤人数
+
{{ attendanceCount }}
-
-
-

-
出勤率
-
- {{ attendanceRate }}
-
%
+
+
出勤率
+
{{ attendanceRate }}%
+
+
+
+

+
+
-
+
+
+
+
+
+

+
+
+
施工人员
+
{{ constructionPersonnelCount }}
+
+
+
+
+
+

+
+
+
分包人员
+
{{ subcontractorsCount }}
+
+
+
+
+
+

+
+
+
管理人员
+
{{ managersCount }}
+
+
+
+
+
+
-
-
@@ -62,7 +92,7 @@
import { ref } from 'vue';
import Title from './title.vue';
import { getScreenNews, getScreenPeople } from '@/api/projectScreen';
-import { keyIndex } from '@/api/enterpriseLarge/index';
+import { keyIndex, projectAttendanceCount, peopleCount, allAttendanceCount } from '@/api/enterpriseLarge/index';
import { mapOption } from './optionList';
import * as echarts from 'echarts';
@@ -114,9 +144,14 @@ const newDetail = ref({
const newId = ref('');
const attendanceCount = ref(0);
const attendanceRate = ref(0);
-const peopleCount = ref(0);
+const totalPeopleCount = ref(0);
const teamAttendanceList = ref([{ id: '', teamName: '', attendanceNumber: 0, allNumber: 0, attendanceRate: 0, attendanceTime: '' }]);
+// 人员分类统计数据
+const constructionPersonnelCount = ref(0);
+const managersCount = ref(0);
+const subcontractorsCount = ref(0);
+
// 项目出勤率数据
const projectAttendanceData = ref([
{ name: 'A项目', value: 62 },
@@ -129,6 +164,11 @@ const projectAttendanceData = ref([
let attendanceChart = null;
const attendanceChartRef = ref
(null);
+// 滚动相关状态
+const scrollInterval = ref(null);
+const currentScrollIndex = ref(0);
+const scrollSpeed = 1000; // 滚动间隔时间(ms)
+
/**
* 获取项目人员出勤数据
*/
@@ -136,13 +176,189 @@ const getPeopleData = async () => {
const res = await getScreenPeople(props.projectId);
const { data, code } = res;
if (code === 200) {
- attendanceCount.value = data.attendanceCount;
- attendanceRate.value = data.attendanceRate;
- peopleCount.value = data.peopleCount;
+ totalPeopleCount.value = data.peopleCount;
teamAttendanceList.value = data.teamAttendanceList;
}
};
+/**
+ * 获取人员分类统计数据
+ */
+const getPeopleCategoryData = async () => {
+ try {
+ const res = await peopleCount();
+ const { data, code } = res;
+ if (code === 200 && data) {
+ constructionPersonnelCount.value = data.constructionPersonnelCount || 0;
+ managersCount.value = data.managersCount || 0;
+ subcontractorsCount.value = data.subcontractorsCount || 0;
+ }
+ } catch (error) {
+ console.error('获取人员分类统计数据失败:', error);
+ }
+};
+
+/**
+ * 获取人员总览数据 - 通过allAttendanceCount接口
+ */
+const getProjectAttendanceCount = async () => {
+ try {
+ const res = await allAttendanceCount();
+ const { data, code } = res;
+ if (code === 0 && data) {
+ // 直接使用接口返回的数据
+ attendanceCount.value = data.attendanceCount || 0;
+ attendanceRate.value = data.attendanceRate || 0;
+ }
+ } catch (error) {
+ console.error('获取人员总览数据失败:', error);
+ }
+};
+
+/**
+ * 滚动到指定项目
+ */
+const scrollToProject = (index: number) => {
+ if (!attendanceChart || projectAttendanceData.value.length === 0) return;
+
+ // 计算实际索引
+ const realIndex = index % projectAttendanceData.value.length;
+ currentScrollIndex.value = realIndex;
+
+ // 获取项目名称
+ const projectName = projectAttendanceData.value[realIndex].name;
+
+ // 触发tooltip显示
+ attendanceChart.dispatchAction({
+ type: 'showTip',
+ seriesIndex: 0,
+ dataIndex: realIndex
+ });
+
+ // 高亮当前项目的柱子
+ attendanceChart.dispatchAction({
+ type: 'highlight',
+ seriesIndex: 0,
+ dataIndex: realIndex
+ });
+
+ // 取消高亮其他项目的柱子
+ attendanceChart.dispatchAction({
+ type: 'downplay',
+ seriesIndex: 0,
+ dataIndex: Array.from({ length: projectAttendanceData.value.length }, (_, i) => i).filter((i) => i !== realIndex)
+ });
+
+ // 更新滚动条位置,确保滚动时滚动条同步移动
+ // 无论项目数量多少,都应该同步滚动条
+ if (projectAttendanceData.value.length > 0) {
+ // 计算滚动条应该移动到的位置
+ // 确保当前项目居中显示
+ const totalProjects = projectAttendanceData.value.length;
+ const visiblePercentage = 15; // 与dataZoom的end值保持一致
+ const itemPercentage = 100 / totalProjects; // 每个项目所占总宽度的百分比
+
+ // 计算新的start值,使当前项目尽量居中显示
+ let newStart = realIndex * itemPercentage - visiblePercentage / 2 + itemPercentage / 2;
+
+ // 确保start值在有效范围内
+ newStart = Math.max(0, Math.min(100 - visiblePercentage, newStart));
+
+ // 更新dataZoom组件的位置
+ attendanceChart.dispatchAction({
+ type: 'dataZoom',
+ start: newStart,
+ end: newStart + visiblePercentage
+ });
+ }
+};
+
+/**
+ * 开始自动滚动
+ */
+const startScroll = () => {
+ if (scrollInterval.value) return;
+
+ scrollInterval.value = window.setInterval(() => {
+ currentScrollIndex.value++;
+ scrollToProject(currentScrollIndex.value);
+ }, scrollSpeed);
+};
+
+/**
+ * 停止自动滚动
+ */
+const stopScroll = () => {
+ if (scrollInterval.value) {
+ clearInterval(scrollInterval.value);
+ scrollInterval.value = null;
+ }
+};
+
+/**
+ * 获取项目出勤率统计数据 - 保持项目出勤率图表功能
+ */
+const getProjectAttendanceStats = async () => {
+ try {
+ const res = await projectAttendanceCount();
+ const { data, code } = res;
+
+ // 添加日志信息以便调试
+ console.log('projectAttendanceCount接口返回数据:', res);
+
+ if (code === 200 && data && data.length > 0) {
+ console.log('有效数据:', data);
+
+ // 更新项目出勤率图表数据
+ projectAttendanceData.value = data.map((project) => ({
+ name: project.projectName,
+ value: project.attendanceRate
+ }));
+
+ console.log('处理后的数据:', projectAttendanceData.value);
+
+ // 如果图表已初始化,重新设置数据
+ if (attendanceChart) {
+ console.log('更新图表数据');
+ attendanceChart.setOption({
+ xAxis: {
+ data: projectAttendanceData.value.map((item) => item.name)
+ },
+ series: [
+ {
+ data: projectAttendanceData.value.map((item) => item.value)
+ }
+ ]
+ });
+ }
+ } else {
+ console.warn('数据不符合预期:', { code, data });
+ // 使用默认数据确保图表有内容显示
+ if (!projectAttendanceData.value.length) {
+ projectAttendanceData.value = [
+ { name: 'A项目', value: 62 },
+ { name: 'B项目', value: 56 },
+ { name: 'C项目', value: 95 },
+ { name: 'D项目', value: 64 },
+ { name: 'E项目', value: 97.5 }
+ ];
+ }
+ }
+ } catch (error) {
+ console.error('获取项目出勤率数据失败:', error);
+ // 发生错误时使用默认数据
+ if (!projectAttendanceData.value.length) {
+ projectAttendanceData.value = [
+ { name: 'A项目', value: 62 },
+ { name: 'B项目', value: 56 },
+ { name: 'C项目', value: 95 },
+ { name: 'D项目', value: 64 },
+ { name: 'E项目', value: 97.5 }
+ ];
+ }
+ }
+};
+
/**
* 获取企业关键指标数据
*/
@@ -163,8 +379,23 @@ const getKeyIndexData = async () => {
*/
const initAttendanceChart = () => {
if (!attendanceChartRef.value) {
+ console.warn('attendanceChartRef不存在');
return;
}
+
+ // 确保有数据可显示
+ if (!projectAttendanceData.value || !projectAttendanceData.value.length) {
+ console.log('使用默认数据初始化图表');
+ projectAttendanceData.value = [
+ { name: 'A项目', value: 62 },
+ { name: 'B项目', value: 56 },
+ { name: 'C项目', value: 95 },
+ { name: 'D项目', value: 64 },
+ { name: 'E项目', value: 97.5 }
+ ];
+ }
+
+ console.log('开始初始化图表,当前数据:', projectAttendanceData.value);
attendanceChart = echarts.init(attendanceChartRef.value);
const option = {
@@ -191,13 +422,45 @@ const initAttendanceChart = () => {
grid: {
top: 40,
right: 30,
- bottom: 40,
+ bottom: 60, // 增加底部空间以确保标签完全显示
left: 30,
containLabel: true,
backgroundColor: 'rgba(10, 24, 45, 0.1)',
borderColor: 'rgba(29, 214, 255, 0.1)',
borderWidth: 1
},
+ // 添加dataZoom组件实现水平滚动
+ dataZoom: [
+ {
+ type: 'slider',
+ show: true,
+ xAxisIndex: [0],
+ start: 0,
+ end: 20, // 固定为20%,与visiblePercentage保持一致
+ height: 20,
+ bottom: 10,
+ backgroundColor: 'rgba(10, 24, 45, 0.2)',
+ fillerColor: 'rgba(29, 214, 255, 0.2)',
+ borderColor: 'rgba(29, 214, 255, 0.3)',
+ textStyle: {
+ color: '#e6f7ff'
+ },
+ handleStyle: {
+ color: 'rgba(29, 214, 255, 0.6)',
+ borderColor: 'rgba(255, 255, 255, 0.3)'
+ },
+ moveHandleStyle: {
+ color: 'rgba(29, 214, 255, 0.8)'
+ }
+ },
+ // 支持鼠标滚轮缩放
+ {
+ type: 'inside',
+ xAxisIndex: [0],
+ start: 0,
+ end: 20 // 固定为20%,与visiblePercentage保持一致
+ }
+ ],
xAxis: {
type: 'category',
data: projectAttendanceData.value.map((item) => item.name),
@@ -209,9 +472,18 @@ const initAttendanceChart = () => {
axisLabel: {
color: '#e6f7ff',
fontSize: 14,
- interval: 0,
+ interval: 0, // 强制显示所有标签
fontWeight: 'bold',
- padding: [10, 0, 0, 0]
+ padding: [10, 0, 0, 0],
+ // 防止标签重叠,旋转角度调整
+ rotate: 0,
+ // 添加formatter函数,当名称超过6个汉字时显示省略号
+ formatter: function (value) {
+ if (value.length > 6) {
+ return value.substring(0, 6) + '...';
+ }
+ return value;
+ }
},
axisTick: {
show: true,
@@ -321,6 +593,19 @@ const initAttendanceChart = () => {
attendanceChart.setOption(option);
+ // 添加鼠标悬浮事件监听
+ if (attendanceChartRef.value) {
+ // 鼠标进入图表区域时停止滚动
+ attendanceChartRef.value.addEventListener('mouseenter', () => {
+ stopScroll();
+ });
+
+ // 鼠标离开图表区域时重新开始滚动
+ attendanceChartRef.value.addEventListener('mouseleave', () => {
+ startScroll();
+ });
+ }
+
// 添加窗口大小变化时的图表更新
const handleResize = () => {
if (attendanceChart) {
@@ -333,6 +618,12 @@ const initAttendanceChart = () => {
// 清理函数
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
+
+ // 移除鼠标事件监听
+ if (attendanceChartRef.value) {
+ attendanceChartRef.value.removeEventListener('mouseenter', stopScroll);
+ attendanceChartRef.value.removeEventListener('mouseleave', startScroll);
+ }
});
};
@@ -347,17 +638,23 @@ const initMapChart = () => {
mapChart.setOption(mapOption);
};
-onMounted(() => {
+onMounted(async () => {
// nextTick(() => {
// initMapChart();
// });
getPeopleData();
getKeyIndexData();
+ getProjectAttendanceCount(); // 获取人员总览数据
+ getPeopleCategoryData();
- // 初始化项目出勤率柱状图
- setTimeout(() => {
- initAttendanceChart();
- }, 100);
+ // 先等待获取项目出勤率数据
+ await getProjectAttendanceStats(); // 获取项目出勤率图表数据
+
+ // 再初始化图表
+ initAttendanceChart();
+
+ // 图表初始化后自动开始滚动
+ startScroll();
});
onUnmounted(() => {
@@ -370,6 +667,9 @@ onUnmounted(() => {
attendanceChart.dispose();
attendanceChart = null;
}
+
+ // 清理滚动计时器
+ stopScroll();
});
@@ -383,37 +683,59 @@ onUnmounted(() => {
.endPage {
display: flex;
flex-direction: column;
- align-items: center;
width: 100%;
- padding: 15px 0;
+ height: 250;
border: 1px solid rgba(29, 214, 255, 0.1);
box-sizing: border-box;
}
-
.endPage {
+ height: auto;
flex: 1;
- margin-top: 23px;
+ min-height: 550px;
}
-
.project_attendance_chart {
display: flex;
flex-direction: column;
- align-items: center;
width: 100%;
- padding: 15px 0;
- border: 1px solid rgba(29, 214, 255, 0.1);
- box-sizing: border-box;
+ margin-top: 15px;
+ padding-bottom: 10px;
.chart_title {
font-size: 16px;
color: #e6f7ff;
- margin-bottom: 15px;
+ margin-bottom: 5px;
text-align: left;
}
+
+ .scroll_controls {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ margin-bottom: 10px;
+ padding-right: 10px;
+ }
+
+ .control_btn {
+ padding: 5px 15px;
+ background: rgba(10, 24, 45, 0.8);
+ border: 1px solid rgba(29, 214, 255, 0.3);
+ color: #e6f7ff;
+ font-size: 12px;
+ cursor: pointer;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+ }
+
+ .control_btn:hover {
+ background: rgba(29, 214, 255, 0.2);
+ border-color: rgba(29, 214, 255, 0.6);
+ }
+
.chart_content {
width: 100%;
- height: 200px;
+ height: 320px; /* 增加高度以容纳滚动条 */
position: relative;
+ overflow: hidden;
}
}
}
@@ -422,20 +744,20 @@ onUnmounted(() => {
width: 100%;
display: grid;
grid-template-columns: repeat(2, 1fr);
- gap: 15px;
- margin-top: 15px;
- padding: 0 15px;
+ gap: 10px;
+ margin-bottom: 8px;
+ padding: 0 10px;
box-sizing: border-box;
}
.indicator-card {
position: relative;
width: 100%;
- height: 100px;
+ height: 80px;
background: rgba(10, 24, 45, 0.7);
border: 1px solid rgba(29, 214, 255, 0.2);
border-radius: 4px;
- padding: 15px;
+ padding: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
@@ -488,53 +810,151 @@ onUnmounted(() => {
object-fit: contain;
}
-.map {
- margin-top: 15px;
-}
-
-.attendance_tag {
+/* 人员总览样式 */
+.people_overview {
width: 100%;
+ margin-top: 5px;
+ box-sizing: border-box;
+ border: 1px solid rgba(29, 214, 255, 0.1);
+ background: rgba(10, 24, 45, 0.3);
+}
+
+.people_overview_title {
+ font-size: 16px;
+ color: #e6f7ff;
+ margin-bottom: 5px;
+ border-bottom: 1px solid rgba(29, 214, 255, 0.1);
+}
+
+.people_overview_content {
display: flex;
- justify-content: space-between;
- padding: 0 30px;
- margin-top: 15px;
-
- .tag_item {
- width: 28%;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 10px;
- border: 1px dashed rgba(29, 214, 255, 0.3);
- padding: 10px;
-
- .tag_info {
- font-size: 20px;
- font-weight: 700;
- color: rgba(230, 247, 255, 1);
- text-shadow: 0px 1.24px 6.21px rgba(0, 190, 247, 1);
- }
-
- .tag_title {
- font-size: 14px;
- font-weight: 400;
- color: rgba(230, 247, 255, 1);
- }
- }
+ align-items: center;
}
-.attendance_list {
- padding: 0px 30px;
+.people_image {
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(135deg, rgba(29, 214, 255, 0.1) 0%, rgba(29, 214, 255, 0.05) 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.placeholder_image {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+.people_stats {
+ flex: 1;
+ display: flex;
+ justify-content: space-around;
+}
+
+.stat_item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.stat_label {
font-size: 14px;
-
- .attendance_item {
- display: grid;
- grid-template-columns: 3fr 2fr 2fr 3fr;
- margin-top: 20px;
- }
+ color: #8ab2ff;
+ margin-bottom: 5px;
}
-.subfont {
- color: rgba(138, 149, 165, 1);
+.stat_value {
+ font-size: 28px;
+ font-weight: bold;
+ color: #e6f7ff;
+ text-shadow: 0px 1.24px 6.21px rgba(0, 190, 247, 0.5);
+}
+
+.stat_rate {
+ font-size: 28px;
+ font-weight: bold;
+ color: #00c853;
+ text-shadow: 0px 1.24px 6.21px rgba(0, 200, 83, 0.5);
+}
+
+/* 点阵地图样式 */
+.people_map {
+ width: 100%;
+ height: 120px;
+ margin-top: 8px;
+ background: rgba(10, 24, 45, 0.5);
+ border: 1px solid rgba(29, 214, 255, 0.1);
+ border-radius: 4px;
+ overflow: hidden;
+ position: relative;
+}
+
+.map_background {
+ width: 100%;
+ height: 100%;
+ background-image: radial-gradient(circle, rgba(29, 214, 255, 0.3) 1px, transparent 1px),
+ radial-gradient(circle, rgba(29, 214, 255, 0.3) 1px, transparent 1px);
+ background-size: 40px 40px;
+ background-position:
+ 0,
+ 0,
+ 20px 20px;
+ position: relative;
+}
+
+/* 人员分类统计样式 */
+.people_categories {
+ display: flex;
+ justify-content: space-around;
+ margin-top: 5px;
+}
+
+.category_item {
+ display: flex;
+ flex: 1;
+ margin: 10px, 30px;
+}
+
+.category_icon {
+ margin-left: 15px;
+ margin-top: 10px;
+ width: 30px;
+ height: 30px;
+ background: linear-gradient(135deg, rgba(29, 214, 255, 0.1) 0%, rgba(10, 120, 200, 0.1) 100%);
+ border: 1px solid rgba(29, 214, 255, 0.2);
+ border-radius: 5px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.category_image {
+ width: 45px;
+ height: 45px;
+ object-fit: contain;
+}
+
+.category_info {
+ display: flex;
+ flex-direction: column;
+ margin-left: 8px;
+ margin-top: 8px;
+ flex: 1;
+}
+
+.category_label {
+ font-size: 12px;
+ color: #8ab2ff;
+ margin-bottom: 1px;
+}
+
+.category_count {
+ font-size: 16px;
+ font-weight: bold;
+ color: #e6f7ff;
+ text-shadow: 0px 1.24px 6.21px rgba(0, 190, 247, 0.3);
}