1.使用vite构建vue3
1.1.执行命令
npm create vite
输入项目名字 xixin-doctor-frontend
选择框架-vue
选择js标准-JavaScript
1.2.项目初始化安装
进入项目目录,安装默认依赖及vue-router,axios,element-plus,@element-plus/icons-vue,vue-echarts
Element-Plus: 官网
npm i
npm i vue-router
npm i axios
npm i element-plus
npm i @element-plus/icons-vue
2.项目初始化
2.1.删除默认生成的代码
1.修改App.vue
<template>
<router-view></router-view>
</template>
2.修改style.css
html,body,div,span,h1,h2,h3,h4,h5,h6,ul,ol,li,p {
margin: 0;
padding: 0;
font-family: "微软雅黑";
}
html,body,#app {
width: 100%;
height: 100%;
}
ul,ol {
list-style: none;
}
a {
text-decoration: none;
}
3.修改index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="https://www.xikang.com/favicon.ico">
<title>熙心健康</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
2.2.创建路由文件
router/index.js
import {createRouter,createWebHashHistory} from 'vue-router'
const routes = [
]
//创建路由实例
const router = createRouter({
history: createWebHashHistory(),
routes
});
//路由守卫
router.beforeEach( (to, from, next) => {
// 检查 sessionStorage 中是否有登录信息
const doctor= sessionStorage.getItem('doctor')
// 如果目标路由需要认证且用户未登录
if ( !doctor&& to.meta.isAuth ) {
router.push('/') // 重定向到登录页
} else {
next() // 继续导航
}
})
export default router;
2.3.创建 axios 实例并封装,配置拦截器
新建api/index.js
import axios from 'axios';
// 创建 axios 实例
const request = axios.create({
baseURL: 'http://127.0.0.1:1234/backend',
timeout: 3000 // 请求超时时间
});
// 响应拦截器
request.interceptors.response.use(
response => {
// 获取响应数据
const res = response.data;
//直接返回响应数据
return res;
},
error => {
// 处理响应错误
console.log('响应错误:' + error); // 打印错误信息
}
);
export default request;
2.4.修改main.js
main.js
import { createApp } from 'vue'
//引入element-plus
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import './style.css'
import 'element-plus/dist/index.css'
import router from './router'
const app = createApp(App)
app.use(router)
app.use(ElementPlus)
//注册所有ElementPlus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
2.5.修改启动端口号
修改vite.config.js
server:{
port: 7777//启动端口号
}
3.组件开发
3.1.登录组件 Login.vue
新建: views/Login.vue
<template>
<div class="wrapper">
<el-card style="width: 480px">
<h2 class="login-title">系统登录</h2>
<el-form ref="form" :model="admin" :rules="rules" label-width="auto" style="max-width: 400px">
<el-form-item label="登录名" prop="loginName">
<el-input :prefix-icon="User" v-model="admin.loginName" placeholder="请输入登录名"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input :prefix-icon="Lock" v-model="admin.password" type="password" placeholder="请输入密码" show-password/>
</el-form-item>
<el-button @click="login" :loading="loading" type="primary" class="login-button">
登录
</el-button>
</el-form>
</el-card>
</div>
</template>
<script setup>
import {ref,reactive} from 'vue'
import {User,Lock} from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import axios from '../api'
import { useRouter } from 'vue-router'
let admin = reactive({
loginName:'',
password: ''
})
let loading = ref(false) //加载
const rules = reactive( {
loginName: [
{ required: true, message: '请输入登录名', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
],})
const form = ref(null);
let router = useRouter()
let login = ()=>{
form.value.validate( valid => {
if (valid) {
loading.value = true
axios.get(`/admin/${admin.loginName}/${admin.password}`)
.then( reponse => {
loading.value = false
if (reponse.code==200) {
ElMessage.success('登录成功')
sessionStorage.setItem('admin',JSON.stringify(reponse.data) )
router.push('/index')
} else{
ElMessage.error(reponse.msg)
}
})
}
});
}
</script>
<style scoped>
.wrapper{
background: linear-gradient(120deg, #a1c4fd, #c2e9fb);
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.el-card{
width: 400px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
padding: 40px;
box-sizing: border-box;
}
.login-title{
text-align: center;
margin-bottom: 30px;
color: #303133;
font-weight: 500;
font-size: 24px;
}
.login-button{
width: 100%;
margin-top: 10px;
background: linear-gradient(45deg, #409EFF, #64b5ff);
border: none;
height: 45px;
font-size: 16px;
}
</style>
3.2.首页组件 Index.vue
<template>
<el-container>
<el-header>
<div>
<h3>熙心体检管理系统</h3>
</div>
<div>
<el-avatar :size="50" src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
<span>{{ admin.loginName }}</span>
</div>
</el-header>
<el-container>
<el-aside>
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
class="el-menu-vertical-demo"
text-color="#fff"
router
>
<el-sub-menu index="sub1">
<template #title>
<el-icon><Position /></el-icon>
<span>基础数据管理</span>
</template>
<el-menu-item index="1">医院数据管理</el-menu-item>
<el-menu-item index="2">医生数据管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index="sub2">
<template #title>
<el-icon><Histogram /></el-icon>
<span>统计分析</span>
</template>
<el-menu-item index="1">XXX</el-menu-item>
<el-menu-item index="2">YYY</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script setup>
import { ref,reactive } from 'vue'
const admin = JSON.parse( sessionStorage.getItem('admin') )
</script>
<style scoped>
.el-container{
width: 100%;
height: 100%;
}
.el-header{
width: 100%;
height: 100px;
background-color: #409eff;
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
}
.el-avatar{
vertical-align: middle;
margin-right: 10px;
}
.el-aside{
width: 200px;
height: 100%;
}
.el-menu{
height: 100%;
}
.el-main{
background-color: #f5f7fa;
}
</style>
3.3.控制台组件 Console.vue
<template>
<div class="page-title">
<div>
<div>控制台</div>
<div class="welcome-text">欢迎回来,管理员!今天是 {{ currentDate }}</div>
</div>
</div>
<div class="stat-cards">
<div class="stat-card">
<div class="card-title">
<el-icon><User/></el-icon>
用户总数
</div>
<div class="card-value">12,586</div>
<div class="card-trend trend-up">
<el-icon><Top /></el-icon> 同比增长 12.5%
</div>
</div>
<div class="stat-card">
<div class="card-title">
<i class="el-icon-shopping-bag-1"></i> 订单总数
</div>
<div class="card-value">8,742</div>
<div class="card-trend trend-up">
<el-icon><ShoppingBag /></el-icon>
同比增长 8.2%
</div>
</div>
<div class="stat-card">
<div class="card-title">
<el-icon><Money /></el-icon>
销售额
</div>
<div class="card-value">¥ 1,254,689</div>
<div class="card-trend trend-up">
<el-icon><Top /></el-icon>
同比增长 15.3%
</div>
</div>
<div class="stat-card">
<div class="card-title">
<el-icon><Timer /></el-icon>
平均响应时间
</div>
<div class="card-value">126 ms</div>
<div class="card-trend trend-down">
<el-icon><Bottom /></el-icon>
同比下降 5.2%
</div>
</div>
</div>
</template>
<script setup>
import {ref,reactive } from 'vue'
import {User,ShoppingBag,Top,Bottom,Timer,Money} from '@element-plus/icons-vue'
const currentDate = new Date().toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long'
})
</script>
<style scoped>
.page-title {
margin-bottom: 20px;
font-size: 12px;
font-weight: 500;
color: #303133;
display: flex;
align-items: center;
justify-content: space-between;
}
.stat-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background: white;
border-radius: 4px;
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
}
.card-title {
color: #909399;
font-size: 14px;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.card-title i {
margin-right: 6px;
}
.card-value {
font-size: 26px;
font-weight: bold;
color: #303133;
margin-bottom: 8px;
}
.card-trend {
font-size: 14px;
display: flex;
align-items: center;
}
.trend-up {
color: #f56c6c;
}
.trend-down {
color: #67c23a;
}
</style>
3.4.医院数据管理组件
1.Hospital.vue
<template>
<el-card >
<el-form :inline="true" :model="hospital" class="demo-form-inline">
<el-form-item label="医院名称">
<el-input v-model="hospital.name" clearable />
</el-form-item>
<el-form-item label="医院地址">
<el-input v-model="hospital.address" clearable />
</el-form-item>
<el-form-item >
<el-button @click="query" type="primary" title="查询" :icon="Search" circle />
<el-button @click="$router.push('/index/hospitaladd')" type="warning" title="新增" :icon="CirclePlus" circle />
</el-form-item>
</el-form>
<el-table :data="tableData" border stripe>
<el-table-column prop="hpId" label="医院编号" width="100"/>
<el-table-column prop="name" label="医院名称" />
<el-table-column label="医院图片" width="100">
<template #default="scope">
<el-image style="width: 50px; height: 50px" :src="scope.row.img" />
</template>
</el-table-column>
<el-table-column prop="telephone" label="医院电话" />
<el-table-column prop="address" label="医院地址"/>
<el-table-column prop="businessHours" label="营业时间" />
<el-table-column prop="deadline" label="采血截止时间" />
<el-table-column label="修改" width="80">
<template #default="scope">
<el-button @click="toUpdate(scope.row)" type="success" :icon="Edit" circle />
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script setup>
import { ref,reactive } from 'vue'
import {Search,CirclePlus,Edit} from '@element-plus/icons-vue'
import axios from '../api'
import { useRouter } from 'vue-router'
const hospital = reactive(
{
name: '',
address: '',
}
)
//
const tableData = ref([])
const query = ()=>{
axios.post('/hospital/query',hospital)
.then( response=>{
tableData.value = response.data
} )
}
const router = useRouter()
const toUpdate = hospital=>{
router.push({
path: '/index/hospitalupdate',
query: hospital
})
}
</script>
<style scoped>
.el-card{
width: 90%;
margin: 0 auto;
}
.el-form{
margin: 0 auto;
}
.el-table{
margin: 0 auto;
}
.el-pagination{
margin: 0 auto;
}
</style>
2.HospitalAdd.vue
<template>
<el-icon @click="$router.back"><DArrowLeft /></el-icon>
<el-card>
<template #header>
<h4>医院信息增加</h4>
</template>
<el-form ref="form" :rules="rules" :model="hospital" label-width="auto" style="width: 90%;margin: 0 auto;">
<el-form-item label="医院名称" prop="name">
<el-input v-model="hospital.name" />
</el-form-item>
<el-form-item label="医院地址" prop="address">
<el-input v-model="hospital.address" />
</el-form-item>
<el-form-item label="医院电话" prop="telephone">
<el-input v-model="hospital.telephone" />
</el-form-item>
<el-form-item label="营业时间" prop="businessHours">
<el-input v-model="hospital.businessHours" />
</el-form-item>
<el-form-item label="采血截止时间" prop="deadline">
<el-input v-model="hospital.deadline" />
</el-form-item>
<el-form-item label="医院图片" prop="img">
<el-input v-model="hospital.img" type="textarea" />
</el-form-item>
<el-button class="submit-btn" type="primary" @click="onSubmit">提交</el-button>
</el-form>
</el-card>
</template>
<script setup>
import { ref,reactive } from 'vue'
import axios from '../api'
import { ElMessage } from 'element-plus'
const hospital = reactive(
{
name: '',
address: '',
img: '',
telephone: '',
address: '',
businessHours: '',
deadline: ''
}
)
const rules = reactive( {
name: [
{ required: true, message: '请输入医院名称', trigger: 'blur' },
],
address: [
{ required: true, message: '请输入医院地址', trigger: 'blur' },
],
telephone: [
{ required: true, message: '请输入医院电话', trigger: 'blur' },
],
businessHours:[
{ required: true,message: '请输入营业时间',trigger: 'blur'}
],
deadline:[
{ required: true,message: '请输入采血截止时间',trigger: 'blur'}
],
img:[
{ required: true,message: '请输入医院图片',trigger: 'blur'}
],
})
const form = ref(null)
const onSubmit = ()=>{
form.value.validate( validate=>{
if (validate) {
axios.post('/hospital',hospital)
.then( response=>{
ElMessage.success(response.data)
} )
}
} )
}
</script>
<style scoped>
.el-card{
width: 60%;
margin: 0 auto;
padding: 20px;
}
.submit-btn{
width: 100%;
}
</style>
3.HospitalUpdate.vue
<template>
<el-icon @click="$router.back"><DArrowLeft /></el-icon>
<el-card>
<template #header>
<h4>医院信息修改</h4>
</template>
<el-form ref="form" :rules="rules" :model="hospital" label-width="auto" style="width: 90%;margin: 0 auto;">
<el-form-item label="医院编号" >
<el-input readonly v-model="hospital.hpId" />
</el-form-item>
<el-form-item label="医院名称" prop="name">
<el-input v-model="hospital.name" />
</el-form-item>
<el-form-item label="医院地址" prop="address">
<el-input v-model="hospital.address" />
</el-form-item>
<el-form-item label="医院电话" prop="telephone">
<el-input v-model="hospital.telephone" />
</el-form-item>
<el-form-item label="营业时间" prop="businessHours">
<el-input v-model="hospital.businessHours" />
</el-form-item>
<el-form-item label="采血截止时间" prop="deadline">
<el-input v-model="hospital.deadline" />
</el-form-item>
<el-form-item label="医院图片" prop="img">
<el-input v-model="hospital.img" type="textarea" />
</el-form-item>
<el-button class="submit-btn" type="primary" @click="onSubmit">提交</el-button>
</el-form>
</el-card>
</template>
<script setup>
import { ref,reactive } from 'vue'
import axios from '../api'
import { ElMessage } from 'element-plus'
import { useRoute } from 'vue-router'
const route = useRoute()
const hospital = reactive( route.query )
const rules = reactive( {
name: [
{ required: true, message: '请输入医院名称', trigger: 'blur' },
],
address: [
{ required: true, message: '请输入医院地址', trigger: 'blur' },
],
telephone: [
{ required: true, message: '请输入医院电话', trigger: 'blur' },
],
businessHours:[
{ required: true,message: '请输入营业时间',trigger: 'blur'}
],
deadline:[
{ required: true,message: '请输入采血截止时间',trigger: 'blur'}
],
img:[
{ required: true,message: '请输入医院图片',trigger: 'blur'}
],
})
const form = ref(null)
const onSubmit = ()=>{
form.value.validate( validate=>{
if (validate) {
axios.put('/hospital',hospital)
.then( response=>{
ElMessage.success(response.data)
} )
}
} )
}
</script>
<style scoped>
.el-card{
width: 60%;
margin: 0 auto;
padding: 20px;
}
.submit-btn{
width: 100%;
}
</style>