Compare commits

...

3 Commits

Author SHA1 Message Date
625bd9e19b 数据概览静态 2026-01-20 16:13:22 +08:00
7aba9c4bb7 全局注册组件 2026-01-20 16:12:28 +08:00
328b8f0e2b 安装echart,首页完成图表 2026-01-16 17:07:12 +08:00
10 changed files with 348 additions and 5 deletions

View File

@ -10,6 +10,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"echarts": "^6.0.0",
"element-plus": "^2.13.1",
"vue": "^3.5.24",
"vue-router": "^4.6.4"

23
pnpm-lock.yaml generated
View File

@ -11,6 +11,9 @@ importers:
'@element-plus/icons-vue':
specifier: ^2.3.2
version: 2.3.2(vue@3.5.26(typescript@5.9.3))
echarts:
specifier: ^6.0.0
version: 6.0.0
element-plus:
specifier: ^2.13.1
version: 2.13.1(vue@3.5.26(typescript@5.9.3))
@ -698,6 +701,9 @@ packages:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
echarts@6.0.0:
resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==}
electron-to-chromium@1.5.267:
resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
@ -898,6 +904,9 @@ packages:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
tslib@2.3.0:
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
@ -1027,6 +1036,9 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
zrender@6.0.0:
resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==}
snapshots:
'@babel/code-frame@7.28.6':
@ -1639,6 +1651,11 @@ snapshots:
define-lazy-prop@3.0.0: {}
echarts@6.0.0:
dependencies:
tslib: 2.3.0
zrender: 6.0.0
electron-to-chromium@1.5.267: {}
element-plus@2.13.1(vue@3.5.26(typescript@5.9.3)):
@ -1848,6 +1865,8 @@ snapshots:
totalist@3.0.1: {}
tslib@2.3.0: {}
typescript@5.9.3: {}
undici-types@7.16.0: {}
@ -1961,3 +1980,7 @@ snapshots:
is-wsl: 3.1.0
yallist@3.1.1: {}
zrender@6.0.0:
dependencies:
tslib: 2.3.0

View File

@ -2,8 +2,6 @@
import { useRouter } from 'vue-router'
const { cut } = defineProps(["cut"])
const routerList = useRouter().options.routes[0]?.children
console.log(routerList);
</script>
<template>
<div class="side_bar" :style="{ width: cut ? '250px' : '80px' }">
@ -11,7 +9,7 @@ console.log(routerList);
<el-menu router :collapse="!cut" default-active="/overview" class="menu" background-color="#304156"
text-color="#bfcbd9" active-text-color="#409EFF">
<el-menu-item class="menu_item" v-for="route in routerList" :key="route.name" :index="route.path">
<i class="iconfont" :class="route.meta?.icon"></i>
<i :class="['iconfont', route.meta?.icon]"></i>
<span>{{ route.meta?.title }}</span>
</el-menu-item>
</el-menu>

View File

