删除无关代码
This commit is contained in:
parent
8d09254180
commit
770c78733d
|
@ -22,7 +22,7 @@ service.interceptors.request.use(
|
|||
|
||||
|
||||
|
||||
config.headers['token'] = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NDE2OTUyNjQsInVzZXJuYW1lIjoiYWRtaW4ifQ.gF2Y2-P5xunAWxDL68nczO8kH9l5qpucO3PuiC5I25w`
|
||||
config.headers['token'] = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NDE3ODAxNDMsInVzZXJuYW1lIjoiYWRtaW4ifQ._jKmdu1T-zpf_qSWRTBtovJ51v2ONC6CGF-60MLJOOE`
|
||||
|
||||
|
||||
// 根据请求方法和数据类型动态设置 Content-Type
|
||||
|
|
|
@ -1,286 +0,0 @@
|
|||
<template>
|
||||
<div class="health-monitor">
|
||||
<el-row :gutter="20">
|
||||
<!-- 实时数据卡片 -->
|
||||
<el-col :span="8" v-for="(item, index) in healthData" :key="index">
|
||||
<el-card class="health-card" :class="item.status">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ item.title }}</span>
|
||||
<el-tag :type="item.status">{{ item.statusText }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-content">
|
||||
<div class="value-container">
|
||||
<span class="value">{{ item.value }}</span>
|
||||
<span class="unit">{{ item.unit }}</span>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<v-chart class="chart" :option="getChartOption(item)" autoresize />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 3D人体模型 -->
|
||||
<el-row class="body-model">
|
||||
<el-col :span="24">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>人体健康状态</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="model-container" ref="modelContainer"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 数据录入表单 -->
|
||||
<el-dialog v-model="dialogVisible" title="健康数据录入" width="50%">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="血压(mmHg)">
|
||||
<el-input-number v-model="form.systolic" :min="0" :max="200" />
|
||||
/
|
||||
<el-input-number v-model="form.diastolic" :min="0" :max="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="心率(次/分)">
|
||||
<el-input-number v-model="form.heartRate" :min="0" :max="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="体温(℃)">
|
||||
<el-input-number v-model="form.temperature" :min="35" :max="42" :precision="1" :step="0.1" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-button class="add-btn" type="primary" size="large" circle @click="dialogVisible = true">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { LineChart } from 'echarts/charts'
|
||||
import { GridComponent, TooltipComponent } from 'echarts/components'
|
||||
import VChart from 'vue-echarts'
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
|
||||
use([CanvasRenderer, LineChart, GridComponent, TooltipComponent])
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const form = ref({
|
||||
systolic: 120,
|
||||
diastolic: 80,
|
||||
heartRate: 75,
|
||||
temperature: 36.5
|
||||
})
|
||||
|
||||
const healthData = ref([
|
||||
{
|
||||
title: '血压',
|
||||
value: '120/80',
|
||||
unit: 'mmHg',
|
||||
status: 'success',
|
||||
statusText: '正常',
|
||||
history: [110, 115, 120, 118, 122, 120]
|
||||
},
|
||||
{
|
||||
title: '心率',
|
||||
value: '75',
|
||||
unit: '次/分',
|
||||
status: 'success',
|
||||
statusText: '正常',
|
||||
history: [72, 75, 73, 76, 75, 75]
|
||||
},
|
||||
{
|
||||
title: '体温',
|
||||
value: '36.5',
|
||||
unit: '℃',
|
||||
status: 'success',
|
||||
statusText: '正常',
|
||||
history: [36.4, 36.5, 36.6, 36.5, 36.4, 36.5]
|
||||
}
|
||||
])
|
||||
|
||||
const getChartOption = (item) => {
|
||||
return {
|
||||
grid: {
|
||||
top: 10,
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
left: 30
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: item.history,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 3D模型相关
|
||||
const modelContainer = ref(null)
|
||||
let scene, camera, renderer, controls
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initThreeJS()
|
||||
})
|
||||
})
|
||||
|
||||
const initThreeJS = () => {
|
||||
scene = new THREE.Scene()
|
||||
camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
modelContainer.value.clientWidth / modelContainer.value.clientHeight,
|
||||
0.1,
|
||||
1000
|
||||
)
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||
renderer.setSize(modelContainer.value.clientWidth, modelContainer.value.clientHeight)
|
||||
modelContainer.value.appendChild(renderer.domElement)
|
||||
|
||||
// 添加环境光
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
|
||||
scene.add(ambientLight)
|
||||
|
||||
// 添加平行光
|
||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
|
||||
directionalLight.position.set(0, 1, 0)
|
||||
scene.add(directionalLight)
|
||||
|
||||
camera.position.z = 5
|
||||
|
||||
controls = new OrbitControls(camera, renderer.domElement)
|
||||
controls.enableDamping = true
|
||||
|
||||
// 这里可以加载人体模型
|
||||
// const loader = new GLTFLoader()
|
||||
// loader.load('path/to/human-model.glb', (gltf) => {
|
||||
// scene.add(gltf.scene)
|
||||
// })
|
||||
|
||||
animate()
|
||||
}
|
||||
|
||||
const animate = () => {
|
||||
requestAnimationFrame(animate)
|
||||
controls.update()
|
||||
renderer.render(scene, camera)
|
||||
}
|
||||
|
||||
const submitForm = () => {
|
||||
// 处理表单提交
|
||||
dialogVisible.value = false
|
||||
// 更新数据
|
||||
healthData.value[0].value = `${form.value.systolic}/${form.value.diastolic}`
|
||||
healthData.value[1].value = form.value.heartRate.toString()
|
||||
healthData.value[2].value = form.value.temperature.toString()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.health-monitor {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.health-card {
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.health-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.value-container {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 0.9em;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.model-container {
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
position: fixed;
|
||||
right: 50px;
|
||||
bottom: 50px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.success {
|
||||
border-top: 3px solid #67C23A;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-top: 3px solid #E6A23C;
|
||||
}
|
||||
|
||||
.danger {
|
||||
border-top: 3px solid #F56C6C;
|
||||
}
|
||||
</style>
|
|
@ -1,426 +0,0 @@
|
|||
<template>
|
||||
<div class="health-news">
|
||||
<!-- 顶部轮播 -->
|
||||
<el-card class="carousel-card">
|
||||
<el-carousel height="300px" :interval="4000" type="card">
|
||||
<el-carousel-item v-for="item in carouselData" :key="item.id">
|
||||
<div class="carousel-content" :style="{ backgroundImage: `url(${item.image})` }">
|
||||
<div class="carousel-info">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<p>{{ item.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-card>
|
||||
|
||||
<!-- 分类标签 -->
|
||||
<div class="category-container">
|
||||
<el-tabs v-model="activeCategory" @tab-click="handleCategoryChange">
|
||||
<el-tab-pane v-for="category in categories" :key="category.value" :label="category.label" :name="category.value">
|
||||
<!-- 文章列表 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<div class="article-list">
|
||||
<el-card v-for="article in filteredArticles" :key="article.id" class="article-card">
|
||||
<div class="article-content">
|
||||
<div class="article-image" v-if="article.image">
|
||||
<el-image :src="article.image" fit="cover" />
|
||||
</div>
|
||||
<div class="article-info">
|
||||
<h3 class="article-title" @click="showArticleDetail(article)">
|
||||
{{ article.title }}
|
||||
</h3>
|
||||
<p class="article-desc">{{ article.description }}</p>
|
||||
<div class="article-meta">
|
||||
<span>
|
||||
<el-icon><Calendar /></el-icon>
|
||||
{{ article.date }}
|
||||
</span>
|
||||
<span>
|
||||
<el-icon><View /></el-icon>
|
||||
{{ article.views }}
|
||||
</span>
|
||||
<span>
|
||||
<el-icon><Star /></el-icon>
|
||||
{{ article.likes }}
|
||||
</span>
|
||||
<el-tag size="small" :type="article.type">{{ article.category }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[6, 12, 24, 36]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧边栏 -->
|
||||
<el-col :span="8">
|
||||
<!-- 健康小贴士 -->
|
||||
<el-card class="tips-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>健康小贴士</span>
|
||||
<el-button text>更多</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div v-for="tip in healthTips" :key="tip.id" class="tip-item">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
<span>{{ tip.content }}</span>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 热门文章 -->
|
||||
<el-card class="hot-articles">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>热门文章</span>
|
||||
<el-button text>更多</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="article in hotArticles"
|
||||
:key="article.id"
|
||||
:timestamp="article.date"
|
||||
placement="top"
|
||||
:type="article.type"
|
||||
>
|
||||
<el-card class="hot-article-card">
|
||||
<h4>{{ article.title }}</h4>
|
||||
<p class="hot-article-meta">
|
||||
<el-icon><View /></el-icon> {{ article.views }}
|
||||
<el-icon><Star /></el-icon> {{ article.likes }}
|
||||
</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 文章详情对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="currentArticle.title"
|
||||
width="70%"
|
||||
class="article-dialog"
|
||||
>
|
||||
<div class="article-detail">
|
||||
<div class="article-header">
|
||||
<el-image v-if="currentArticle.image" :src="currentArticle.image" fit="cover" />
|
||||
<div class="article-info">
|
||||
<p class="article-meta">
|
||||
<span><el-icon><Calendar /></el-icon> {{ currentArticle.date }}</span>
|
||||
<span><el-icon><View /></el-icon> {{ currentArticle.views }}</span>
|
||||
<span><el-icon><Star /></el-icon> {{ currentArticle.likes }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="article-content" v-html="currentArticle.content"></div>
|
||||
<div class="article-actions">
|
||||
<el-button type="primary" @click="likeArticle">
|
||||
<el-icon><Star /></el-icon> 点赞
|
||||
</el-button>
|
||||
<el-button type="success" @click="shareArticle">
|
||||
<el-icon><Share /></el-icon> 分享
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import {
|
||||
Calendar,
|
||||
View,
|
||||
Star,
|
||||
InfoFilled,
|
||||
Share
|
||||
} from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// 轮播数据
|
||||
const carouselData = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: '健康生活方式指南',
|
||||
description: '科学饮食,规律运动,健康生活',
|
||||
image: 'https://via.placeholder.com/800x400'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '春季养生专题',
|
||||
description: '春季养生要点,助你轻松调养',
|
||||
image: 'https://via.placeholder.com/800x400'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '居家健康指南',
|
||||
description: '在家也能享受专业健康服务',
|
||||
image: 'https://via.placeholder.com/800x400'
|
||||
}
|
||||
])
|
||||
|
||||
// 分类数据
|
||||
const categories = ref([
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '养生保健', value: 'health' },
|
||||
{ label: '疾病预防', value: 'prevention' },
|
||||
{ label: '饮食营养', value: 'nutrition' },
|
||||
{ label: '运动健身', value: 'fitness' }
|
||||
])
|
||||
|
||||
const activeCategory = ref('all')
|
||||
|
||||
// 文章列表数据
|
||||
const articles = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: '每天一个苹果,医生远离我',
|
||||
description: '关于苹果的营养价值和健康功效的深入解析...',
|
||||
image: 'https://via.placeholder.com/300x200',
|
||||
date: '2024-03-20',
|
||||
views: 1234,
|
||||
likes: 88,
|
||||
category: '饮食营养',
|
||||
type: 'success',
|
||||
content: '详细的文章内容...'
|
||||
},
|
||||
// ... 更多文章数据
|
||||
])
|
||||
|
||||
// 健康小贴士
|
||||
const healthTips = ref([
|
||||
{
|
||||
id: 1,
|
||||
content: '每天喝够8杯水,保持身体水分充足'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
content: '保持良好作息,每天保证7-8小时睡眠'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
content: '适量运动,每周至少运动3次,每次30分钟'
|
||||
}
|
||||
])
|
||||
|
||||
// 热门文章
|
||||
const hotArticles = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: '2024年最新健康饮食指南',
|
||||
date: '2024-03-19',
|
||||
views: 2345,
|
||||
likes: 156,
|
||||
type: 'primary'
|
||||
},
|
||||
// ... 更多热门文章
|
||||
])
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(6)
|
||||
const total = ref(100)
|
||||
|
||||
// 文章详情相关
|
||||
const dialogVisible = ref(false)
|
||||
const currentArticle = ref({})
|
||||
|
||||
// 计算属性
|
||||
const filteredArticles = computed(() => {
|
||||
if (activeCategory.value === 'all') {
|
||||
return articles.value
|
||||
}
|
||||
return articles.value.filter(article => article.category === activeCategory.value)
|
||||
})
|
||||
|
||||
// 方法定义
|
||||
const handleCategoryChange = (tab) => {
|
||||
console.log('切换分类:', tab.props.name)
|
||||
}
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val
|
||||
}
|
||||
|
||||
const showArticleDetail = (article) => {
|
||||
currentArticle.value = article
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const likeArticle = () => {
|
||||
currentArticle.value.likes++
|
||||
ElMessage.success('点赞成功')
|
||||
}
|
||||
|
||||
const shareArticle = () => {
|
||||
ElMessage.success('分享功能开发中...')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.health-news {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.carousel-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.carousel-content {
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.carousel-info {
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
|
||||
color: white;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.carousel-info h3 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.category-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.article-card {
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.article-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.article-content {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.article-image {
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.article-image .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.article-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
margin: 0 0 10px;
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.article-desc {
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.article-meta span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.tips-card,
|
||||
.hot-articles {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tip-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.hot-article-card {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.hot-article-meta {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
color: #999;
|
||||
margin: 5px 0 0;
|
||||
}
|
||||
|
||||
.article-dialog :deep(.el-dialog__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.article-detail {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.article-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.article-header .el-image {
|
||||
width: 100%;
|
||||
max-height: 400px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.article-actions {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
|
@ -1,539 +0,0 @@
|
|||
<template>
|
||||
<div class="health-shop">
|
||||
<!-- 顶部搜索和筛选 -->
|
||||
<el-card class="filter-card">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="搜索商品..."
|
||||
prefix-icon="Search"
|
||||
clearable
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-radio-group v-model="category" size="large">
|
||||
<el-radio-button v-for="cat in categories" :key="cat.value" :label="cat.value">
|
||||
{{ cat.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-select v-model="sortBy" placeholder="排序方式" style="width: 100%">
|
||||
<el-option label="综合排序" value="default" />
|
||||
<el-option label="销量优先" value="sales" />
|
||||
<el-option label="价格从低到高" value="priceAsc" />
|
||||
<el-option label="价格从高到低" value="priceDesc" />
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<!-- 商品展示区 -->
|
||||
<el-row :gutter="20" class="product-container">
|
||||
<el-col :span="6" v-for="product in filteredProducts" :key="product.id">
|
||||
<el-card class="product-card" :body-style="{ padding: '0px' }">
|
||||
<div class="product-image">
|
||||
<el-image :src="product.image" fit="cover">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon><Picture /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="product-tags" v-if="product.tags && product.tags.length">
|
||||
<el-tag v-for="tag in product.tags" :key="tag" size="small" :type="getTagType(tag)">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3 class="product-title" @click="showProductDetail(product)">{{ product.name }}</h3>
|
||||
<div class="product-price">
|
||||
<span class="current-price">¥{{ product.price }}</span>
|
||||
<span class="original-price" v-if="product.originalPrice">¥{{ product.originalPrice }}</span>
|
||||
</div>
|
||||
<div class="product-meta">
|
||||
<span class="sales">月销 {{ product.sales }}+</span>
|
||||
<el-rate v-model="product.rating" disabled text-color="#ff9900" />
|
||||
</div>
|
||||
<div class="product-actions">
|
||||
<el-button type="primary" @click="addToCart(product)">加入购物车</el-button>
|
||||
<el-button type="danger" @click="buyNow(product)">立即购买</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 购物车抽屉 -->
|
||||
<el-drawer
|
||||
v-model="cartDrawer"
|
||||
title="购物车"
|
||||
direction="rtl"
|
||||
size="30%"
|
||||
>
|
||||
<div class="cart-content">
|
||||
<el-empty v-if="!cartItems.length" description="购物车是空的" />
|
||||
<template v-else>
|
||||
<div class="cart-items">
|
||||
<el-card v-for="item in cartItems" :key="item.id" class="cart-item">
|
||||
<div class="cart-item-content">
|
||||
<el-image :src="item.image" class="cart-item-image" />
|
||||
<div class="cart-item-info">
|
||||
<h4>{{ item.name }}</h4>
|
||||
<div class="cart-item-price">¥{{ item.price }}</div>
|
||||
<el-input-number
|
||||
v-model="item.quantity"
|
||||
:min="1"
|
||||
:max="99"
|
||||
size="small"
|
||||
@change="updateCart"
|
||||
/>
|
||||
</div>
|
||||
<el-button
|
||||
type="danger"
|
||||
circle
|
||||
class="remove-btn"
|
||||
@click="removeFromCart(item)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="cart-footer">
|
||||
<div class="cart-total">
|
||||
总计: <span class="total-price">¥{{ totalPrice }}</span>
|
||||
</div>
|
||||
<el-button type="primary" size="large" @click="checkout">
|
||||
结算 ({{ totalItems }}件)
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 商品详情对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="currentProduct.name"
|
||||
width="70%"
|
||||
class="product-dialog"
|
||||
>
|
||||
<div class="product-detail">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-carousel trigger="click" height="400px">
|
||||
<el-carousel-item v-for="img in currentProduct.images" :key="img">
|
||||
<el-image :src="img" fit="contain" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-info">
|
||||
<div class="detail-price">
|
||||
<span class="price">¥{{ currentProduct.price }}</span>
|
||||
<span class="original" v-if="currentProduct.originalPrice">
|
||||
原价: ¥{{ currentProduct.originalPrice }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-meta">
|
||||
<div class="meta-item">
|
||||
<span class="label">销量:</span>
|
||||
<span class="value">{{ currentProduct.sales }}+</span>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="label">评分:</span>
|
||||
<el-rate v-model="currentProduct.rating" disabled show-score />
|
||||
</div>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="detail-desc">{{ currentProduct.description }}</div>
|
||||
<div class="detail-actions">
|
||||
<el-input-number v-model="purchaseQuantity" :min="1" :max="99" />
|
||||
<el-button type="primary" size="large" @click="addToCart(currentProduct, purchaseQuantity)">
|
||||
加入购物车
|
||||
</el-button>
|
||||
<el-button type="danger" size="large" @click="buyNow(currentProduct, purchaseQuantity)">
|
||||
立即购买
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { Search, Picture, Delete } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
// 分类数据
|
||||
const categories = ref([
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '营养保健', value: 'nutrition' },
|
||||
{ label: '医疗器械', value: 'medical' },
|
||||
{ label: '健康食品', value: 'food' },
|
||||
{ label: '运动装备', value: 'sports' }
|
||||
])
|
||||
|
||||
// 筛选相关
|
||||
const searchQuery = ref('')
|
||||
const category = ref('all')
|
||||
const sortBy = ref('default')
|
||||
|
||||
// 商品数据
|
||||
const products = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '复合维生素片',
|
||||
price: 128,
|
||||
originalPrice: 168,
|
||||
description: '提供每日所需的基本维生素和矿物质...',
|
||||
image: 'https://via.placeholder.com/300x300',
|
||||
images: [
|
||||
'https://via.placeholder.com/800x800',
|
||||
'https://via.placeholder.com/800x800',
|
||||
'https://via.placeholder.com/800x800'
|
||||
],
|
||||
sales: 1000,
|
||||
rating: 4.5,
|
||||
category: 'nutrition',
|
||||
tags: ['热销', '限时特惠']
|
||||
},
|
||||
// ... 更多商品数据
|
||||
])
|
||||
|
||||
// 购物车相关
|
||||
const cartDrawer = ref(false)
|
||||
const cartItems = ref([])
|
||||
|
||||
// 商品详情相关
|
||||
const dialogVisible = ref(false)
|
||||
const currentProduct = ref({})
|
||||
const purchaseQuantity = ref(1)
|
||||
|
||||
// 计算属性
|
||||
const filteredProducts = computed(() => {
|
||||
let result = products.value
|
||||
|
||||
// 分类筛选
|
||||
if (category.value !== 'all') {
|
||||
result = result.filter(p => p.category === category.value)
|
||||
}
|
||||
|
||||
// 搜索筛选
|
||||
if (searchQuery.value) {
|
||||
const query = searchQuery.value.toLowerCase()
|
||||
result = result.filter(p =>
|
||||
p.name.toLowerCase().includes(query) ||
|
||||
p.description.toLowerCase().includes(query)
|
||||
)
|
||||
}
|
||||
|
||||
// 排序
|
||||
switch (sortBy.value) {
|
||||
case 'sales':
|
||||
result = [...result].sort((a, b) => b.sales - a.sales)
|
||||
break
|
||||
case 'priceAsc':
|
||||
result = [...result].sort((a, b) => a.price - b.price)
|
||||
break
|
||||
case 'priceDesc':
|
||||
result = [...result].sort((a, b) => b.price - a.price)
|
||||
break
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const totalPrice = computed(() => {
|
||||
return cartItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
|
||||
})
|
||||
|
||||
const totalItems = computed(() => {
|
||||
return cartItems.value.reduce((sum, item) => sum + item.quantity, 0)
|
||||
})
|
||||
|
||||
// 方法定义
|
||||
const getTagType = (tag) => {
|
||||
const types = {
|
||||
'热销': 'danger',
|
||||
'限时特惠': 'warning',
|
||||
'新品': 'success'
|
||||
}
|
||||
return types[tag] || 'info'
|
||||
}
|
||||
|
||||
const showProductDetail = (product) => {
|
||||
currentProduct.value = product
|
||||
purchaseQuantity.value = 1
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const addToCart = (product, quantity = 1) => {
|
||||
const existingItem = cartItems.value.find(item => item.id === product.id)
|
||||
if (existingItem) {
|
||||
existingItem.quantity += quantity
|
||||
} else {
|
||||
cartItems.value.push({
|
||||
...product,
|
||||
quantity
|
||||
})
|
||||
}
|
||||
ElMessage.success('已添加到购物车')
|
||||
cartDrawer.value = true
|
||||
}
|
||||
|
||||
const removeFromCart = (item) => {
|
||||
const index = cartItems.value.indexOf(item)
|
||||
if (index > -1) {
|
||||
cartItems.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const updateCart = () => {
|
||||
// 更新购物车总价等信息
|
||||
}
|
||||
|
||||
const buyNow = (product, quantity = 1) => {
|
||||
ElMessageBox.confirm(
|
||||
`确认购买 ${product.name} ${quantity}件?`,
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
}
|
||||
).then(() => {
|
||||
ElMessage.success('购买成功!')
|
||||
})
|
||||
}
|
||||
|
||||
const checkout = () => {
|
||||
if (cartItems.value.length === 0) {
|
||||
ElMessage.warning('购物车是空的')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(
|
||||
`确认结算 ${totalItems.value} 件商品,总计 ¥${totalPrice.value}?`,
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
}
|
||||
).then(() => {
|
||||
cartItems.value = []
|
||||
cartDrawer.value = false
|
||||
ElMessage.success('购买成功!')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.health-shop {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.product-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.product-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.product-image {
|
||||
position: relative;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.product-image .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.product-tags {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.product-title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.current-price {
|
||||
font-size: 20px;
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.product-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.sales {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.product-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.cart-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cart-items {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.cart-item-content {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cart-item-image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.cart-item-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cart-item-info h4 {
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
|
||||
.cart-item-price {
|
||||
color: #f56c6c;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.cart-footer {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.cart-total {
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.total-price {
|
||||
color: #f56c6c;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.detail-info {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.detail-price {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.detail-price .price {
|
||||
font-size: 24px;
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.detail-price .original {
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.meta-item .label {
|
||||
color: #666;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.detail-desc {
|
||||
margin: 20px 0;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.detail-actions {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.image-slot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
</style>
|
|
@ -1,388 +0,0 @@
|
|||
<template>
|
||||
<div class="health-warning">
|
||||
<!-- 预警概览 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" v-for="(item, index) in warningStats" :key="index">
|
||||
<el-card class="warning-card" :class="item.level">
|
||||
<div class="warning-content">
|
||||
<el-badge :value="item.count" :type="item.type">
|
||||
<el-icon class="warning-icon"><component :is="item.icon" /></el-icon>
|
||||
</el-badge>
|
||||
<div class="warning-info">
|
||||
<div class="warning-title">{{ item.title }}</div>
|
||||
<div class="warning-desc">{{ item.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 预警规则设置 -->
|
||||
<el-card class="rule-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>预警规则设置</span>
|
||||
<el-button type="primary" @click="addRule">
|
||||
<el-icon><Plus /></el-icon>添加规则
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table :data="rules" style="width: 100%">
|
||||
<el-table-column prop="name" label="规则名称" />
|
||||
<el-table-column prop="type" label="监测指标">
|
||||
<template #default="scope">
|
||||
<el-tag>{{ scope.row.type }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="condition" label="触发条件" />
|
||||
<el-table-column prop="level" label="预警等级">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.level">{{ getLevelText(scope.row.level) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button-group>
|
||||
<el-button type="primary" link @click="editRule(scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" link @click="deleteRule(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 预警历史 -->
|
||||
<el-card class="history-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>预警历史</span>
|
||||
<el-radio-group v-model="timeFilter" size="small">
|
||||
<el-radio-button label="today">今天</el-radio-button>
|
||||
<el-radio-button label="week">本周</el-radio-button>
|
||||
<el-radio-button label="month">本月</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(activity, index) in warningHistory"
|
||||
:key="index"
|
||||
:type="activity.type"
|
||||
:color="getWarningColor(activity.level)"
|
||||
:timestamp="activity.timestamp"
|
||||
:hollow="activity.hollow"
|
||||
>
|
||||
<el-card class="timeline-card">
|
||||
<h4>{{ activity.title }}</h4>
|
||||
<p>{{ activity.content }}</p>
|
||||
<div class="timeline-footer">
|
||||
<el-tag size="small" :type="activity.level">
|
||||
{{ getLevelText(activity.level) }}
|
||||
</el-tag>
|
||||
<el-button type="primary" link size="small" @click="handleWarning(activity)">
|
||||
处理
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-card>
|
||||
|
||||
<!-- 规则编辑对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="isEdit ? '编辑规则' : '新增规则'"
|
||||
width="50%"
|
||||
>
|
||||
<el-form :model="ruleForm" label-width="100px">
|
||||
<el-form-item label="规则名称">
|
||||
<el-input v-model="ruleForm.name" placeholder="请输入规则名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="监测指标">
|
||||
<el-select v-model="ruleForm.type" placeholder="请选择监测指标">
|
||||
<el-option label="血压" value="bloodPressure" />
|
||||
<el-option label="心率" value="heartRate" />
|
||||
<el-option label="体温" value="temperature" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="触发条件">
|
||||
<el-input v-model="ruleForm.condition" placeholder="请输入触发条件" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预警等级">
|
||||
<el-select v-model="ruleForm.level" placeholder="请选择预警等级">
|
||||
<el-option label="一般" value="info" />
|
||||
<el-option label="警告" value="warning" />
|
||||
<el-option label="严重" value="danger" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveRule">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Warning, Bell, Plus, Notification, CircleCheck } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
// 预警统计数据
|
||||
const warningStats = ref([
|
||||
{
|
||||
title: '严重预警',
|
||||
count: 3,
|
||||
type: 'danger',
|
||||
level: 'danger',
|
||||
icon: 'Warning',
|
||||
description: '需要立即处理'
|
||||
},
|
||||
{
|
||||
title: '一般预警',
|
||||
count: 8,
|
||||
type: 'warning',
|
||||
level: 'warning',
|
||||
icon: 'Bell',
|
||||
description: '需要关注'
|
||||
},
|
||||
{
|
||||
title: '已处理',
|
||||
count: 25,
|
||||
type: 'info',
|
||||
level: 'info',
|
||||
icon: 'CircleCheck',
|
||||
description: '本周已处理'
|
||||
},
|
||||
{
|
||||
title: '预警规则',
|
||||
count: 12,
|
||||
type: 'primary',
|
||||
level: 'primary',
|
||||
icon: 'Notification',
|
||||
description: '当前生效规则'
|
||||
}
|
||||
])
|
||||
|
||||
// 预警规则数据
|
||||
const rules = ref([
|
||||
{
|
||||
name: '高血压预警',
|
||||
type: '血压',
|
||||
condition: '收缩压 > 140 或 舒张压 > 90',
|
||||
level: 'danger',
|
||||
status: 1
|
||||
},
|
||||
{
|
||||
name: '心率异常预警',
|
||||
type: '心率',
|
||||
condition: '心率 < 60 或 心率 > 100',
|
||||
level: 'warning',
|
||||
status: 1
|
||||
}
|
||||
])
|
||||
|
||||
// 预警历史数据
|
||||
const timeFilter = ref('today')
|
||||
const warningHistory = ref([
|
||||
{
|
||||
timestamp: '2024-03-20 14:30',
|
||||
title: '血压异常预警',
|
||||
content: '检测到血压值 150/95,超出正常范围',
|
||||
level: 'danger',
|
||||
type: 'danger',
|
||||
hollow: false
|
||||
},
|
||||
{
|
||||
timestamp: '2024-03-20 10:20',
|
||||
title: '心率异常预警',
|
||||
content: '检测到心率值 102,超出正常范围',
|
||||
level: 'warning',
|
||||
type: 'warning',
|
||||
hollow: false
|
||||
}
|
||||
])
|
||||
|
||||
// 规则表单相关
|
||||
const dialogVisible = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const ruleForm = ref({
|
||||
name: '',
|
||||
type: '',
|
||||
condition: '',
|
||||
level: ''
|
||||
})
|
||||
|
||||
// 方法定义
|
||||
const getLevelText = (level) => {
|
||||
const texts = {
|
||||
danger: '严重',
|
||||
warning: '警告',
|
||||
info: '一般'
|
||||
}
|
||||
return texts[level] || level
|
||||
}
|
||||
|
||||
const getWarningColor = (level) => {
|
||||
const colors = {
|
||||
danger: '#F56C6C',
|
||||
warning: '#E6A23C',
|
||||
info: '#909399'
|
||||
}
|
||||
return colors[level] || '#409EFF'
|
||||
}
|
||||
|
||||
const addRule = () => {
|
||||
isEdit.value = false
|
||||
ruleForm.value = {
|
||||
name: '',
|
||||
type: '',
|
||||
condition: '',
|
||||
level: ''
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const editRule = (row) => {
|
||||
isEdit.value = true
|
||||
ruleForm.value = { ...row }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const deleteRule = (row) => {
|
||||
ElMessageBox.confirm(
|
||||
'确定要删除该预警规则吗?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
const index = rules.value.indexOf(row)
|
||||
rules.value.splice(index, 1)
|
||||
ElMessage.success('删除成功')
|
||||
})
|
||||
}
|
||||
|
||||
const saveRule = () => {
|
||||
if (isEdit.value) {
|
||||
const index = rules.value.findIndex(r => r.name === ruleForm.value.name)
|
||||
if (index !== -1) {
|
||||
rules.value[index] = { ...ruleForm.value }
|
||||
}
|
||||
} else {
|
||||
rules.value.push({ ...ruleForm.value, status: 1 })
|
||||
}
|
||||
dialogVisible.value = false
|
||||
ElMessage.success(isEdit.value ? '修改成功' : '添加成功')
|
||||
}
|
||||
|
||||
const handleStatusChange = (row) => {
|
||||
ElMessage.success(`${row.name}已${row.status ? '启用' : '停用'}`)
|
||||
}
|
||||
|
||||
const handleWarning = (activity) => {
|
||||
ElMessage.success(`正在处理: ${activity.title}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.health-warning {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.warning-card {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.warning-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 2.5em;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.warning-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.warning-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.warning-desc {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.rule-card,
|
||||
.history-card {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.timeline-card {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.timeline-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.danger {
|
||||
border-left: 4px solid var(--el-color-danger);
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-left: 4px solid var(--el-color-warning);
|
||||
}
|
||||
|
||||
.info {
|
||||
border-left: 4px solid var(--el-color-info);
|
||||
}
|
||||
|
||||
.primary {
|
||||
border-left: 4px solid var(--el-color-primary);
|
||||
}
|
||||
</style>
|
|
@ -106,9 +106,9 @@
|
|||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
QuestionFilled, School, DocumentChecked, DataAnalysis,
|
||||
Reading, Edit, User, Star
|
||||
import {
|
||||
QuestionFilled, School, DocumentChecked, DataAnalysis,
|
||||
Reading, Edit, User, Star
|
||||
} from '@element-plus/icons-vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
@ -139,7 +139,7 @@ const popularCourses = ref([
|
|||
title: 'C++面向对象编程精讲',
|
||||
category: 'cpp',
|
||||
categoryName: 'C++编程',
|
||||
coverImg: '/src/assets/images/course-covers/cpp.jpg',
|
||||
coverImg: 'https://p5.img.cctvpic.com/photoworkspace/contentimg/2023/03/30/2023033011303020756.jpg',
|
||||
studentCount: 1234,
|
||||
rating: 4.8,
|
||||
price: 0
|
||||
|
@ -149,28 +149,21 @@ const popularCourses = ref([
|
|||
title: '保密法律法规学习',
|
||||
category: 'law',
|
||||
categoryName: '法律法规',
|
||||
coverImg: '/src/assets/images/course-covers/law.jpg',
|
||||
coverImg: 'https://p5.img.cctvpic.com/photoworkspace/contentimg/2023/03/30/2023033011303020756.jpg',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '深度学习在气象云图识别中的应用',
|
||||
category: 'test',
|
||||
categoryName: '测试',
|
||||
coverImg: '/src/assets/images/course-covers/weather.jpg',
|
||||
coverImg: 'https://p5.img.cctvpic.com/photoworkspace/contentimg/2023/03/30/2023033011303020756.jpg',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '天气雷达与云图识别基础',
|
||||
category: 'test',
|
||||
categoryName: '测试',
|
||||
coverImg: '/src/assets/images/course-covers/radar.jpg',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '气象卫星云图分析与天气预测',
|
||||
category: 'test',
|
||||
categoryName: '测试',
|
||||
coverImg: '/src/assets/images/course-covers/satellite.jpg',
|
||||
coverImg: 'https://p5.img.cctvpic.com/photoworkspace/contentimg/2023/03/30/2023033011303020756.jpg',
|
||||
}
|
||||
])
|
||||
|
||||
|
@ -266,20 +259,20 @@ onMounted(() => {
|
|||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
|
||||
|
||||
.line {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
animation: fadeInUp 0.8s forwards;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
|
||||
|
||||
&:first-child {
|
||||
animation-delay: 0.2s;
|
||||
background: linear-gradient(to right, #60a5fa, #34d399);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
&:last-child {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
@ -291,13 +284,13 @@ onMounted(() => {
|
|||
display: inline-block;
|
||||
font-size: 20px;
|
||||
opacity: 0.9;
|
||||
|
||||
|
||||
.typing-text {
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.8s 1s forwards;
|
||||
}
|
||||
|
||||
|
||||
.cursor {
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
|
@ -324,12 +317,12 @@ onMounted(() => {
|
|||
font-size: 16px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s;
|
||||
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
@ -343,7 +336,7 @@ onMounted(() => {
|
|||
background-color: #f8fafc;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
@ -353,11 +346,11 @@ onMounted(() => {
|
|||
height: 1px;
|
||||
background: linear-gradient(to right, transparent, rgba(37, 99, 235, 0.1), transparent);
|
||||
}
|
||||
|
||||
|
||||
.section-header {
|
||||
text-align: center;
|
||||
margin-bottom: 60px;
|
||||
|
||||
|
||||
.section-title {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
|
@ -368,7 +361,7 @@ onMounted(() => {
|
|||
opacity: 0;
|
||||
animation: fadeInUp 0.8s forwards;
|
||||
}
|
||||
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
|
@ -386,20 +379,20 @@ onMounted(() => {
|
|||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
||||
.feature-icon {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
|
||||
.feature-bg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.feature-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
|
@ -410,27 +403,27 @@ onMounted(() => {
|
|||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
|
||||
.el-icon {
|
||||
font-size: 36px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
p {
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
.feature-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -447,11 +440,11 @@ onMounted(() => {
|
|||
.popular-courses {
|
||||
padding: 100px 0;
|
||||
background-color: white;
|
||||
|
||||
|
||||
.section-header {
|
||||
text-align: center;
|
||||
margin-bottom: 60px;
|
||||
|
||||
|
||||
.section-title {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
|
@ -462,7 +455,7 @@ onMounted(() => {
|
|||
opacity: 0;
|
||||
animation: fadeInUp 0.8s forwards;
|
||||
}
|
||||
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
|
@ -479,28 +472,28 @@ onMounted(() => {
|
|||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
margin-bottom: 30px;
|
||||
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
||||
.course-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.course-image {
|
||||
position: relative;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
.course-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -514,7 +507,7 @@ onMounted(() => {
|
|||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
.course-badge {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
|
@ -524,16 +517,16 @@ onMounted(() => {
|
|||
font-size: 12px;
|
||||
color: white;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
|
||||
|
||||
&.cpp {
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.course-content {
|
||||
padding: 20px;
|
||||
|
||||
|
||||
.course-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 16px;
|
||||
|
@ -544,34 +537,34 @@ onMounted(() => {
|
|||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
|
||||
.course-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.course-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.course-price {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #f43f5e;
|
||||
|
||||
|
||||
&.free {
|
||||
color: #10b981;
|
||||
}
|
||||
|
@ -594,18 +587,18 @@ onMounted(() => {
|
|||
:global(.introjs-tooltip) {
|
||||
background-color: #4080ff;
|
||||
color: white;
|
||||
|
||||
|
||||
.introjs-tooltip-title {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.introjs-button {
|
||||
background-color: white;
|
||||
color: #4080ff;
|
||||
border: none;
|
||||
text-shadow: none;
|
||||
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
@ -654,22 +647,22 @@ onMounted(() => {
|
|||
@media (max-width: 768px) {
|
||||
.home .banner {
|
||||
padding: 60px 0;
|
||||
|
||||
|
||||
.banner-content {
|
||||
.title-wrapper {
|
||||
.animated-title {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.banner-actions {
|
||||
flex-direction: column;
|
||||
padding: 0 20px;
|
||||
|
||||
|
||||
.action-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -1,374 +0,0 @@
|
|||
<template>
|
||||
<div class="statistics">
|
||||
<!-- 数据概览卡片 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" v-for="(item, index) in overviewData" :key="index">
|
||||
<el-card class="overview-card" :class="item.trend">
|
||||
<div class="overview-content">
|
||||
<div class="overview-icon">
|
||||
<el-icon><component :is="item.icon" /></el-icon>
|
||||
</div>
|
||||
<div class="overview-info">
|
||||
<div class="overview-title">{{ item.title }}</div>
|
||||
<div class="overview-value">{{ item.value }}</div>
|
||||
<div class="overview-trend">
|
||||
较上周
|
||||
<span :class="item.trend">{{ item.percentage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 趋势图表 -->
|
||||
<el-row :gutter="20" class="chart-row">
|
||||
<el-col :span="16">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>健康指标趋势分析</span>
|
||||
<el-radio-group v-model="timeRange" size="small">
|
||||
<el-radio-button label="week">周</el-radio-button>
|
||||
<el-radio-button label="month">月</el-radio-button>
|
||||
<el-radio-button label="year">年</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
<v-chart class="trend-chart" :option="trendOption" autoresize />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>健康状态分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<v-chart class="pie-chart" :option="pieOption" autoresize />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 详细数据表格 -->
|
||||
<el-card class="table-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>历史记录</span>
|
||||
<div class="header-actions">
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="搜索..."
|
||||
prefix-icon="Search"
|
||||
style="width: 200px"
|
||||
/>
|
||||
<el-button type="primary" @click="exportData">
|
||||
<el-icon><Download /></el-icon>导出数据
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="filteredTableData" style="width: 100%" height="400">
|
||||
<el-table-column prop="date" label="日期" sortable />
|
||||
<el-table-column prop="bloodPressure" label="血压" sortable />
|
||||
<el-table-column prop="heartRate" label="心率" sortable />
|
||||
<el-table-column prop="temperature" label="体温" sortable />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)">
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="totalItems"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { LineChart, PieChart } from 'echarts/charts'
|
||||
import {
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
TitleComponent
|
||||
} from 'echarts/components'
|
||||
import VChart from 'vue-echarts'
|
||||
import {
|
||||
TrendCharts,
|
||||
Histogram,
|
||||
DataLine,
|
||||
Timer,
|
||||
Search,
|
||||
Download
|
||||
} from '@element-plus/icons-vue'
|
||||
|
||||
use([
|
||||
CanvasRenderer,
|
||||
LineChart,
|
||||
PieChart,
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
TitleComponent
|
||||
])
|
||||
|
||||
// 数据概览
|
||||
const overviewData = ref([
|
||||
{
|
||||
title: '平均血压',
|
||||
value: '120/80',
|
||||
percentage: '+2.5%',
|
||||
trend: 'up',
|
||||
icon: 'TrendCharts'
|
||||
},
|
||||
{
|
||||
title: '平均心率',
|
||||
value: '75',
|
||||
percentage: '-1.2%',
|
||||
trend: 'down',
|
||||
icon: 'Histogram'
|
||||
},
|
||||
{
|
||||
title: '平均体温',
|
||||
value: '36.5',
|
||||
percentage: '0%',
|
||||
trend: 'stable',
|
||||
icon: 'DataLine'
|
||||
},
|
||||
{
|
||||
title: '记录次数',
|
||||
value: '28',
|
||||
percentage: '+15%',
|
||||
trend: 'up',
|
||||
icon: 'Timer'
|
||||
}
|
||||
])
|
||||
|
||||
// 时间范围选择
|
||||
const timeRange = ref('week')
|
||||
|
||||
// 趋势图配置
|
||||
const trendOption = computed(() => ({
|
||||
title: {
|
||||
text: '健康指标变化趋势'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['血压', '心率', '体温']
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '血压/心率',
|
||||
min: 0,
|
||||
max: 200
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '体温',
|
||||
min: 35,
|
||||
max: 42
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '血压',
|
||||
type: 'line',
|
||||
data: [120, 122, 118, 121, 119, 120, 118]
|
||||
},
|
||||
{
|
||||
name: '心率',
|
||||
type: 'line',
|
||||
data: [75, 73, 77, 76, 74, 75, 72]
|
||||
},
|
||||
{
|
||||
name: '体温',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: [36.5, 36.6, 36.4, 36.5, 36.7, 36.5, 36.4]
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
||||
// 饼图配置
|
||||
const pieOption = ref({
|
||||
title: {
|
||||
text: '健康状态分布'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: [
|
||||
{ value: 70, name: '正常' },
|
||||
{ value: 20, name: '轻微异常' },
|
||||
{ value: 10, name: '需要关注' }
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([
|
||||
{
|
||||
date: '2024-03-20',
|
||||
bloodPressure: '120/80',
|
||||
heartRate: 75,
|
||||
temperature: 36.5,
|
||||
status: '正常'
|
||||
},
|
||||
// ... 更多数据
|
||||
])
|
||||
|
||||
// 表格分页
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const totalItems = ref(100)
|
||||
const searchQuery = ref('')
|
||||
|
||||
const filteredTableData = computed(() => {
|
||||
return tableData.value.filter(data => {
|
||||
return Object.values(data).some(value =>
|
||||
value.toString().toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val
|
||||
}
|
||||
|
||||
const getStatusType = (status) => {
|
||||
const types = {
|
||||
'正常': 'success',
|
||||
'轻微异常': 'warning',
|
||||
'需要关注': 'danger'
|
||||
}
|
||||
return types[status] || 'info'
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
// 实现导出功能
|
||||
console.log('导出数据')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.statistics {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.overview-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.overview-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.overview-icon {
|
||||
font-size: 2.5em;
|
||||
margin-right: 15px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.overview-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.overview-title {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.overview-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.overview-trend {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.up {
|
||||
color: #67C23A;
|
||||
}
|
||||
|
||||
.down {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
.stable {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.chart-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.trend-chart {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.pie-chart {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
|
@ -78,12 +78,12 @@
|
|||
<div class="course-list">
|
||||
<div class="container">
|
||||
<el-row :gutter="24">
|
||||
<el-col
|
||||
v-for="course in filteredCourses"
|
||||
:key="course.id"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="8"
|
||||
<el-col
|
||||
v-for="course in filteredCourses"
|
||||
:key="course.id"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="8"
|
||||
:lg="6"
|
||||
>
|
||||
<div class="course-card" @click="viewCourse(course.id)">
|
||||
|
@ -233,7 +233,7 @@ const filteredCourses = computed(() => {
|
|||
}
|
||||
if (searchQuery.value) {
|
||||
const query = searchQuery.value.toLowerCase()
|
||||
result = result.filter(course =>
|
||||
result = result.filter(course =>
|
||||
course.title.toLowerCase().includes(query) ||
|
||||
course.description.toLowerCase().includes(query)
|
||||
)
|
||||
|
@ -315,7 +315,7 @@ const handleCurrentChange = (val) => {
|
|||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
|
||||
&:hover, &:focus {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ const handleCurrentChange = (val) => {
|
|||
.el-tag {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
@ -419,7 +419,7 @@ const handleCurrentChange = (val) => {
|
|||
transition: all 0.3s;
|
||||
margin-bottom: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.1);
|
||||
|
@ -654,4 +654,4 @@ const handleCurrentChange = (val) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
>
|
||||
<template #filter-content>
|
||||
|
||||
<el-select v-model="listQuery.params.openType" class="filter-item" placeholder="开放类型" clearable>
|
||||
<el-select v-model="listQuery.params.openType" class="filter-item" placeholder="开放类型" style="width: 207px;" clearable>
|
||||
<el-option
|
||||
v-for="item in openTypes"
|
||||
:key="item.value"
|
||||
|
@ -171,3 +171,12 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.filter-item {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
Loading…
Reference in New Issue