提交 bb76d85f authored 作者: coderBryanFu's avatar coderBryanFu

feat:新增概览页时间线日历热力图功能

上级 bb99a95a
<script setup lang="ts">
import "@/styles/container.scss"
import TextStyle from './textStyle.vue';
import ConstStyle from './constStyle.vue';
import { ElScrollbar, ElSpace } from "element-plus";
</script>
<template>
<el-scrollbar>
<div class="common-page">
<el-space direction="vertical" alignment="flex-start">
<div class="text-title-0-show">开发样式</div>
<div class="text-title-1-show">样式变量</div>
<const-style></const-style>
<ConstStyle />
<div class="text-title-1-show">文字样式</div>
<text-style></text-style>
<TextStyle />
</el-space>
</div>
</el-scrollbar>
</template>
\ No newline at end of file
</template>
<script setup>
import "@/styles/container.scss"
import TextStyle from './textStyle.vue';
import ConstStyle from './constStyle.vue';
</script>
\ No newline at end of file
<template>
<table style="width: 100%; border-collapse: collapse; border: 1px solid #ebeef5;">
<table style="width: 1600px; border-collapse: collapse; border: 1px solid #ebeef5;">
<!-- 表头 -->
<thead>
<tr class="text-title-2">
......
......@@ -8,6 +8,7 @@ import { withFallbackImage } from "./utils";
import "./styles/scrollbar.css";
import "./styles/elui.css";
import "./styles/main.css";
import "./styles/common.scss"
import '@/assets/fonts/font.css'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
......
<template>
<div style="position: relative;">
<div ref="chartRef" style="height: 620px; width: 1640px" @mousemove="handleMouseMove"></div>
<div v-show="tooltipVisible" class="custom-tooltip background-as-card"
:style="{ left: mouseX + 'px', top: mouseY + 'px' }">
<div class="tooltip-header flex-display">
<div class="tooltip-header-left text-title-3-bold">{{ currentDate }}</div>
<div class="tooltip-header-right text-title-3-show">{{ `${'3个部门'}/${'3项举措'}` }}</div>
<div ref="chartRef" style="height: 620px; width: 1640px" @click="handleClick"></div>
<div
v-show="tooltipVisible"
class="custom-tooltip background-as-card"
:style="{ left: mouseX + 'px', top: mouseY + 'px', cursor: dragging ? 'move' : 'default' }"
@mousedown="startDrag"
>
<div
class="tooltip-header flex-display"
@mousedown.stop="startDrag"
style="cursor: move;"
>
<div class="tooltip-header-left text-title-3-bold">{{ currentDate }}</div>
<div class="tooltip-header-right text-title-3-show">{{ `${'3个部门'}/${'3项举措'}` }}</div>
</div>
<div class="tooltip-main">
<div class="tooltip-main-item" v-for="item, index in currentDetailList" :key="index">
<div class="item-header flex-display">
<div class="item-header-left flex-display">
<div class="logo">
<img style="width:100%; height: 100%" :src="item.orgLogoUrl" alt="logo">
</div>
<div class="name text-bold">{{ item.orgName }}</div>
<div class="status">
<div class="status-on text-tip-2" v-if="item.stauts === 2">{{ '已落实' }}</div>
<div class="status-off text-tip-2" v-else>{{ '未落实' }}</div>
</div>
</div>
<div class="item-header-right flex-display">
<AreaTag v-for="tag, idx in item.techDomainList.slice(0,3)" :key="idx" :tagName="tag"></AreaTag>
</div>
<div class="tooltip-main-item" v-for="item, index in currentDetailList" :key="index">
<div class="item-header flex-display">
<div class="item-header-left flex-display">
<div class="logo">
<img style="width:100%; height: 100%" :src="item.orgLogoUrl" alt="logo">
</div>
<div class="item-content text-compact">{{ item.name }}</div>
<div class="item-footer"></div>
<div class="name text-bold">{{ item.orgName }}</div>
<div class="status">
<div class="status-on text-tip-2" v-if="item.stauts === 2">{{ '已落实' }}</div>
<div class="status-off text-tip-2" v-else>{{ '未落实' }}</div>
</div>
</div>
<div class="item-header-right flex-display">
<AreaTag v-for="tag, idx in item.techDomainList.slice(0, 3)" :key="idx" :tagName="tag"></AreaTag>
</div>
</div>
<div class="item-content text-compact">{{ item.name }}</div>
<div class="item-footer"></div>
</div>
</div>
</div>
</div>
</template>
......@@ -53,6 +60,32 @@ const props = defineProps(
}
);
const dragging = ref(false);
let dragOffsetX = 0;
let dragOffsetY = 0;
const startDrag = (e) => {
dragging.value = true;
dragOffsetX = e.clientX - mouseX.value;
dragOffsetY = e.clientY - mouseY.value;
document.addEventListener('mousemove', onDrag);
document.addEventListener('mouseup', stopDrag);
}
const onDrag = (e) => {
if (dragging.value) {
mouseX.value = e.clientX - dragOffsetX;
mouseY.value = e.clientY - dragOffsetY;
}
}
const stopDrag = () => {
dragging.value = false;
document.removeEventListener('mousemove', onDrag);
document.removeEventListener('mouseup', stopDrag);
}
echarts.use([
CustomChart,
......@@ -72,10 +105,18 @@ const mouseY = ref(0)
const currentDate = ref('')
const currentDetailList = ref([])
// 鼠标移动,记录位置
// // 鼠标移动,记录位置
const handleMouseMove = (e) => {
mouseX.value = e.clientX + 20
mouseY.value = e.clientY + 20
mouseY.value = e.clientY - 20
}
const handleClick = (e) => {
// mouseX.value = e.clientX - 160
// mouseY.value = e.clientY - 120
mouseX.value = e.clientX + 10
mouseY.value = e.clientY - 100
}
// 计算展示的月份 获取今天在内的之后的三个月的月份
......@@ -141,12 +182,27 @@ console.log('timelineData', props.timelineData);
const { dataMap, monthStats } = buildDataMap(props.timelineData);
function getOption() {
const calendarWidthPercent = 30;
const gapPercent = (95 - (calendarWidthPercent * 3)) / 4;
// 计算所有天的最大 count
let maxCount = 0;
dataMap.forEach(item => {
if (item.count > maxCount) maxCount = item.count;
});
// 颜色插值函数
function getColorByCount(count) {
// rgb(231,243,255) -> rgb(137,193,255)
const start = [231, 243, 255];
const end = [137, 193, 255];
if (maxCount === 0) return `rgb(${start.join(',')})`;
const ratio = Math.min(count / maxCount, 1);
const rgb = start.map((s, i) => Math.round(s + (end[i] - s) * ratio));
return `rgb(${rgb.join(',')})`;
}
const calendars = months.map((month, index) => ({
top: 40,
left: `${gapPercent + index * (calendarWidthPercent + gapPercent)}%`,
......@@ -167,14 +223,16 @@ function getOption() {
textStyle: {
rich: {
month: {
fontSize: 24,
fontSize: 18,
fontWeight: 'bold',
color: '#1e3a8a',
color: 'rgb(5, 95, 194)',
fontFamily: 'Source Han Sans CN'
},
stats: {
fontSize: 14,
color: '#1e40af',
fontSize: 18,
color: 'rgb(5, 95, 194)',
fontWeight: 'bold',
fontFamily: 'Source Han Sans CN'
}
}
}
......@@ -189,9 +247,6 @@ function getOption() {
monthData.push([dateStr, item]);
}
// console.log('monthData', monthData);
return {
type: 'custom',
coordinateSystem: 'calendar',
......@@ -210,14 +265,17 @@ function getOption() {
const y = cellPoint[1] - cellHeight / 2;
const groupChildren = [];
const bgColor = item.status === 'has_events' ? '#bfdbfe' : '#f0f9ff';
// 动态颜色
const bgColor = item.status === 'has_events'
? getColorByCount(item.count)
: '#f0f9ff';
groupChildren.push({
type: 'rect',
shape: { x, y, width: cellWidth, height: cellHeight },
style: {
fill: bgColor,
stroke: '#efefef',
stroke: '#fff',
lineWidth: 1
},
});
......@@ -252,7 +310,7 @@ function getOption() {
textAlign: 'left',
textVerticalAlign: 'bottom',
fontSize: 18,
fontWeight: 'bold',
fontFamily: 'Source Han Sans CN'
},
});
} else {
......@@ -266,7 +324,8 @@ function getOption() {
textAlign: 'center',
textVerticalAlign: 'bottom',
fontSize: 10,
fill: '#93c5fd',
fill: 'rgb(185, 220, 255)',
fontFamily: 'Source Han Sans CN'
},
});
groupChildren.push({
......@@ -278,8 +337,8 @@ function getOption() {
textAlign: 'center',
textVerticalAlign: 'top',
fontSize: 12,
fontWeight: 'bold',
fill: '#93c5fd',
fill: 'rgb(185, 220, 255)',
fontFamily: 'Source Han Sans CN'
},
});
}
......@@ -292,7 +351,6 @@ function getOption() {
};
});
return {
tooltip: { show: false },
title: titles,
......@@ -307,7 +365,7 @@ onMounted(() => {
chartInstance.setOption(getOption());
window.addEventListener('resize', resizeChart);
chartInstance.on('mouseover', (params) => {
chartInstance.on('click', (params) => {
console.log('params', params);
if (Array.isArray(params.data) && params.data.length >= 2) {
......@@ -318,13 +376,15 @@ onMounted(() => {
currentDate.value = date
currentDetailList.value = list
tooltipVisible.value = true
} else {
tooltipVisible.value = false
}
}
});
chartInstance.on('mouseout', () => {
tooltipVisible.value = false
});
// chartInstance.on('mouseout', () => {
// tooltipVisible.value = false
// });
}
});
......@@ -347,11 +407,12 @@ onBeforeUnmount(() => {
.custom-tooltip {
position: fixed;
/* 使用 fixed 相对于视口定位 */
pointer-events: none;
/* pointer-events: none; */
/* 让鼠标事件穿透到图表,避免闪烁 */
z-index: 1000;
width: 670px;
background: rgb(255, 255, 255);
}
.tooltip-header {
......@@ -404,16 +465,19 @@ onBeforeUnmount(() => {
background: rgba(206, 79, 81, 0.1);
}
.item-content{
.item-content {
height: 48px;
display: -webkit-box;
-webkit-line-clamp: 2; /* 限制显示2行 */
-webkit-line-clamp: 2;
/* 限制显示2行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-word; /* 可选:防止单词被截断 */
word-break: break-word;
/* 可选:防止单词被截断 */
}
.item-header-right{
.item-header-right {
gap: 8px;
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论