@ -18,6 +18,7 @@ body {
body,
ul,
h1,
h2,
h3,
h4,
p,
@ -103,6 +104,11 @@ ol {
line-height: 0;
clear: both;
}
.box_shadow {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 0 10px 1px #d7d7d7;
}
/* fade */
.fade-enter-active,
.fade-leave-active {

View File

@ -0,0 +1,34 @@
<script setup lang='ts'>
const props = defineProps<{
total: Number
}>()
</script>
<template>
<div class="table box_shadow">
<h3>
<slot name="header"></slot>
</h3>
<slot></slot>
<div class="page">
<el-pagination background :total="props.total" />
</div>
</div>
</template>
<style scoped>
.table {
margin-top: 20px;
h3 {
display: flex;
justify-content: space-between;
font-weight: 100;
padding: 20px 15px;
border-bottom: 1px solid #ddd;
}
.page {
padding: 20px 15px;
}
}
</style>

14
src/components/index.ts Normal file
View File

@ -0,0 +1,14 @@
// 导入所有组件
const modules = import.meta.glob("./**/*.vue", {
import: "default",
eager: true,
});
export default {
install(app: any) {
// 全局注册组件
Object.keys(modules).forEach((item) => {
const itemName = item.split("/")[1];
app.component(itemName, modules[item]);
});
},
};

View File

@ -0,0 +1,11 @@
// 获取全局配置项
import type { ComponentInternalInstance } from "vue";
import { getCurrentInstance } from "vue";
export default function useCurrentInstance() {
const { appContext } = getCurrentInstance() as ComponentInternalInstance;
const globalProperties = appContext.config.globalProperties;
return {
globalProperties,
};
}

View File

@ -7,11 +7,16 @@ import "element-plus/dist/index.css";
import zhCn from "element-plus/es/locale/lang/zh-cn";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import "@/assets/font/iconfont.css";
import * as echarts from "echarts";
import Components from "@/components";
const app = createApp(App);
// 全局挂载echarts
app.config.globalProperties.$echarts = echarts;
// 全局注册icon图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(router);
app.use(Components);
app.use(ElementPlus, { locale: zhCn }).mount("#app");

View File

@ -1,5 +1,252 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import useCurrentInstance from "@/hooks/useCurrentInstance";
const titleList = ref([
{ icon: 'icon-zongshu', count: 93, title: "总手术数" },
{ icon: 'icon-huanzheguanli', count: 93, title: "患者总数" },
{ icon: 'icon-yisheng', count: 3, title: "手术医生" },
{ icon: 'icon-shebeigailan', count: 93, title: "设备品牌" },
])
const tableData = ref([{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
}])
for (let index = 0; index < 10; index++) {
tableData.value.push({
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},)
}
const pieDom = ref<HTMLDivElement | null>(null)
const barDom = ref<HTMLDivElement | null>(null)
const rederPie = () => {
const { globalProperties } = useCurrentInstance()
const chart = globalProperties.$echarts.init(pieDom.value)
const option = {
title: [
{
subtext: '交通性',
left: '28%',
bottom: '5%',
textAlign: 'center',
subtextStyle: {
color: '#333',
fontSize: 18,
fontWeight: 'bold'
}
},
{
subtext: '梗阻性',
left: '75%',
bottom: '5%',
textAlign: 'center',
subtextStyle: {
color: '#333',
fontSize: 18,
fontWeight: 'bold'
}
}
],
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
left: '20px',
top: '20px',
orient: 'vertical',
},
color: ['#2c5aa0', '#4a90e2', '#5bc0de'],
series: [
{
name: '交通性',
type: 'pie',
radius: '45%',
center: ['30%', '50%'],
data: [
{ value: 30, name: '高压' },
{ value: 28, name: '正压' },
{ value: 26, name: '低压' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
},
{
name: '梗阻性',
type: 'pie',
radius: '45%',
center: ['75%', '50%'],
data: [
{ value: 30, name: '高压' },
{ value: 28, name: '正压' },
{ value: 26, name: '低压' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
chart.setOption(option)
}
const rederBar = () => {
const { globalProperties } = useCurrentInstance()
const chart = globalProperties.$echarts.init(barDom.value)
const option = {
tooltip: {
trigger: 'axis',
formatter: '{b} : {c} 台手术',
axisPointer: {
type: 'shadow',
shadowStyle: {
color: "rgba(200, 200, 200, .1)"
}
}
},
grid: {
left: '5%',
bottom: '5%',
containLabel: true
},
color: {
type: 'linear',
y2: 1,
colorStops: [{
offset: '0', color: '#3063b0' // 0%
}, {
offset: '1', color: '#519ef8' // 100%
}],
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
},
yAxis: {
type: 'value'
},
series: [
{
data: [120, 200, 150, 80, 70],
type: 'bar'
}
]
};
chart.setOption(option)
}
onMounted(() => {
rederPie()
rederBar()
})
</script>
<template>
<div class="data">
<h1>数据概览</h1>
<ul>
<li class="box_shadow" v-for="item in titleList">
<i :class="['iconfont', item.icon]"></i>
<div>
<h2>{{ item.count }}</h2>
<text>{{ item.title }}</text>
</div>
</li>
</ul>
<div class="chart">
<div class="box_shadow">
<h3>手术类型分布</h3>
<div ref="pieDom"></div>
</div>
<div class="box_shadow">
<h3>医生手术统计</h3>
<div ref="barDom"></div>
</div>
</div>
<TableBox :total="100">
<template #header>
最近手术记录 <el-button size="small" type="primary">查看全部</el-button>
</template>
<el-table stripe :data="tableData" height="480">
<el-table-column prop="date" label="日期" width="180" />
<el-table-column prop="name" label="姓名" width="180" />
<el-table-column prop="name" label="手术医生" width="180" />
<el-table-column prop="name" label="手术名称" />
<el-table-column prop="name" label="脑积水类型" width="180" />
<el-table-column prop="name" label="当前阀门压力" width="180" />
<el-table-column fixed="right" label="操作" align="center" width="180">
<template #default>
<el-button link type="primary" size="small">
查看详情
</el-button>
<el-button link type="primary" size="small">手术记录</el-button>
</template>
</el-table-column>
</el-table>
</TableBox>
</div>
</template>
</template>
<style scoped>
.data {
h3 {
font-weight: 100;
padding: 20px 15px;
border-bottom: 1px solid #ddd;
}
ul {
display: flex;
justify-content: space-between;
gap: 20px;
li {
display: flex;
flex: 1;
height: 120px;
padding: 20px;
align-self: center;
gap: 15px;
i {
font-size: 45px;
}
h2 {
font-size: 30px;
}
text {
color: #666;
}
}
}
.chart {
display: flex;
gap: 20px;
margin-top: 20px;
>div {
flex: 1;
height: 400px;
>div {
width: 100%;
height: 330px;
}
}
}
}
</style>

View File

@ -2,6 +2,10 @@
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"types": ["vite/client"],
/* Linting */