健康咨询平台大屏
This commit is contained in:
Generated
+19331
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -34,7 +34,7 @@
|
||||
"postcss-px2rem": "^0.3.0",
|
||||
"prettier": "^3.1.0",
|
||||
"qs": "^6.11.2",
|
||||
"vite": "^7.0.5",
|
||||
"swiper": "^12.0.2",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue3-seamless-scroll": "^2.0.1"
|
||||
},
|
||||
@@ -45,7 +45,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||
"@typescript-eslint/parser": "^6.15.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
||||
"ant-design-vue": "^3.2.20",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
@@ -56,7 +56,7 @@
|
||||
"jest": "^29.7.0",
|
||||
"less": "^4.2.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.5",
|
||||
"vite": "^5.4.20",
|
||||
"vite-plugin-resolve-externals": "^0.2.2",
|
||||
"vue-tsc": "^1.8.5"
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
+10
-2
@@ -19,13 +19,21 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import('/@/views/sys/login.vue'),
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: 'index',
|
||||
path: '/home2',
|
||||
name: 'index2',
|
||||
meta: {
|
||||
title: '副屏',
|
||||
},
|
||||
component: () => import('/@/views/indexSun.vue'),
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: 'index',
|
||||
meta: {
|
||||
title: '健康咨询',
|
||||
},
|
||||
component: () => import('../views/healthConsultation/HealthConsultationIndex.vue'),
|
||||
},
|
||||
{
|
||||
path: '/indexSon',
|
||||
name: 'indexSon',
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div class="sun-screen-box">
|
||||
<div class="sun-screen-title">
|
||||
<div class="sun-title-left">
|
||||
{{ dateInfo?.day }} {{ dateInfo?.times }}<span style="margin-left: 30px">{{ rightDate }}</span>
|
||||
</div>
|
||||
<div class="sun-title-center">健康咨询平台</div>
|
||||
<div class="sun-title-right">
|
||||
<!-- <a-button class="top-right-button" @click="toOther">健康监测大屏</a-button> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="sun-body-content">
|
||||
<div class="sun-body-content-top">
|
||||
<health-content-top />
|
||||
</div>
|
||||
<div class="sun-body-content-middle">
|
||||
<health-content-middle />
|
||||
</div>
|
||||
<div class="sun-body-content-bottom">
|
||||
<health-content-bottom-item1 class="item" />
|
||||
<health-content-bottom-item2 class="item" />
|
||||
<health-content-bottom-item3 class="item" />
|
||||
<health-content-bottom-item4 class="item" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import healthContentTop from './components/healthContentTop.vue';
|
||||
import healthContentMiddle from './components/healthContentMiddle.vue';
|
||||
import healthContentBottomItem1 from './components/healthContentBottomItem1.vue';
|
||||
import healthContentBottomItem2 from './components/healthContentBottomItem2.vue';
|
||||
import healthContentBottomItem3 from './components/healthContentBottomItem3.vue';
|
||||
import healthContentBottomItem4 from './components/healthContentBottomItem4.vue';
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { startTime } from '/@/utils/utils.ts';
|
||||
|
||||
interface DateInfo {
|
||||
day?: string;
|
||||
times?: string;
|
||||
}
|
||||
const dateInfo = ref<DateInfo>({});
|
||||
const rightDate = ref<string>('');
|
||||
const initDate = ref(startTime);
|
||||
|
||||
const getTime = () => {
|
||||
dateInfo.value = {
|
||||
day: dayjs().format('YYYY年MM月DD日'),
|
||||
times: dayjs().format('HH:mm:ss'),
|
||||
};
|
||||
};
|
||||
onMounted(() => {
|
||||
getTime();
|
||||
rightDate.value = '累计运行时间:' + (dayjs().diff(dayjs(initDate.value), 'days') + 1) + ' 天';
|
||||
});
|
||||
|
||||
watch(
|
||||
() => dateInfo.value.day,
|
||||
() => {
|
||||
rightDate.value = '累计运行时间:' + (dayjs().diff(dayjs(initDate.value), 'days') + 1) + ' 天';
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sun-screen-box {
|
||||
height: 100vh;
|
||||
letter-spacing: 2px;
|
||||
background: url('/@/assets/indexSun/bigBg.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.sun-screen-title {
|
||||
height: 87px;
|
||||
background: url('/@/assets/indexSun/bg.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
|
||||
.sun-title-left {
|
||||
font-family: MicrosoftYaHei-Bold;
|
||||
// font-size: 22px;
|
||||
font-size: clamp(14px, 1vw, 50px);
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
line-height: 60px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.sun-title-right {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
line-height: 50px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.sun-title-center {
|
||||
flex: 1;
|
||||
font-size: 58px;
|
||||
letter-spacing: 10px;
|
||||
// text-shadow: 2px 2px 5px rgba(118, 141, 219, 0.53);
|
||||
font-weight: 400;
|
||||
font-family: PangMenZhengDao;
|
||||
}
|
||||
}
|
||||
|
||||
.sun-body-content {
|
||||
height: calc(100% - 87px);
|
||||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
.sun-body-content-top {
|
||||
height: 10%;
|
||||
}
|
||||
.sun-body-content-middle {
|
||||
height: 55%;
|
||||
// padding: 0 20px 20px 20px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
// position: absolute;
|
||||
// left: 0;
|
||||
// top: 200px;
|
||||
}
|
||||
.sun-body-content-bottom {
|
||||
height: 35%;
|
||||
// padding: 0 20px 20px 20px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
// height: 300px;
|
||||
// position: absolute;
|
||||
// left: 0;
|
||||
// bottom: 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
display: flex;
|
||||
justify-content: space-between; /* 左右元素靠边,中间等间距 */
|
||||
gap: 16px;
|
||||
.item {
|
||||
flex: 1; /* 等分占位 */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { get } from '/@/api/request';
|
||||
|
||||
export enum Api {
|
||||
staticCount = '/health-consultation/api/consult/screen/queryStaticCount',
|
||||
docList = '/health-consultation/api/consult/screen/queryDocList',
|
||||
docCountStat = '/health-consultation/api/consult/screen/queryDocCountStat',
|
||||
deptCountStat = '/health-consultation/api/consult/screen/queryDeptCountStat',
|
||||
departmentCountStat = '/health-consultation/api/consult/screen/queryDepartmentCountStat',
|
||||
hospitalCountStat = '/health-consultation/api/consult/screen/queryHospitalCountStat',
|
||||
}
|
||||
|
||||
//咨询大屏人数统计查询
|
||||
export const getStaticCount = () => get(Api.staticCount);
|
||||
|
||||
//咨询大屏人数统计查询
|
||||
export const getDocList = () => get(Api.docList);
|
||||
|
||||
//医院专家数量统计
|
||||
export const getDocCountStat = () => get(Api.docCountStat);
|
||||
|
||||
//单位咨询数量统计
|
||||
export const getDeptCountStat = () => get(Api.deptCountStat);
|
||||
|
||||
//科室咨询数量统计
|
||||
export const getDepartmentCountStat = () => get(Api.departmentCountStat);
|
||||
|
||||
//医院咨询数量
|
||||
export const getHospitalCountStat = () => get(Api.hospitalCountStat);
|
||||
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="title">医院专家数量统计</div>
|
||||
<div ref="chartRef" class="chart"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onBeforeUnmount, ref } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { getDocCountStat } from '/@/views/healthConsultation/api/health';
|
||||
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
// const total = 2920;
|
||||
|
||||
// const data = [
|
||||
// { name: '新疆医科大学第一附属医院', value: 650 },
|
||||
// { name: '昌吉州宝石花医院', value: 422 },
|
||||
// { name: '新疆维吾尔自治区人民医院', value: 928 },
|
||||
// { name: '新克拉玛依市中心医院', value: 560 },
|
||||
// ];
|
||||
|
||||
const transformHospitalData = (hospitals: { docCount: number; hospitalName: string }[]) => {
|
||||
return hospitals.map((hospital) => ({
|
||||
name: hospital.hospitalName,
|
||||
value: hospital.docCount,
|
||||
}));
|
||||
};
|
||||
|
||||
const colors = ['#1385FA', '#FB466C', '#2AC79F', '#FEB101'];
|
||||
|
||||
function getData() {
|
||||
getDocCountStat()
|
||||
.then((res: any) => {
|
||||
if (res.code == 200) {
|
||||
const transformedData = transformHospitalData(res.result);
|
||||
initChart(transformedData);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
function getTotalDocCount(hospitals: { value: number; name: string }[]): number {
|
||||
return hospitals.reduce((total, hospital) => total + hospital.value, 0);
|
||||
}
|
||||
|
||||
const initChart = (value: any[]) => {
|
||||
if (!chartRef.value) return;
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
const option: echarts.EChartsOption = {
|
||||
// title: {
|
||||
// text: '医院专家数量统计',
|
||||
// left: 'center',
|
||||
// top: 10,
|
||||
// textStyle: {
|
||||
// color: '#fff',
|
||||
// fontSize: 18,
|
||||
// fontWeight: 'bold',
|
||||
// },
|
||||
// },
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}人 ({d}%)',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '专家人数',
|
||||
type: 'pie',
|
||||
radius: ['30%', '40%'],
|
||||
center: ['50%', '55%'],
|
||||
data: value,
|
||||
label: {
|
||||
show: true,
|
||||
color: '#fff',
|
||||
fontSize: 12,
|
||||
lineHeight: 20,
|
||||
formatter: (params: any) => {
|
||||
const name: string = params.name;
|
||||
const value: number = params.value;
|
||||
const maxLen = 6; // 超过 6 个字就换行
|
||||
let newName = '';
|
||||
|
||||
for (let i = 0; i < name.length; i += maxLen) {
|
||||
newName += name.substring(i, i + maxLen) + '\n';
|
||||
}
|
||||
|
||||
return `${newName}(${value}人)`;
|
||||
},
|
||||
},
|
||||
// itemStyle: {
|
||||
// borderRadius: 5,
|
||||
// borderColor: '#0f2c44',
|
||||
// borderWidth: 2,
|
||||
// },
|
||||
},
|
||||
],
|
||||
graphic: [
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '48%',
|
||||
style: {
|
||||
text: `总人数`,
|
||||
// textAlign: 'center',
|
||||
fill: '#fff',
|
||||
fontSize: 16,
|
||||
fontFamily: 'HarmonyOS Sans SC',
|
||||
// fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '56%', // 第二行位置
|
||||
style: {
|
||||
// text: `${total}`,
|
||||
text: `${getTotalDocCount(value)}`,
|
||||
// textAlign: 'center',
|
||||
fill: '#fff',
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: 'HarmonyOS Sans SC',
|
||||
},
|
||||
},
|
||||
],
|
||||
legend: [
|
||||
{
|
||||
orient: 'vertical',
|
||||
left: 20,
|
||||
top: 10,
|
||||
itemGap: 10,
|
||||
icon: 'path://M200 512 H307 a154,154 0 1,0 308,0 a154,154 0 1,0 -308,0 H824',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 12,
|
||||
rich: {
|
||||
name: {
|
||||
fontSize: 12,
|
||||
color: '#fff',
|
||||
align: 'left', // 左对齐
|
||||
// lineHeight: 20,
|
||||
fontFamily: 'HarmonyOS Sans SC',
|
||||
},
|
||||
// value: {
|
||||
// fontSize: 14,
|
||||
// color: '#aaa',
|
||||
// align: 'left', // 左对齐
|
||||
// lineHeight: 20,
|
||||
// },
|
||||
},
|
||||
},
|
||||
// data: [data[0].name, data[3].name],
|
||||
data:
|
||||
value && Array.isArray(value)
|
||||
? value
|
||||
.filter((_, index) => index % 2 === 0)
|
||||
.slice(0, 2)
|
||||
.map((item) => item.name || '')
|
||||
: ['', ''],
|
||||
// formatter: (name: string) => {
|
||||
// const item = data.find((d) => d.name === name);
|
||||
// return `{name|${name}}\n{value|${item?.value}人}`;
|
||||
// },
|
||||
},
|
||||
{
|
||||
orient: 'vertical',
|
||||
right: 20,
|
||||
top: 10,
|
||||
itemGap: 10,
|
||||
icon: 'circle',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 12,
|
||||
rich: {
|
||||
name: {
|
||||
fontSize: 12,
|
||||
color: '#fff',
|
||||
align: 'right', // 右对齐
|
||||
// lineHeight: 20,
|
||||
},
|
||||
// value: {
|
||||
// fontSize: 14,
|
||||
// color: '#aaa',
|
||||
// align: 'right', // 右对齐
|
||||
// lineHeight: 20,
|
||||
// },
|
||||
},
|
||||
},
|
||||
// data: [data[1].name, data[2].name],
|
||||
data:
|
||||
value && Array.isArray(value)
|
||||
? value
|
||||
.filter((_, index) => index % 2 === 1)
|
||||
.slice(0, 2)
|
||||
.map((item) => item.name || '')
|
||||
: ['', ''],
|
||||
// formatter: (name: string) => {
|
||||
// const item = data.find((d) => d.name === name);
|
||||
// return `{name|${name}}\n{value|${item?.value}人}`;
|
||||
// },
|
||||
},
|
||||
],
|
||||
color: colors,
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
window.addEventListener('resize', () => chartInstance?.resize());
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
// initChart();
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
chartInstance?.dispose();
|
||||
chartInstance = null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.chart-container {
|
||||
height: 100%;
|
||||
// width: calc(100% / 5);
|
||||
border: 1px solid #00ccff;
|
||||
border-radius: 14px;
|
||||
background: rgba(8, 75, 100, 0.2);
|
||||
.title {
|
||||
height: 40px;
|
||||
border-radius: 14px 14px 0 0;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
letter-spacing: 2px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
line-height: 40px;
|
||||
padding: 0 20px;
|
||||
background: linear-gradient(to right, rgba(8, 75, 100, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
|
||||
}
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="title">单位咨询数量统计</div>
|
||||
<div ref="chartRef" class="chart"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { getDeptCountStat } from '/@/views/healthConsultation/api/health';
|
||||
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
let chart: echarts.ECharts | null = null;
|
||||
|
||||
function getData() {
|
||||
getDeptCountStat()
|
||||
.then((res: any) => {
|
||||
if (res.code == 200) {
|
||||
// 提取数据
|
||||
const consultHelperCounts = res.result.map((item: any) => item.consultHelperCount);
|
||||
const consultDocCounts = res.result.map((item: any) => item.consultDocCount);
|
||||
const departNames = res.result.map((item: any) => item.departName);
|
||||
initChart(departNames, consultDocCounts, consultHelperCounts);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
const initChart = (departNames: string[], consultDocCounts: number[], consultHelperCounts: number[]) => {
|
||||
if (!chartRef.value) return;
|
||||
chart = echarts.init(chartRef.value);
|
||||
|
||||
const option: echarts.EChartsOption = {
|
||||
backgroundColor: 'transparent',
|
||||
// title: {
|
||||
// text: '单位咨询数量统计',
|
||||
// left: 'center',
|
||||
// top: 10,
|
||||
// textStyle: {
|
||||
// color: '#fff',
|
||||
// fontSize: 18,
|
||||
// },
|
||||
// },
|
||||
legend: {
|
||||
top: 10,
|
||||
textStyle: { color: '#fff' },
|
||||
itemWidth: 20,
|
||||
itemHeight: 16,
|
||||
itemGap: 30, // 默认 10,这里改成 30,让两段文字隔开一些
|
||||
// 圆环 icon
|
||||
// icon: 'path://M512 0a512 512 0 1 0 0 1024A512 512 0 0 0 512 0zm0 896a384 384 0 1 1 0-768 384 384 0 0 1 0 768z',
|
||||
icon: 'circle',
|
||||
data: ['员工咨询全科医生', '员工咨询专家'],
|
||||
},
|
||||
grid: {
|
||||
top: 70,
|
||||
left: 50,
|
||||
right: 30,
|
||||
bottom: 70,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: departNames,
|
||||
axisLine: { lineStyle: { color: '#3d5269' } },
|
||||
axisLabel: {
|
||||
color: '#fff',
|
||||
fontSize: 10,
|
||||
rotate: 30,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3d5269' } },
|
||||
splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } },
|
||||
axisLabel: { color: '#89DBFF' },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '员工咨询全科医生',
|
||||
type: 'bar',
|
||||
data: consultDocCounts,
|
||||
barWidth: 12, // 更细的柱子
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{ offset: 0, color: 'rgba(90,191,255,0.7)' }, // 中心透明
|
||||
{ offset: 0.5, color: 'rgba(30,166,253,0.4)' }, // 中间过渡
|
||||
{ offset: 1, color: 'rgba(30,166,253,0.7)' }, // 边缘实心
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '员工咨询专家',
|
||||
type: 'bar',
|
||||
data: consultHelperCounts,
|
||||
barWidth: 12, // 更细的柱子
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{ offset: 0, color: 'rgba(60,255,253,0.7)' }, // 中心透明
|
||||
{ offset: 0.5, color: 'rgba(60,255,253,0.4)' }, // 中间过渡
|
||||
{ offset: 1, color: 'rgba(48,223,221,0.7)' }, // 边缘实心
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
window.addEventListener('resize', () => chart?.resize());
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
// initChart();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
chart?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
height: 100%;
|
||||
/* width: calc(100% / 5); */
|
||||
border: 1px solid #00ccff;
|
||||
border-radius: 14px;
|
||||
background: rgba(8, 75, 100, 0.2);
|
||||
.title {
|
||||
height: 40px;
|
||||
border-radius: 14px 14px 0 0;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
letter-spacing: 2px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
line-height: 40px;
|
||||
padding: 0 20px;
|
||||
background: linear-gradient(to right, rgba(8, 75, 100, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
|
||||
}
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="title">科室咨询数量统计</div>
|
||||
<div ref="chartRef" class="chart"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { getDepartmentCountStat } from '/@/views/healthConsultation/api/health';
|
||||
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
let chart: echarts.ECharts | null = null;
|
||||
|
||||
function getData() {
|
||||
getDepartmentCountStat()
|
||||
.then((res: any) => {
|
||||
if (res.code == 200) {
|
||||
// 提取数据
|
||||
const consultVedioCount = res.result.map((item: any) => item.consultVedioCount);
|
||||
const consultImCount = res.result.map((item: any) => item.consultImCount);
|
||||
const departmentName = res.result.map((item: any) => item.departmentName);
|
||||
initChart(departmentName, consultImCount, consultVedioCount);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
const initChart = (departmentName: string[], consultImCount: number[], consultVedioCount: number[]) => {
|
||||
if (!chartRef.value) return;
|
||||
chart = echarts.init(chartRef.value);
|
||||
|
||||
const option: echarts.EChartsOption = {
|
||||
backgroundColor: 'transparent',
|
||||
// title: {
|
||||
// text: '科室咨询数量统计',
|
||||
// left: 'center',
|
||||
// top: 10,
|
||||
// textStyle: {
|
||||
// color: '#fff',
|
||||
// fontSize: 18,
|
||||
// },
|
||||
// },
|
||||
legend: {
|
||||
top: 10,
|
||||
textStyle: { color: '#fff' },
|
||||
itemWidth: 20,
|
||||
itemHeight: 16,
|
||||
itemGap: 30, // 默认 10,这里改成 30,让两段文字隔开一些
|
||||
// 圆环 icon
|
||||
// icon: 'path://M512 0a512 512 0 1 0 0 1024A512 512 0 0 0 512 0zm0 896a384 384 0 1 1 0-768 384 384 0 0 1 0 768z',
|
||||
icon: 'circle',
|
||||
data: ['图文', '视频'],
|
||||
},
|
||||
grid: {
|
||||
top: 70,
|
||||
left: 50,
|
||||
right: 30,
|
||||
bottom: 70,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: departmentName,
|
||||
axisLine: { lineStyle: { color: '#3d5269' } },
|
||||
axisLabel: {
|
||||
color: '#fff',
|
||||
fontSize: 10,
|
||||
rotate: 30,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3d5269' } },
|
||||
splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } },
|
||||
axisLabel: { color: '#89DBFF' },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '图文',
|
||||
type: 'bar',
|
||||
data: consultImCount,
|
||||
barWidth: 12, // 更细的柱子
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{ offset: 0, color: 'rgba(90,191,255,0.7)' }, // 中心透明
|
||||
{ offset: 0.5, color: 'rgba(30,166,253,0.4)' }, // 中间过渡
|
||||
{ offset: 1, color: 'rgba(30,166,253,0.7)' }, // 边缘实心
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '视频',
|
||||
type: 'bar',
|
||||
data: consultVedioCount,
|
||||
barWidth: 12, // 更细的柱子
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{ offset: 0, color: 'rgba(60,255,253,0.7)' }, // 中心透明
|
||||
{ offset: 0.5, color: 'rgba(60,255,253,0.4)' }, // 中间过渡
|
||||
{ offset: 1, color: 'rgba(48,223,221,0.7)' }, // 边缘实心
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
window.addEventListener('resize', () => chart?.resize());
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
// initChart();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
chart?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
height: 100%;
|
||||
/* width: calc(100% / 5); */
|
||||
border: 1px solid #00ccff;
|
||||
border-radius: 14px;
|
||||
background: rgba(8, 75, 100, 0.2);
|
||||
.title {
|
||||
height: 40px;
|
||||
border-radius: 14px 14px 0 0;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
letter-spacing: 2px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
line-height: 40px;
|
||||
padding: 0 20px;
|
||||
background: linear-gradient(to right, rgba(8, 75, 100, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
|
||||
}
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="title">医院咨询数量统计</div>
|
||||
<div ref="chartRef" class="chart"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { getHospitalCountStat } from '/@/views/healthConsultation/api/health';
|
||||
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
let chart: echarts.ECharts | null = null;
|
||||
|
||||
// 格式化医院名称,如果太长则在中间添加换行符
|
||||
const formatHospitalName = (hospitalName: string, maxLength: number = 8) => {
|
||||
if (hospitalName.length > maxLength) {
|
||||
const midIndex = Math.floor(hospitalName.length / 2);
|
||||
// 寻找最佳断行点,避免在词中间断开
|
||||
let breakIndex = midIndex;
|
||||
for (let i = midIndex; i < hospitalName.length; i++) {
|
||||
const char = hospitalName.charAt(i);
|
||||
// 如果是中文字符或空格,可以在其后断开
|
||||
if (/[\u4e00-\u9fa5\s]/.test(char)) {
|
||||
breakIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 如果还没找到合适的断行点,则在中间断开
|
||||
if (breakIndex === midIndex) {
|
||||
for (let i = midIndex - 1; i > 0; i--) {
|
||||
const char = hospitalName.charAt(i);
|
||||
if (/[\u4e00-\u9fa5\s]/.test(char)) {
|
||||
breakIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hospitalName.slice(0, breakIndex) + '\n' + hospitalName.slice(breakIndex);
|
||||
}
|
||||
return hospitalName;
|
||||
};
|
||||
|
||||
function getData() {
|
||||
getHospitalCountStat()
|
||||
.then((res: any) => {
|
||||
if (res.code == 200) {
|
||||
console.log(res);
|
||||
// 提取数据
|
||||
const consultVedioCount = res.result.map((item: any) => item.consultVedioCount);
|
||||
const consultImCount = res.result.map((item: any) => item.consultImCount);
|
||||
// 格式化医院名称
|
||||
const hospitalName = res.result.map((item: any) => formatHospitalName(item.hospitalName));
|
||||
initChart(hospitalName, consultImCount, consultVedioCount);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
const initChart = (hospitalName: string[], consultImCount: number[], consultVedioCount: number[]) => {
|
||||
if (!chartRef.value) return;
|
||||
chart = echarts.init(chartRef.value);
|
||||
|
||||
const option: echarts.EChartsOption = {
|
||||
backgroundColor: 'transparent',
|
||||
// title: {
|
||||
// text: '单位咨询数量统计',
|
||||
// left: 'center',
|
||||
// top: 10,
|
||||
// textStyle: {
|
||||
// color: '#fff',
|
||||
// fontSize: 18,
|
||||
// },
|
||||
// },
|
||||
legend: {
|
||||
top: 10,
|
||||
textStyle: { color: '#fff' },
|
||||
itemWidth: 20,
|
||||
itemHeight: 16,
|
||||
itemGap: 30, // 默认 10,这里改成 30,让两段文字隔开一些
|
||||
// 圆环 icon
|
||||
// icon: 'path://M512 0a512 512 0 1 0 0 1024A512 512 0 0 0 512 0zm0 896a384 384 0 1 1 0-768 384 384 0 0 1 0 768z',
|
||||
icon: 'circle',
|
||||
data: ['图文', '视频'],
|
||||
},
|
||||
grid: {
|
||||
top: 70,
|
||||
left: 50,
|
||||
right: 30,
|
||||
bottom: 70,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: hospitalName,
|
||||
axisLine: { lineStyle: { color: '#3d5269' } },
|
||||
axisLabel: {
|
||||
color: '#fff',
|
||||
fontSize: 10,
|
||||
// rotate: 30,
|
||||
interval: 0, // 显示全部标签
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3d5269' } },
|
||||
splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } },
|
||||
axisLabel: { color: '#89DBFF' },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '图文',
|
||||
type: 'bar',
|
||||
data: consultImCount,
|
||||
barWidth: 12, // 更细的柱子
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{ offset: 0, color: 'rgba(90,191,255,0.7)' }, // 中心透明
|
||||
{ offset: 0.5, color: 'rgba(30,166,253,0.4)' }, // 中间过渡
|
||||
{ offset: 1, color: 'rgba(30,166,253,0.7)' }, // 边缘实心
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '视频',
|
||||
type: 'bar',
|
||||
data: consultVedioCount,
|
||||
barWidth: 12, // 更细的柱子
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{ offset: 0, color: 'rgba(60,255,253,0.7)' }, // 中心透明
|
||||
{ offset: 0.5, color: 'rgba(60,255,253,0.4)' }, // 中间过渡
|
||||
{ offset: 1, color: 'rgba(48,223,221,0.7)' }, // 边缘实心
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
window.addEventListener('resize', () => chart?.resize());
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
// initChart();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
chart?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
height: 100%;
|
||||
/* width: calc(100% / 5); */
|
||||
border: 1px solid #00ccff;
|
||||
border-radius: 14px;
|
||||
background: rgba(8, 75, 100, 0.2);
|
||||
.title {
|
||||
height: 40px;
|
||||
border-radius: 14px 14px 0 0;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
letter-spacing: 2px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
line-height: 40px;
|
||||
padding: 0 20px;
|
||||
background: linear-gradient(to right, rgba(8, 75, 100, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
|
||||
}
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<swiper :slides-per-view="1" :space-between="10" :loop="true" :autoplay="{ delay: 10000, disableOnInteraction: false }" class="mySwiper">
|
||||
<swiper-slide v-for="(page, index) in pages" :key="index">
|
||||
<div class="grid-container">
|
||||
<div v-for="item in page" :key="item.id" class="item">
|
||||
<div class="item-left">
|
||||
<a-image class="item-img" :preview="false" height="100%" :src="getFileUrl(item.photo)" />
|
||||
</div>
|
||||
<div class="item-right">
|
||||
<div class="name">
|
||||
<span>{{ item.doctorName }}</span>
|
||||
<span class="position">({{ item.doctorJob_dictText }})</span>
|
||||
</div>
|
||||
<div class="hospital">{{ item.resourceName }} | {{ item.departmentName }}</div>
|
||||
<div class="experience">
|
||||
<span class="label">经历:</span>
|
||||
{{ item.experience ? item.experience : '暂无' }}
|
||||
</div>
|
||||
<div class="goodAt">
|
||||
<span class="label">擅长:</span>
|
||||
{{ item.goodAt ? item.goodAt : '暂无' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/navigation';
|
||||
import SwiperCore from 'swiper';
|
||||
import { Autoplay, Navigation } from 'swiper/modules'; // 修改此处导入路径
|
||||
// import defaultPng from '/@/assets/health/default.png';
|
||||
import { Image as AImage } from 'ant-design-vue';
|
||||
|
||||
import { getDocList } from '/@/views/healthConsultation/api/health';
|
||||
|
||||
function getFileUrl(path?: string) {
|
||||
const GATEWAY_URL = import.meta.env.VITE_GLOB_API_URL;
|
||||
if (!path) return '/@/assets/health/default.png'; // 使用默认图片
|
||||
return `${GATEWAY_URL}/file/show/${path}`;
|
||||
}
|
||||
|
||||
// 安装 Swiper 模块
|
||||
SwiperCore.use([Autoplay, Navigation]);
|
||||
interface Doctor {
|
||||
id: string;
|
||||
photo?: string;
|
||||
doctorName: string;
|
||||
doctorJob_dictText: string;
|
||||
resourceName: string;
|
||||
departmentName: string;
|
||||
experience: string;
|
||||
goodAt: string;
|
||||
}
|
||||
|
||||
// 将每 6 个元素分为一页
|
||||
const items = ref<Doctor[]>([]);
|
||||
const pages = computed(() => {
|
||||
const result: Doctor[][] = [];
|
||||
for (let i = 0; i < items.value.length; i += 6) {
|
||||
result.push(items.value.slice(i, i + 6));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
function getData() {
|
||||
getDocList()
|
||||
.then((res: any) => {
|
||||
if (res.code == 200) {
|
||||
items.value = res.result;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.mySwiper {
|
||||
height: 100%; /* 设置整个轮播高度 */
|
||||
padding-bottom: 16px;
|
||||
box-sizing: border-box;
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr); /* 每行平分Swiper高度 */
|
||||
gap: 10px; /* item 间距 */
|
||||
height: 100%; /* 占满Swiper高度 */
|
||||
// padding: 10px; /* 可选:轮播边缘留空 */
|
||||
// box-sizing: border-box;
|
||||
.item {
|
||||
width: 100%;
|
||||
background: rgba(8, 75, 100, 0.2);
|
||||
border: 1px solid #00ccff;
|
||||
border-radius: 14px;
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
transition: transform 0.3s;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
.item-left {
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
margin-right: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.item-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
}
|
||||
.item-right {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// gap: clamp(10px, 1vw, 40px);
|
||||
// gap: 8px;
|
||||
.name {
|
||||
// height: 10%;
|
||||
color: #ffc000;
|
||||
font-size: 23px;
|
||||
font-weight: bold;
|
||||
font-family: HarmonyOS Sans SC;
|
||||
letter-spacing: 1px;
|
||||
line-height: 34px;
|
||||
.position {
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.hospital {
|
||||
// height: 10%;
|
||||
font-family: PingFang SC;
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
// font-size: clamp(14px, 1vw, 50px);
|
||||
color: #fff;
|
||||
line-height: 32px;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.experience {
|
||||
// height: 40%;
|
||||
// height: 57px;
|
||||
// line-height: 22px;
|
||||
font-family: PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3; /* 显示3行 */
|
||||
line-clamp: 3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 8px;
|
||||
.label {
|
||||
color: #82a9ef;
|
||||
}
|
||||
}
|
||||
.goodAt {
|
||||
// height: 40%;
|
||||
font-family: PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3; /* 显示3行 */
|
||||
line-clamp: 3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
letter-spacing: 1px;
|
||||
.label {
|
||||
color: #82a9ef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="health-content-top">
|
||||
<div class="count-item">
|
||||
<div class="text">入驻专家</div>
|
||||
<div class="num">{{ countData.docCount }}</div>
|
||||
</div>
|
||||
<div class="count-item">
|
||||
<div class="text">累计咨询</div>
|
||||
<div class="num">{{ countData.consultCount }}</div>
|
||||
</div>
|
||||
<div class="count-item">
|
||||
<div class="text">咨询人数</div>
|
||||
<div class="num">{{ countData.consultPersonCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { getStaticCount } from '/@/views/healthConsultation/api/health';
|
||||
|
||||
interface CountData {
|
||||
docCount: number;
|
||||
consultCount: number; // 假设这是“累计咨询”的字段名,请根据实际接口调整
|
||||
consultPersonCount: number;
|
||||
}
|
||||
const countData = ref<CountData>({
|
||||
docCount: 0,
|
||||
consultCount: 0,
|
||||
consultPersonCount: 0,
|
||||
});
|
||||
function getData() {
|
||||
getStaticCount()
|
||||
.then((res: any) => {
|
||||
if (res.code == 200) {
|
||||
countData.value = res.result;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.health-content-top {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// position: absolute;
|
||||
// left: 50%;
|
||||
// top: 101px;
|
||||
// transform: translateX(-50%);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 10px;
|
||||
gap: 20px;
|
||||
|
||||
> div {
|
||||
position: relative;
|
||||
|
||||
// &:before {
|
||||
// position: absolute;
|
||||
// top: 10px;
|
||||
// left: 10px;
|
||||
// // background: #0a2043;
|
||||
// content: '';
|
||||
// width: 280px;
|
||||
// height: 62px;
|
||||
// z-index: -1;
|
||||
// }
|
||||
}
|
||||
|
||||
> :nth-child(1) {
|
||||
background: url('/@/assets/health/healthBlue.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
> :nth-child(2) {
|
||||
background: url('/@/assets/health/healthGreen.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
> :nth-child(3) {
|
||||
background: url('/@/assets/health/healthYellow.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.count-item {
|
||||
width: calc(100% / 5);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
color: #fff;
|
||||
//background: url('/@/assets/images/footer-num.png') no-repeat;
|
||||
//border-radius: 14px;
|
||||
|
||||
.num {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
font-family: HarmonyOS Sans SC;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
font-family: HarmonyOS Sans SC;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user