feat(实时资讯): 新增周热度统计图表组件及交互功能

添加LineHol和LineHolYellow图表组件用于展示周热度统计数据
实现行业、概念标签和媒体来源的tab切换功能
优化页面布局和样式,调整部分标题文字
This commit is contained in:
zzp 2025-09-11 20:21:53 +08:00
parent 2204df2614
commit 4f5682c80a
3 changed files with 309 additions and 40 deletions

View File

@ -0,0 +1,149 @@
<template>
<view>
<div ref="chartDom" style="width: 100vw; height: 700rpx"></div>
</view>
</template>
<script setup>
import { watch, onMounted, reactive, ref } from "vue";
import * as echarts from "echarts";
import { newsInfoScore } from "@/api/newsInfo";
const chartDom = ref(null);
let myChart = null;
const data = reactive({});
const keys = ref([]);
const values = ref([]);
const props = defineProps({
data: {
type: Object,
default: () => {},
},
});
//
const initChart = () => {
myChart = echarts.init(chartDom.value);
const builderJson = {
all: 10887,
charts: {
map: 3237,
lines: 2164,
bar: 7561,
line: 7778,
pie: 7355,
scatter: 2405,
candlestick: 1842,
radar: 2090,
heatmap: 1762,
treemap: 1593,
graph: 2060,
boxplot: 1537,
parallel: 1908,
gauge: 2107,
funnel: 1692,
sankey: 1568,
},
components: {
geo: 2788,
title: 9575,
legend: 9400,
tooltip: 9466,
grid: 9266,
markPoint: 3419,
markLine: 2984,
timeline: 2739,
dataZoom: 2744,
visualMap: 2466,
toolbox: 3034,
polar: 1945,
},
ie: 9743,
};
const waterMarkText = "ECHARTS";
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// canvas.width = canvas.height = 100;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.globalAlpha = 0.08;
ctx.font = "20px Microsoft Yahei";
ctx.translate(50, 50);
ctx.rotate(-Math.PI / 4);
ctx.fillText(waterMarkText, 0, 0);
let option = {
backgroundColor: {
type: "pattern",
image: canvas,
repeat: "repeat",
},
tooltip: {},
title: [],
grid: [
{
top: 0,
width: "90%",
bottom: "0%",
left: 0,
containLabel: true,
},
],
xAxis: [
{
type: "value",
max: builderJson.all,
splitLine: {
show: false,
},
},
],
yAxis: [
{
type: "category",
data: Object.keys(builderJson.charts),
axisLabel: {
interval: 0,
rotate: 30,
},
splitLine: {
show: false,
},
},
],
series: [
{
type: "bar",
stack: "chart",
z: 3,
label: {
position: "right",
show: true,
},
data: Object.keys(builderJson.charts).map(function (key) {
return builderJson.charts[key];
}),
},
],
};
//使
myChart.setOption(option);
};
onMounted(async () => {
// await getData();
// initChart();
// setInterval(async () => {
// await getData();
// initChart();
// }, 5000);
initChart();
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,88 @@
<template>
<view>
<div ref="chartDom" style="width: 100vw; height: 700rpx"></div>
</view>
</template>
<script setup>
import { watch, onMounted, reactive, ref } from "vue";
import * as echarts from "echarts";
import { newsInfoScore } from "@/api/newsInfo";
const chartDom = ref(null);
let myChart = null;
const data = reactive({});
const keys = ref([]);
const values = ref([]);
const props = defineProps({
data: {
type: Object,
default: () => {},
},
});
//
const initChart = () => {
myChart = echarts.init(chartDom.value);
let option = {
dataset: {
source: [
["score", "amount", "product"],
[89.3, 58212, "Matcha Latte"],
[57.1, 78254, "Milk Tea"],
[74.4, 41032, "Cheese Cocoa"],
[50.1, 12755, "Cheese Brownie"],
[89.7, 20145, "Matcha Cocoa"],
[68.1, 79146, "Tea"],
[19.6, 91852, "Orange Juice"],
[10.6, 101852, "Lemon Juice"],
[32.7, 20112, "Walnut Brownie"],
],
},
grid: { containLabel: true },
xAxis: { name: "amount" },
yAxis: { type: "category" },
visualMap: {
orient: "horizontal",
left: "center",
min: 10,
max: 100,
text: ["High Score", "Low Score"],
// Map the score column to color
dimension: 0,
inRange: {
color: ["#65B581", "#FFCE34", "#FD665F"],
},
},
series: [
{
type: "bar",
encode: {
// Map the "amount" column to X axis.
x: "amount",
// Map the "product" column to Y axis
y: "product",
},
},
],
};
//使
myChart.setOption(option);
};
onMounted(async () => {
// await getData();
// initChart();
// setInterval(async () => {
// await getData();
// initChart();
// }, 5000);
initChart();
});
</script>
<style scoped lang="scss"></style>

View File

@ -6,9 +6,7 @@
<img :src="bannerImg" class="banner_bk" />
<view class="r_banner_title">
<img :src="bannerTitle" class="banner_title" />
<text>数据更新时间:{{
dayjs(new Date().getTime()).format("YYYY-MM-DD")
}}</text>
<text>数据更新时间:{{ dayjs(new Date().getTime()).format("YYYY-MM-DD") }}</text>
</view>
</view>
<!-- banner end -->
@ -60,31 +58,32 @@
<indexMenuTitle title="资讯评分分布区间"></indexMenuTitle>
<Line style="margin-top: 30rpx" :data="lineData"></Line>
<view style="
display: flex;
flex-direction: column;
text-align: center;
justify-content: center;
align-items: center;
padding-bottom: 30rpx;
">
<view style="display: flex; flex-direction: column; text-align: center; justify-content: center; align-items: center; padding-bottom: 30rpx">
<InfoSummary style="width: 85%" :count="newsNum"></InfoSummary>
</view>
</view>
<view style="background-color: white; margin-top: 40rpx">
<indexMenuTitle title="资讯头条榜 Top20"></indexMenuTitle>
<indexMenuTitle title="周热度统计"></indexMenuTitle>
<LineHol v-if="lineTabIndex === 0"></LineHol>
<LineHolYellow v-else-if="lineTabIndex === 2"></LineHolYellow>
<div style="display: flex; justify-content: center; margin-bottom: 20px; margin-top: 20px">
<div class="tabs">
<div :class="['tab', 'tab_left', { active: lineTabIndex === 0 }]" @click="handleTabClick(0)">申万行业</div>
<div :class="['tab', { active: lineTabIndex === 1 }]" @click="handleTabClick(1)">概念标签</div>
<div :class="['tab', 'tab_right', { active: lineTabIndex === 2 }]" @click="handleTabClick(2)">媒体来源</div>
</div>
</div>
</view>
<view style="background-color: white; margin-top: 40rpx">
<indexMenuTitle title="编辑精选 Top20"></indexMenuTitle>
<RankList :newsList="newsList"></RankList>
</view>
<view style="background-color: white; margin-top: 40rpx">
<view style="
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 30rpx;
height: 100rpx;
">
<view style="display: flex; justify-content: space-between; align-items: center; padding-right: 30rpx; height: 100rpx">
<indexMenuTitle title="热门行业池 Top10"></indexMenuTitle>
<view style="display: flex; gap: 3rpx">
<text class="view-all" @click="onViewAll(0)">查看全部</text>
@ -96,13 +95,7 @@
</view>
<view style="background-color: white; margin-top: 40rpx">
<view style="
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 30rpx;
height: 100rpx;
">
<view style="display: flex; justify-content: space-between; align-items: center; padding-right: 30rpx; height: 100rpx">
<indexMenuTitle title="风口概念池 Top10"></indexMenuTitle>
<view style="display: flex; gap: 3rpx">
<text class="view-all" @click="onViewAll(1)">查看全部</text>
@ -115,8 +108,11 @@
<view class="logout" @click="loginOut" v-if="Session.get('token')">退出登录</view>
<LoginPopup :show="LoginShow" @handlePopupClose="handlePopupClose"
@handlePopupSuccessCallback="handlePopupSuccessCallback" @handlePopupErrorCallback="handlePopupErrorCallback" />
<LoginPopup
:show="LoginShow"
@handlePopupClose="handlePopupClose"
@handlePopupSuccessCallback="handlePopupSuccessCallback"
@handlePopupErrorCallback="handlePopupErrorCallback" />
</view>
</template>
@ -132,21 +128,15 @@ import tagicon_2 from "@/assets/zixun/tagicon_2.png";
import LoginPopup from "@/components/loginPopup/index.vue";
import { Session } from "@/utils/storage";
import {
getindustryCount,
getConceptCount,
getTopNews,
getTopIndustry_d,
getTopConcept_d,
getNews_cnt_d,
newsInfoScore,
} from "@/api/newsInfo";
import { getindustryCount, getConceptCount, getTopNews, getTopIndustry_d, getTopConcept_d, getNews_cnt_d, newsInfoScore } from "@/api/newsInfo";
import countTo from "@/components/count-to/vue-countTo.vue";
import RankList from "@/components/RankList.vue"; //
import InfoSummary from "@/components/InfoSummary.vue"; //
import indexMenuTitle from "@/components/indexMenuTitle.vue"; //
import dayjs from "dayjs/esm/index";
import HotIndustryList from "@/components/HotIndustryList.vue"; //
import LineHol from "@/components/charts/LineHol.vue";
import LineHolYellow from "@/components/charts/LineHolYellow.vue";
const newsList = ref([]);
@ -218,9 +208,9 @@ function initData() {
getNews_cnt_dFn();
}
const type = ref();
function onViewAll(type) {
type.value = type;
const type = ref(null);
function onViewAll(type1) {
type.value = type1;
if (Session.get("token")) {
uni.navigateTo({
url: "/pages/realtimeInfo/rankDetail?type=" + type,
@ -260,6 +250,11 @@ function loginOut() {
window.location.reload();
}
const lineTabIndex = ref(0);
function handleTabClick(index) {
lineTabIndex.value = index;
}
onUnmounted(() => {
clearInterval(timer);
});
@ -400,4 +395,41 @@ onMounted(async () => {
margin-right: 20rpx;
margin-bottom: 20rpx;
}
.tabs {
display: flex;
align-items: center;
height: 30px;
border-radius: 10px;
.tab {
width: 100px;
height: 100%;
display: flex;
text-align: center;
justify-content: center;
align-items: center;
border: 1px solid #e7e7e7;
color: #000;
font-size: 12px;
}
.tab_left {
border-start-start-radius: 10px;
border-end-start-radius: 10px;
border-right: 0;
}
.tab_right {
border-start-end-radius: 10px;
border-end-end-radius: 10px;
border-left: 0;
}
.active {
border: 1px solid #007bff;
background-color: #007bff;
color: #fff;
}
}
</style>