This commit is contained in:
yy 2024-05-17 12:06:29 +08:00
parent c05e694e38
commit 147e82c394
41 changed files with 3201 additions and 2134 deletions

View File

@ -10,11 +10,11 @@
"type-check": "vue-tsc --noEmit"
},
"dependencies": {
"@stomp/stompjs": "^7.0.0",
"axios": "^1.3.3",
"crypto-js": "^4.2.0",
"echarts": "^5.4.1",
"element-plus": "2.3.1",
"element-china-area-data": "^6.1.0",
"element-plus": "2.7.2",
"js-cookie": "^3.0.5",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
@ -22,8 +22,8 @@
"sass": "^1.58.3",
"uuid": "^9.0.1",
"vant": "^4.8.3",
"vue": "^3.2.45",
"vue-router": "^4.1.6",
"vue": "^3.4.21",
"vue-router": "^4.3.0",
"xlsx": "^0.18.5"
},
"devDependencies": {

99
src/api/dict.ts Normal file
View File

@ -0,0 +1,99 @@
import request from "@/utils/request";
const dictUrl = "/admin/dict";
const dictPageUrl = "/admin/dict/tree";
const dictItemUrl = "/admin/dict/item";
const dictItemPageUrl = "/admin/dict/item/page";
export function dictPage(current: number, size: number, name?: string) {
return new Promise(resolve => {
request.get(dictPageUrl, {
params: {
current,
size,
name
}
}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function dictItemPage(dictId: string, name?: string) {
return new Promise(resolve => {
request.get(dictItemPageUrl, {
params: {
dictId,
name
}
}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function saveDict(dict: any): Promise<any> {
return new Promise(resolve => {
request.post(dictUrl, dict).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function updateDict(dict: any): Promise<any> {
return new Promise(resolve => {
request.put(dictUrl, dict).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function saveDictItem(item: any): Promise<any> {
return new Promise(resolve => {
request.post(dictItemUrl, item).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function updateDictItem(item: any): Promise<any> {
return new Promise(resolve => {
request.put(dictItemUrl, item).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function deleteDict(ids: any[]): Promise<any> {
return new Promise(resolve => {
request.delete(dictUrl, {
data: ids
}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function deleteDictItem(id: string): Promise<any> {
return new Promise(resolve => {
request.delete(dictItemUrl + "/" + id).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}

View File

@ -1,18 +1,122 @@
import request, {CommonHeaderEnum} from "@/utils/request";
const getHospitalListUrl = "/admin/hospital/getHospitalList"
const getHospitalPageUrl = "/admin/hospital/getHospitalPage"
const getMyHospitalListUrl = "/admin/hospital/getMyHospitalList";
const saveHospitalUrl = "/admin/hospital/saveHospital";
const getHospitalPageUrl = "/admin/hospital/getHospitalPage";
const deleteHospitalByIdsUrl = "/admin/hospital/deleteHospitalByIds";
const saveHospitalManagerUrl = "/admin/hospital/saveHospitalManager";
const updateHospitalUrl = "/admin/hospital/updateHospital";
const getHospitalManagerUrl = "/admin/hospital/getHospitalManager";
const changeHospitalUrl = "/admin/hospital/changeHospital";
const getCurrentHospitalUrl = "/admin/hospital/getCurrentHospital";
export const getHospitalList = () => {
return new Promise(resolve => {
request({
url: getHospitalListUrl,
method: 'post',
headers: {
'Content-Type': CommonHeaderEnum.FORM_CONTENT_TYPE,
},
}).then(res => {
resolve(res.data);
})
return new Promise(resolve => {
request({
url: getHospitalListUrl,
method: 'post',
headers: {
'Content-Type': CommonHeaderEnum.FORM_CONTENT_TYPE,
},
}).then(res => {
resolve(res.data);
})
})
}
export function getMyHospitalList() {
return new Promise(resolve => {
request.post(getMyHospitalListUrl).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function saveHospital(hospital: any) {
return new Promise(resolve => {
request.postForm(saveHospitalUrl, hospital).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function getHospitalPage(current: number, size: number, name?: string) {
return new Promise(resolve => {
request.postForm(getHospitalPageUrl, {
offset: current,
limit: size,
name: name
}).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function deleteHospitalByIds(ids: string) {
return new Promise(resolve => {
request.postForm(deleteHospitalByIdsUrl, {ids}).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function saveHospitalManager(hospitalId: string, userId: string) {
return new Promise(resolve => {
request.postForm(saveHospitalManagerUrl, {hospitalId, userId}).then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function updateHospital(form: any) {
return new Promise(resolve => {
request.postForm(updateHospitalUrl, form).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function getHospitalManager(hospitalId: string) {
return new Promise(resolve => {
request.postForm(getHospitalManagerUrl, {
hospitalId
}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function changeHospital(id: string) {
return new Promise(resolve => {
request.postForm(changeHospitalUrl, {id}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err);
})
})
}
export function getCurrentHospital() {
return new Promise(resolve => {
request.post(getCurrentHospitalUrl).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}

57
src/api/medicine.ts Normal file
View File

@ -0,0 +1,57 @@
import request from "@/utils/request";
const getDatabaseListUrl = "/admin/medicine/getDatabaseList";
const getSurgeryCountUrl = "/admin/medicine/getSurgeryCount";
const getSurgeryDurationUrl = "/admin/medicine/getSurgeryDuration";
const getSurgeryTypeProportionUrl = "/admin/medicine/getSurgeryTypeProportion";
const getSurgeryOtherDurationUrl = "/admin/medicine/getSurgeryOtherDuration";
export function getDatabaseList() {
return new Promise(resolve => {
request.post(getDatabaseListUrl).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err)
})
})
}
export function getSurgeryCount(start: string, end: string): Promise<any> {
return new Promise(resolve => {
request.postForm(getSurgeryCountUrl, {start, end}).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err)
})
})
}
export function getSurgeryDuration(start: string, end: string): Promise<any> {
return new Promise(resolve => {
request.postForm(getSurgeryDurationUrl, {start, end}).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function getSurgeryTypeProportion(start: string, end: string): Promise<any> {
return new Promise(resolve => {
request.postForm(getSurgeryTypeProportionUrl, {start, end}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}
export function getSurgeryOtherDuration(start: string, end: string): Promise<any> {
return new Promise(resolve => {
request.postForm(getSurgeryOtherDurationUrl, {start, end}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err)
})
})
}

View File

@ -4,76 +4,76 @@ const getMenuTreeUrl = "/admin/menu/tree";
const menuUrl = "/admin/menu";
export function getMenuTree(menuName?: string, parent?: string, type?: string) {
return new Promise((resolve, reject) => {
const params: any = [];
if (parent) {
params.push("parent=" + parent);
}
if (menuName) {
params.push("menuName=" + menuName);
}
if (type) {
params.push("type=" + type);
}
request.get(getMenuTreeUrl + (params.length > 0 ? "?" + params.join("&") : ""))
.then((res) => {
resolve(res.data);
}).catch(err => {
reject(err);
})
})
return new Promise((resolve, reject) => {
const params: any = [];
if (parent) {
params.push("parent=" + parent);
}
if (menuName) {
params.push("menuName=" + menuName);
}
if (type) {
params.push("type=" + type);
}
request.get(getMenuTreeUrl + (params.length > 0 ? "?" + params.join("&") : ""))
.then((res) => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function getRoleTree(roleId: string) {
return new Promise(resolve => {
request.get(getMenuTreeUrl + "/" + roleId).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
});
})
return new Promise(resolve => {
request.get(getMenuTreeUrl + "/" + roleId).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
});
})
}
export function saveMenu(menu: any) {
return new Promise((resolve, reject) => {
request.post(menuUrl, menu).then((res: any) => {
resolve(res.data);
}).catch(err => {
reject(err);
})
})
return new Promise((resolve, reject) => {
request.post(menuUrl, menu).then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function updateMenu(menu: any) {
return new Promise((resolve, reject) => {
request.put(menuUrl, menu).then((res: any) => {
resolve(res);
}).catch(err => {
reject(err);
})
})
return new Promise((resolve, reject) => {
request.put(menuUrl, menu).then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function deleteById(id: string) {
return new Promise((resolve) => {
request.delete(menuUrl + "/" + id).then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
return new Promise((resolve) => {
request.delete(menuUrl + "/" + id).then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function getUserMenu(type?: string, parentId?: string) {
return new Promise(resolve => {
const params: string[] = [];
if (type) params.push("type=" + type);
if (parentId) params.push("parentId=" + parentId);
request.get(menuUrl + (params.length > 0 ? "?" + params.join("&") : ""))
.then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
});
})
return new Promise(resolve => {
const params: string[] = [];
if (type) params.push("type=" + type);
if (parentId) params.push("parentId=" + parentId);
request.get(menuUrl + (params.length > 0 ? "?" + params.join("&") : ""))
.then((res: any) => {
resolve(res.data);
}).catch(err => {
resolve(err);
});
})
}

View File

@ -6,46 +6,46 @@ const updateUrl = "/admin/sysMessage/update";
const deleteUrl = "/admin/sysMessage/delete";
export function save(message: any, organization?: [], permissions?: []) {
return new Promise(resolve => {
request.postForm(saveUrl, message).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
return new Promise(resolve => {
request.postForm(saveUrl, message).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function update(message: any) {
return new Promise(resolve => {
request.postForm(updateUrl, message).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
return new Promise(resolve => {
request.postForm(updateUrl, message).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function deleteByIds(ids: string[]) {
return new Promise(resolve => {
request.postForm(deleteUrl, {ids: ids.join(",")}).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
return new Promise(resolve => {
request.postForm(deleteUrl, {ids: ids.join(",")}).then(res => {
resolve(res.data);
}).catch(err => {
resolve(err);
})
})
}
export function page(current: number, size: number, condition?: { category: string, message: string }) {
return new Promise(resolve => {
request.postForm(pageUrl, {
current: current,
size: size,
category: condition?.category,
message: condition?.message
}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err);
})
export function page(current: number, size: number, condition?: { category: string, message: string }): Promise<any> {
return new Promise(resolve => {
request.postForm(pageUrl, {
current: current,
size: size,
category: condition?.category,
message: condition?.message
}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err);
})
})
}

View File

@ -12,6 +12,8 @@ const userUrl = '/admin/user'
const modifyPwUrl = "/admin/user/modifyPw"
const getUserListByHospitalUrl = "/admin/user/getUserListByHospital";
export function getUserInfo() {
return new Promise(resolve => {
getData(userInfoUrl).then((data: any) => {
@ -117,3 +119,13 @@ export function deleteU(ids: any[]) {
})
})
}
export function getUserListByHospital(hospitalId: string) {
return new Promise(resolve => {
request.postForm(getUserListByHospitalUrl, {hospitalId}).then(res => {
resolve(res.data)
}).catch(err => {
resolve(err);
})
})
}

View File

@ -51,16 +51,16 @@
</template>
<script lang='ts' setup>
import {onMounted, reactive, ref, toRefs, watch} from 'vue'
import {ElMessage} from 'element-plus'
import {onMounted, reactive, ref} from 'vue'
import type {UploadProps} from 'element-plus'
import {useLoginStore} from '@/stores/user-info-store'
import {ElMessage} from 'element-plus'
import {editPassword, getUserInfo, updateUserInfo} from "@/api/user";
import {handleHttpUpload} from "@/api/file-upload";
import {useUserStore} from "@/stores/user-info-store";
const emit = defineEmits(['close'])
// const userInfo = useLoginStore().getlogin()
const userInfo = useUserStore().getlogin()
const formRef = ref()
const uploadRef = ref()
@ -188,7 +188,7 @@ const submitForm = async () => {
if (valid) {
// console.log('submit!')
//
// useLoginStore().setlogin('name', formData.value.name)
useUserStore().setlogin('name', formData.value.name)
if (tabActive.value == 0) {
const param = {
name: formData.value.name,

View File

@ -1,11 +1,10 @@
import { createApp, defineComponent, ref, reactive } from 'vue'
import {createApp} from 'vue'
import main from './main.vue'
import router from './router'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus';
import {createPinia} from 'pinia'
import ElementPlus, {ElDialog} from 'element-plus';
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import { ElDialog } from 'element-plus'
import TableAbility from '@/components/table-ability.vue'
@ -16,10 +15,8 @@ import '@/assets/css/custom-element.scss';
import '@/assets/css/animastore.css';
import '../permisstion';
// import '@/utils/debugger'
const pinia = createPinia()
const app = createApp(main)
const app = createApp(main)
ElDialog.props.draggable.default = true
@ -31,7 +28,7 @@ app.use(router)
app.component('el-dialog', ElDialog)
app.component('TableAbility', TableAbility)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
app.component(key, component)
}
app.mount('#app')

View File

@ -1,100 +1,63 @@
import {createRouter, createWebHistory} from 'vue-router'
import {useLoginStore} from '@/stores/user-info-store'
import {useUserStore} from '@/stores/user-info-store'
import {ElMessage} from 'element-plus'
import {constantRoute} from './routes'
import {decrypt, decryption, encrypt, encryption, isBase64} from "@/utils/other";
import {decrypt, encrypt, isBase64} from "@/utils/other";
import {Session} from "@/utils/storage";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: constantRoute,
stringifyQuery: stringifyQuery,
parseQuery: parseQuery,
history: createWebHistory(import.meta.env.BASE_URL),
routes: constantRoute,
stringifyQuery: stringifyQuery,
parseQuery: parseQuery,
})
const kk = "raxipnenttlewe";
router.beforeEach((to, from, next) => {
const loginInfo = useLoginStore().getlogin()
// const isLogin = loginInfo.isLogin
// 普通用户
const commonUser = [
'/login',
'/home',
'/patients-manage',
'/patients-manage/patients-manage',
'/patients-manage/surgery-info'
]
// 中级管理员
const IntermediateAdmin = [
'/login',
'/home',
'/patients-manage',
'/patients-manage/patients-manage',
'/patients-manage/surgery-info',
'/remote-manage',
'/remote-manage/remote-manage',
'/remote-manage/remote-control'
]
// 高级管理员
const SeniorAdmin = [
'/login',
'/home',
'/patients-manage',
'/patients-manage/patients-manage',
'/patients-manage/surgery-info',
'/remote-manage',
'/remote-manage/remote-manage',
'/remote-manage/remote-control',
'/permissions-manage',
'/permissions-manage/doctor-manage',
'/permissions-manage/role-manage',
'/permissions-manage/menu-manage',
'/logs-manage',
'/logs-manage/message-manage',
'/logs-manage/logs-manage'
]
const isViewRoute = () => {
let release = true
/*switch (loginInfo.permissions) {
case '超级管理员':
release = true
break;
case '高级管理员':
release = SeniorAdmin.some((p: string) => p === to.path)
break;
case '中级管理员':
release = IntermediateAdmin.some((p: string) => p === to.path)
break;
case '普通用户':
release = commonUser.some((p: string) => p === to.path)
break;
default:
break;
}*/
return release
}
if (to.fullPath === '/login') {
next()
return
}
/*if (!isLogin) next('/login') //
else */
if (!isViewRoute()) {
ElMessage.error('无权访问!')
next(from.fullPath)
} else next()
router.beforeEach(async (to, from, next) => {
if (to.fullPath.startsWith("/login")) {
next()
return
}
const b = await isViewRoute(to);
if (!b) {
ElMessage.error('无权访问!')
next(from.fullPath)
} else next()
});
const isViewRoute = async (to: any) => {
const data: any = await useUserStore().getMenuPathList();
return data.paths.indexOf(to.path) != -1
}
function stringifyQuery(query: any) {
return btoa(encrypt(JSON.stringify(query), kk));
return btoa(encrypt(JSON.stringify(query), kk));
}
function parseQuery(query: any) {
if (isBase64(query)) {
return JSON.parse(decrypt(atob(query), kk));
} else {
return query;
}
if (isBase64(query)) {
return JSON.parse(decrypt(atob(query), kk));
} else {
return query;
}
}
const token = Session.get("token");
if (token) {
router.addRoute({
name: "root",
path: "/",
redirect: "/home"
})
} else {
router.addRoute({
name: "root",
path: "/",
redirect: "/login"
})
}
export default router

View File

@ -1,131 +1,138 @@
export const constantRoute=[
{ path: '/:pathMatch(.*)*', name: 'not-found', redirect: '/login' },
{
path: '/login',
name: '登录',
component: () => import('@/views/login/login.vue'),
},
{
path: '/',
redirect: '/login',
component: () => import('@/views/index.vue'),
export const constantRoute = [
{
path: '/404',
name: "404",
component: () => import('@/views/404/404.vue')
},
{
path: '/login',
name: '登录',
component: () => import('@/views/login/login.vue'),
},
{
path: '/page',
component: () => import('@/views/index.vue'),
children: [
{
path: '/home',
name: '首页',
component: () => import('@/views/home/index.vue'),
},
{
path: '/permissions-manage',
name: '权限管理',
redirect: '/permissions-manage/doctor-manage',
component: () => import('@/views/permissions-manage/index.vue'),
children: [
{
path: '/home',
name: '首页',
component: () => import('@/views/home/index.vue'),
},
{
path: '/permissions-manage',
name: '权限管理',
redirect: '/permissions-manage/doctor-manage',
component: () => import('@/views/permissions-manage/index.vue'),
children: [
{
path: '/permissions-manage/doctor-manage',
name: '医生管理',
component: () => import('@/views/permissions-manage/doctor-manage.vue'),
},
{
path: '/permissions-manage/role-manage',
name: '角色管理',
component: () => import('@/views/permissions-manage/role-manage.vue'),
},
{
path: '/permissions-manage/menu-manage',
name: '菜单管理',
component: () => import('@/views/permissions-manage/menu-manage.vue'),
}
]
},
{
path: '/patients-manage',
name: '患者管理',
redirect: '/patients-manage/patients-manage',
component: () => import('@/views/patients-manage/index.vue'),
children: [
{
path: '/patients-manage/patients-manage',
name: '患者管理',
component: () => import('@/views/patients-manage/patients-manage.vue'),
},
{
path: '/patients-manage/surgery-info',
name: '手术信息',
component: () => import('@/views/patients-manage/surgery-info.vue'),
}
]
},
{
path: '/remote-manage',
name: '远程管理',
redirect: '/remote-manage/remote-manage',
component: () => import('@/views/remote-manage/index.vue'),
children: [
{
path: '/remote-manage/remote-manage',
name: '远程管理',
component: () => import('@/views/remote-manage/remote-manage.vue'),
},
{
path: '/remote-manage/remote-thumbnail',
name: '预览缩略图',
component: () => import('@/views/remote-manage/remote-thumbnail.vue'),
},
{
path: '/remote-manage/remote-control',
name: '远程控制',
component: () => import('@/views/remote-manage/remote-control.vue'),
}
]
},
{
path: '/system-manage',
name: '后台管理',
redirect: '/system-manage/system-home',
component: () => import('@/views/system-manage/index.vue'),
children: [
{
path: '/system-manage/system-home',
name: '后台首页',
component: () => import('@/views/system-manage/system-home.vue'),
},
{
path: '/system-manage/hospitals',
name: '合作医院',
component: () => import('@/views/system-manage/hospitals.vue'),
},
{
path: '/system-manage/hospitals-map',
name: '合作医院地图',
component: () => import('@/views/system-manage/hospitals-map.vue'),
},
{
path: '/system-manage/user-list',
name: '用户列表',
component: () => import('@/views/system-manage/user-list.vue'),
}
]
},
{
path: '/logs-manage',
name: '系统管理',
redirect: '/logs-manage/message-manage',
component: () => import('@/views/logs-manage/index.vue'),
children: [
{
path: '/logs-manage/message-manage',
name: '消息管理',
component: () => import('@/views/logs-manage/message-manage.vue'),
},
{
path: '/logs-manage/logs-manage',
name: '日志管理',
component: () => import('@/views/logs-manage/logs-manage.vue'),
}
]
}
{
path: '/permissions-manage/doctor-manage',
name: '医生管理',
component: () => import('@/views/permissions-manage/doctor-manage.vue'),
},
{
path: '/permissions-manage/role-manage',
name: '角色管理',
component: () => import('@/views/permissions-manage/role-manage.vue'),
},
{
path: '/permissions-manage/menu-manage',
name: '菜单管理',
component: () => import('@/views/permissions-manage/menu-manage.vue'),
}
]
}
},
{
path: '/patients-manage',
name: '患者管理',
redirect: '/patients-manage/patients-manage',
component: () => import('@/views/patients-manage/index.vue'),
children: [
{
path: '/patients-manage/patients-manage',
name: '患者管理',
component: () => import('@/views/patients-manage/patients-manage.vue'),
},
{
path: '/patients-manage/surgery-info',
name: '手术信息',
component: () => import('@/views/patients-manage/surgery-info.vue'),
}
]
},
{
path: '/remote-manage',
name: '远程管理',
redirect: '/remote-manage/remote-manage',
component: () => import('@/views/remote-manage/index.vue'),
children: [
{
path: '/remote-manage/remote-manage',
name: '远程管理',
component: () => import('@/views/remote-manage/remote-manage.vue'),
},
{
path: '/remote-manage/remote-thumbnail',
name: '预览缩略图',
component: () => import('@/views/remote-manage/remote-thumbnail.vue'),
},
{
path: '/remote-manage/remote-control',
name: '远程控制',
component: () => import('@/views/remote-manage/remote-control.vue'),
}
]
},
{
path: '/system-manage',
name: '后台管理',
redirect: '/system-manage/system-home',
component: () => import('@/views/system-manage/index.vue'),
children: [
{
path: '/system-manage/system-home',
name: '后台首页',
component: () => import('@/views/system-manage/system-home.vue'),
},
{
path: '/system-manage/hospitals',
name: '合作医院',
component: () => import('@/views/system-manage/hospitals.vue'),
},
{
path: '/system-manage/hospitals-map',
name: '合作医院地图',
component: () => import('@/views/system-manage/hospitals-map.vue'),
},
{
path: '/system-manage/user-list',
name: '用户列表',
component: () => import('@/views/system-manage/user-list.vue'),
}
]
},
{
path: '/logs-manage',
name: '系统管理',
redirect: '/logs-manage/message-manage',
component: () => import('@/views/logs-manage/index.vue'),
children: [
{
path: '/logs-manage/message-manage',
name: '消息管理',
component: () => import('@/views/logs-manage/message-manage.vue'),
},
{
path: '/logs-manage/logs-manage',
name: '日志管理',
component: () => import('@/views/logs-manage/logs-manage.vue'),
},
{
path: "/logs-manage/dict-manage",
name: "词典管理",
component: () => import("@/views/logs-manage/dict-manage.vue")
}
]
}
]
}
]

View File

View File

@ -18,7 +18,7 @@ export const useRemoteWsStore = defineStore("remoteWs", {
exceptionMsg: {
"BIS_except": "脑电双频指数异常", "DBP_except": "舒张压异常", "EtCO2_except": "呼气末二氧化碳异常",
"HR_except": "心率异常", "SBP_except": "收缩压异常", "ST_except": "ST异常"
},
} as any,
}
},
actions: {

View File

@ -1,9 +1,12 @@
import { defineStore } from 'pinia'
import {defineStore} from 'pinia'
import {getUserMenu} from "@/api/menu";
import {Session} from "@/utils/storage";
import router from "@/router";
export const useLoginStore = defineStore('login', {
export const useUserStore = defineStore('login', {
state: () => {
return {
login: {
userInfo: {
account: '',
name: '',
/**
@ -12,21 +15,65 @@ export const useLoginStore = defineStore('login', {
*
*
*/
hospital: ''
} as any
hospitalName: '',
hospitalId: '',
menuPathList: {
paths: [],
menus: []
},
roleName: ''
} as any,
showHomeMsg: false
}
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {
getlogin() {
return this.login
const onlineUser = Session.get("onlineUser");
return this.userInfo.account ? this.userInfo : onlineUser
},
setlogin(key: string, e: any) {
this.login[key] = e
this.userInfo[key] = e
Session.set("onlineUser", this.userInfo);
},
logout() {
this.login.isLogin = false
Session.clear();
router.removeRoute("root");
router.addRoute({
name: "root",
path: "/",
redirect: "/login"
})
},
getMenuPathList(): Promise<any> {
return new Promise(resolve => {
if (this.userInfo.menuPathList.paths.length == 0) {
getUserMenu().then((res: any) => {
if (res.code == 0) {
const paths: any = [];
const menus: any = [];
res.data.forEach((item: any) => {
handleMenuPath(item, paths, menus);
});
this.userInfo.menuPathList = {paths, menus};
resolve(this.userInfo.menuPathList);
}
})
} else {
resolve(this.userInfo.menuPathList)
}
})
}
},
})
})
function handleMenuPath(menu: any, paths: any, menus?: any) {
paths.push(menu.path);
if (menus) {
menus.push(menu);
}
if (menu.children && menu.children.length > 0) {
menu.children.forEach((child: any) => {
handleMenuPath(child, paths);
})
}
}

View File

@ -3,21 +3,21 @@
* @returns
*/
export function dateFormater(formater: string, time?: any) {
let date = time ? new Date(time) : new Date(),
Y = date.getFullYear() + '',
M = date.getMonth() + 1,
D = date.getDate(),
H = date.getHours(),
m = date.getMinutes(),
s = date.getSeconds();
return formater
.replace(/YYYY|yyyy/g, Y)
.replace(/YY|yy/g, Y.substr(2, 2))
.replace(/MM/g, (M < 10 ? '0' : '') + M)
.replace(/DD|dd/g, (D < 10 ? '0' : '') + D)
.replace(/HH|hh/g, (H < 10 ? '0' : '') + H)
.replace(/mm/g, (m < 10 ? '0' : '') + m)
.replace(/ss/g, (s < 10 ? '0' : '') + s);
let date = time ? new Date(time) : new Date(),
Y = date.getFullYear() + '',
M = date.getMonth() + 1,
D = date.getDate(),
H = date.getHours(),
m = date.getMinutes(),
s = date.getSeconds();
return formater
.replace(/YYYY|yyyy/g, Y)
.replace(/YY|yy/g, Y.substr(2, 2))
.replace(/MM/g, (M < 10 ? '0' : '') + M)
.replace(/DD|dd/g, (D < 10 ? '0' : '') + D)
.replace(/HH|hh/g, (H < 10 ? '0' : '') + H)
.replace(/mm/g, (m < 10 ? '0' : '') + m)
.replace(/ss/g, (s < 10 ? '0' : '') + s);
}
/**
@ -26,20 +26,20 @@ export function dateFormater(formater: string, time?: any) {
* @returns
*/
export function getMonthDays(time: any) {
if (!time) time = new Date();
const now = new Date(time);
const year = now.getFullYear();
const month = now.getMonth() + 1;
if (!time) time = new Date();
const now = new Date(time);
const year = now.getFullYear();
const month = now.getMonth() + 1;
const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
const daysInMonth = month === 2
? isLeapYear ? 29 : 28
: month === 4 || month === 6 || month === 9 || month === 11
? 30
: 31;
const daysInMonth = month === 2
? isLeapYear ? 29 : 28
: month === 4 || month === 6 || month === 9 || month === 11
? 30
: 31;
return daysInMonth;
return daysInMonth;
}
/**
@ -47,22 +47,22 @@ export function getMonthDays(time: any) {
* @returns
*/
export function getFirstDayOfWeek(date: string | Date | number, i: number) {
let temp: any;
if (!(date instanceof Date)) {
if (typeof date == "number") {
temp = new Date(date);
} else {
const s = date.replace(/-/g, "/");
temp = new Date(s);
}
} else {
temp = new Date(date.getTime())
}
if (i < 1 || i > 7) {
return null;
}
const day = temp.getDay() || 7;
return new Date(temp.getFullYear(), temp.getMonth(), temp.getDate() + i - day);
let temp: any;
if (!(date instanceof Date)) {
if (typeof date == "number") {
temp = new Date(date);
} else {
const s = date.replace(/-/g, "/");
temp = new Date(s);
}
} else {
temp = new Date(date.getTime())
}
if (i < 1 || i > 7) {
return null;
}
const day = temp.getDay() || 7;
return new Date(temp.getFullYear(), temp.getMonth(), temp.getDate() + i - day);
}
/**
@ -72,18 +72,18 @@ export function getFirstDayOfWeek(date: string | Date | number, i: number) {
const weekArray = ['周天', '周一', '周二', '周三', '周四', '周五', '周六'];
export function getCurrentDate(date: any) {
let myDate = new Date();
if (date) {
if (date instanceof Date) {
myDate = date;
} else {
date = date.replace(/-/g, '/');
myDate = new Date(date);
}
}
let myDate = new Date();
if (date) {
if (date instanceof Date) {
myDate = date;
} else {
date = date.replace(/-/g, '/');
myDate = new Date(date);
}
}
const days = myDate.getDay();
return weekArray[days];
const days = myDate.getDay();
return weekArray[days];
}
/**
@ -91,20 +91,44 @@ export function getCurrentDate(date: any) {
* @returns
*/
export function getDays(date: any, days: number) {
if (date) {
let temp: any;
if (!(date instanceof Date)) {
date = date.replace(/-/g, '/');
temp = new Date(date);
} else {
temp = new Date(date.getTime());
}
if (date) {
let temp: any;
if (!(date instanceof Date)) {
date = date.replace(/-/g, '/');
temp = new Date(date);
} else {
temp = new Date(date.getTime());
}
const time = temp.setDate(temp.getDate() + days);
return new Date(time);
}
const time = temp.setDate(temp.getDate() + days);
return new Date(time);
}
}
export function getEndOfMonth(year: number, month: number) {
return new Date(year, month, 0).getDate()
return new Date(year, month, 0).getDate()
}
export function getWeekDates(d: any) {
let temp: Date;
if (!(d instanceof Date)) {
if (typeof d == 'number') {
temp = new Date(d)
} else {
temp = new Date(d.replace(/-/g, "/"))
}
} else {
temp = d
}
const day = temp.getDay();
const date = temp.getDate() + 1
const startOfWeek = new Date(temp)
const startDate = new Date(startOfWeek.setDate(date - day));
const endOfWeek = new Date(temp)
const endDate = new Date(endOfWeek.setDate(date + (6 - day)));
return {start: startDate, end: endDate};
}

View File

@ -48,4 +48,24 @@ export function isBase64(s: string) {
} catch (e) {
return false;
}
}
export function generateRandomColor() {
// 生成随机的 RGB 值
const red = Math.floor(Math.random() * 256);
const green = Math.floor(Math.random() * 256);
const blue = Math.floor(Math.random() * 256);
// 将 RGB 值转换为 16 进制格式
return rgbToHex(red, green, blue);
}
// 将 RGB 值转换为 16 进制格式
function rgbToHex(r: number, g: number, b: number) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function componentToHex(c: number) {
const hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}

View File

@ -3,11 +3,7 @@ import axios from "axios";
import {Session} from "@/utils/storage";
import {decryption, encryption} from "@/utils/other";
import {ElMessageBox} from "element-plus";
/*
import { ElMessage } from 'element-plus'
//引入用户相关的仓库
import useUserStore from '@/stores/modules/user'
*/
import {useUserStore} from "@/stores/user-info-store";
const BASE_URL = import.meta.env.VITE_RAX_BASE_URL
@ -49,7 +45,7 @@ export const post = (url: any, params: any, success: any) => {
};
export const getMapJson = (name: string) => {
return axiosInstance.post(BASE_URL+'/static/json/' + name)
return axios.post('/static/json/' + name)
}
export const getData = (url: string, params?: any) => {
return axiosInstance.get(url, params)
@ -83,14 +79,14 @@ function handleResponse(response: any) {
axiosInstance.interceptors.response.use(handleResponse, error => {
const code = error.response.status || 200
if (code == 424) {
if (code == 424 || code == 401) {
ElMessageBox.confirm('令牌状态已过期,请点击重新登录', "系统提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: 'warning',
});
Session.clear();
window.location.href = '/';
useUserStore().logout();
window.location.replace("/");
}
return error
})

11
src/views/404/404.vue Normal file
View File

@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>

View File

@ -1,67 +1,100 @@
<template>
<div class="home-page">
<div class="background-box">
<div class="left-content">
<div class="message-box">
<el-carousel height="32px" direction="vertical" indicator-position="none" autoplay>
<el-carousel-item v-for="(item, index) in messages" :key="'message-' + index">
<p class="text-row-1" style="line-height: 32px;" :class="{'is-link': item.link}"
@click="() => {item.link && router.push(item.link)}">{{ item.type }} {{ item.content }}</p>
</el-carousel-item>
</el-carousel>
</div>
<div class="header-box">
<div class="header-item">
<span class="main-color f20" style="font-weight: 600;">{{ userInfo.name }}</span>
<span class="text2-color f14">{{ userInfo.permissions }}</span>
</div>
<div class="header-item">
<el-icon class="text1-color" style="font-size: 26px;margin-right: 20px;">
<Calendar/>
</el-icon>
<div>
<p class="text1-color f14">待办任务</p>
<p class="main-color f20">{{ total }}</p>
</div>
</div>
</div>
<div class="echart-box">
<div class="echart-item">
<NumberChart/>
</div>
<div class="echart-item">
<NumberPieChart/>
</div>
<div class="echart-item">
<TimeChart/>
</div>
<div class="echart-item">
<TimeBarChart/>
</div>
</div>
</div>
<div class="right-content">
<div class="week-calendar">
<WeekCalendar/>
</div>
<div class="system-logs">
<div class="title">
<span>系统日志</span>
<span class="f14" style="cursor: pointer;" @click="router.push('./logs-manage/logs-manage')">更多</span>
</div>
<div class="content">
<SystemLogs/>
</div>
</div>
</div>
</div>
</div>
<div class="home-page">
<div class="background-box">
<div class="left-content">
<div class="message-box">
<el-carousel height="32px" direction="vertical" indicator-position="none" autoplay>
<el-carousel-item v-for="(item, index) in messages" :key="'message-' + index">
<p class="text-row-1" style="line-height: 32px;" :class="{'is-link': item.href}"
@click="userStore.showHomeMsg=true">{{ item.category }} {{ item.message }}</p>
</el-carousel-item>
</el-carousel>
</div>
<div class="header-box">
<div class="header-item">
<span class="main-color f20" style="font-weight: 600;">{{ userInfo.name }}</span>
<span class="text2-color f14">{{ userInfo.roleName }}</span>
</div>
<div class="header-item">
<el-icon class="text1-color" style="font-size: 26px;margin-right: 20px;">
<Calendar/>
</el-icon>
<div>
<p class="text1-color f14">待办任务</p>
<p class="main-color f20">{{ todoTotal }}</p>
</div>
</div>
</div>
<div class="echart-box">
<div class="echart-item">
<NumberChart/>
</div>
<div class="echart-item">
<NumberPieChart/>
</div>
<div class="echart-item">
<TimeChart/>
</div>
<div class="echart-item">
<TimeBarChart/>
</div>
</div>
</div>
<div class="right-content">
<div class="week-calendar">
<WeekCalendar/>
</div>
<div class="system-logs" v-if="showLogMod">
<div class="title">
<span>系统日志</span>
<span class="f14" style="cursor: pointer;" @click="router.push('./logs-manage/logs-manage')">更多</span>
</div>
<div class="content">
<SystemLogs/>
</div>
</div>
</div>
</div>
<el-drawer
class="message-drawer-box"
v-model="userStore.showHomeMsg"
title="通知消息"
>
<div class="body">
<el-card style="margin-top: 10px;" v-for="(item, index) in messageTable">
<template #header>
<div class="card-header">
<span>{{ item.category }}</span>
</div>
</template>
<p class="text item">{{ item.message }}</p>
<template #footer>
<span>{{ item.creatorName }}</span>
<span style="float: inline-end;">{{ item.createTime }}</span>
</template>
</el-card>
</div>
<div class="footer">
<el-pagination
v-model:page-size="size"
v-model:current-page="current"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
background
layout="prev, pager, next, jumper, sizes"
:page-sizes="[10, 20, 30, 50]"
:total="total"
/>
</div>
</el-drawer>
</div>
</template>
<script lang='ts' setup>
import {onMounted, ref} from 'vue'
import {useRouter} from 'vue-router'
import {useLoginStore} from '@/stores/user-info-store'
import {useUserStore} from '@/stores/user-info-store'
import NumberChart from "./number-chart.vue";
import NumberPieChart from "./number-pie-chart.vue";
import TimeChart from "./time-chart.vue";
@ -70,161 +103,208 @@ import WeekCalendar from "./week-calendar.vue";
import SystemLogs from "@/components/system-logs.vue";
import * as dailyPlanApi from "@/api/daily-plan";
import {dateFormater} from "@/utils/date-util";
import * as msgApi from "@/api/sys-message";
const router = useRouter()
const userInfo = useLoginStore().getlogin()
const userStore = useUserStore();
const userInfo = userStore.getlogin()
const showLogMod = ref(false)
const messages = ref([] as any)
const total = ref(0) //
messages.value.push({
type: '通知',
content: '测试测试测试测试测试测试测试测试测试测试',
link: '/system-manage/system-home'
})
messages.value.push({
type: '公告',
content: '公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试公告测试'
})
const messageTable = ref([] as any)
const current = ref(1);
const size = ref(10);
const total = ref(0);
const todoTotal = ref(0) //
onMounted(() => {
getTodoCount();
init();
})
function init() {
const path = router.currentRoute.value.path;
userStore.getMenuPathList().then(res => {
res.menus.filter((menu: any) => menu.path == path).forEach((menu: any) => {
menu.children.filter((child: any) => child.permission == 'home_sys_log_view').forEach(() => {
showLogMod.value = true;
})
})
});
getTodoCount();
messages.value = [];
messageTable.value = [];
current.value = 1
total.value = 0
loadMsg();
}
function getTodoCount() {
dailyPlanApi.getTodoCountByDate(dateFormater("yyyy-MM-dd", new Date())).then((res: any) => {
if (res.code == 0) {
total.value = res.data;
}
});
dailyPlanApi.getTodoCountByDate(dateFormater("yyyy-MM-dd", new Date())).then((res: any) => {
if (res.code == 0) {
todoTotal.value = res.data;
}
});
}
function handleSizeChange() {
messageTable.value = [];
loadMsg()
}
function handleCurrentChange() {
messageTable.value = [];
loadMsg()
}
async function loadMsg() {
const res = await msgApi.page(current.value, size.value)
if (res.code == 0) {
total.value = res.data.total
res.data.records.forEach((row: any) => {
if (current.value == 1) {
messages.value.push(row)
}
messageTable.value.push(row)
})
}
}
</script>
<style lang='scss' scoped>
.home-page {
width: 100%;
height: 100%;
padding: 15px;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 15px;
overflow-y: auto;
.background-box {
width: 100%;
height: 100%;
background: white;
padding: 20px 35px;
display: flex;
justify-content: space-between;
min-height: 900px;
.background-box {
width: 100%;
height: 100%;
background: white;
padding: 20px 35px;
display: flex;
justify-content: space-between;
min-height: 900px;
}
}
.left-content {
width: calc(100% - 350px);
height: 100%;
.left-content {
width: calc(100% - 350px);
height: 100%;
.message-box {
height: 34px;
background: #f8f8f8;
color: $text-color;
padding: 0 30px;
border: 1px solid $border1-color;
margin-bottom: 20px;
.message-box {
height: 34px;
background: #f8f8f8;
color: $text-color;
padding: 0 30px;
border: 1px solid $border1-color;
margin-bottom: 20px;
.is-link {
cursor: pointer;
color: $main-color;
}
}
.is-link {
cursor: pointer;
color: $main-color;
}
}
.header-box {
width: 100%;
height: 90px;
display: flex;
justify-content: space-between;
.header-box {
width: 100%;
height: 90px;
display: flex;
justify-content: space-between;
.header-item {
width: calc(60% - 20px);
height: 100%;
border: 1px solid $border-color;
border-radius: 5px;
box-shadow: 1px 1px 5px $border2-color;
padding: 0 90px;
line-height: 1.5;
display: flex;
flex-direction: column;
justify-content: center;
.header-item {
width: calc(60% - 20px);
height: 100%;
border: 1px solid $border-color;
border-radius: 5px;
box-shadow: 1px 1px 5px $border2-color;
padding: 0 90px;
line-height: 1.5;
display: flex;
flex-direction: column;
justify-content: center;
& ~ .header-item {
width: 40%;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
}
}
& ~ .header-item {
width: 40%;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
}
}
.echart-box {
width: 100%;
height: calc(100% - 164px);
margin-top: 20px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.echart-box {
width: 100%;
height: calc(100% - 164px);
margin-top: 20px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.echart-item {
width: calc(60% - 20px);
height: calc(50% - 10px);
border: 1px solid $border-color;
border-radius: 5px;
padding: 20px;
box-shadow: 1px 1px 5px $border2-color;
.echart-item {
width: calc(60% - 20px);
height: calc(50% - 10px);
border: 1px solid $border-color;
border-radius: 5px;
padding: 20px;
box-shadow: 1px 1px 5px $border2-color;
&:nth-child(even) {
width: 40%;
}
&:nth-child(even) {
width: 40%;
}
&:nth-child(n + 3) {
margin-top: 20px;
}
}
}
}
&:nth-child(n + 3) {
margin-top: 20px;
}
}
}
}
.right-content {
width: 330px;
height: 100%;
.right-content {
width: 330px;
height: 100%;
.week-calendar {
width: 100%;
height: 50%;
}
.week-calendar {
width: 100%;
height: 50%;
}
.system-logs {
width: 100%;
height: calc(50% - 20px);
margin-top: 20px;
overflow: hidden;
.system-logs {
width: 100%;
height: calc(50% - 20px);
margin-top: 20px;
overflow: hidden;
.title {
width: 100%;
height: 40px;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
width: 100%;
height: 40px;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.content {
width: 100%;
height: calc(100% - 50px);
margin-top: 10px;
border: 1px solid $border-color;
padding: 30px;
overflow-x: hidden;
overflow-y: auto;
}
}
}
.content {
width: 100%;
height: calc(100% - 50px);
margin-top: 10px;
border: 1px solid $border-color;
padding: 30px;
overflow-x: hidden;
overflow-y: auto;
}
}
}
.message-drawer-box {
.body {
margin-bottom: 20px;
}
.footer {
position: fixed;
bottom: 6px;
background: white;
}
}
}</style>

View File

@ -1,21 +1,27 @@
<template>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="getData(upMonth)"><ArrowLeft /></el-icon>
<el-date-picker v-model="currentMonth" format="YYYY年MM月" type="month" :editable="false" :clearable="false" @change="getData" />
<el-icon @click="getData(downMonth)"><ArrowRight /></el-icon>
</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
<div class="total-mini">本月共计{{ total }}台手术</div>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="getData(upMonth)">
<ArrowLeft/>
</el-icon>
<el-date-picker v-model="currentMonth" format="YYYY年MM月" type="month" :editable="false" :clearable="false"
@change="getData"/>
<el-icon @click="getData(downMonth)">
<ArrowRight/>
</el-icon>
</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
<div class="total-mini">本月共计{{ total }}台手术</div>
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import {onMounted, ref} from 'vue'
import * as echarts from 'echarts'
import { dateFormater, getMonthDays } from '@/utils/date-util'
import {dateFormater, getMonthDays} from '@/utils/date-util'
import * as medicineApi from "@/api/medicine";
const chartDom = ref()
@ -25,159 +31,173 @@ const downMonth = ref()
const total = ref(0)
onMounted(() => {
getData(new Date())
init();
})
// true
function calcTime(time: any) {
return Boolean(new Date().getTime() < new Date(time).getTime())
function init() {
getData(new Date())
}
function initChart(chartData: any) {
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: [],
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
return dateFormater('yyyy年MM月', currentMonth.value) + params[0].axisValue + '<br>手术数量 <b>' + params[0].value + '</b> 台'
}
},
grid: {
left: 20,
right: 50,
bottom: 5,
top: 50,
containLabel: true,
},
xAxis: {
name: '日期',
show: true,
type: 'category',
boundaryGap: false,
nameTextStyle: { color: '#909399' },
axisLine: { show: true, lineStyle: { color: '#006080', width: 2 } },
axisTick: { show: false },
axisLabel: { show: true, interval: chartData.xData.length - 2 },
splitLine: { show: true, lineStyle: { color: 'rgba(0, 96, 128, .05)', width: 1, type: 'solid' } },
data: chartData.xData,
},
yAxis: {
name: '数量',
show: true,
type: 'value',
min: 0,
nameTextStyle: { color: '#909399', align: 'center', verticalAlign: 'top', padding: [0, 0] },
axisLine: { show: true, lineStyle: { color: '#006080', width: 2 } },
axisTick: { show: false },
axisLabel: { show: false },
splitLine: { show: false, lineStyle: { color: '#D4E8F0', width: 1, type: 'solid' } },
},
series: [{
type: 'line',
symbol: 'none',
smooth: true,
showSymbol: false,
lineStyle: {
color: 'rgb(0, 96, 128)',
width: 2,
shadowColor: 'rgba(0, 96, 128, .3)',
shadowBlur: 10,
shadowOffsetY: 20
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(0, 96, 128, 1)'
}, {
offset: 0.8,
color: 'rgba(255, 255, 255, 0.1)'
}], false)
},
data: chartData.data
}],
};
chart.setOption(option);
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: [],
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
return dateFormater('yyyy年MM月', currentMonth.value) + params[0].axisValue + '<br>手术数量 <b>' + params[0].value + '</b> 台'
}
},
grid: {
left: 20,
right: 50,
bottom: 5,
top: 50,
containLabel: true,
},
xAxis: {
name: '日期',
show: true,
type: 'category',
boundaryGap: false,
nameTextStyle: {color: '#909399'},
axisLine: {show: true, lineStyle: {color: '#006080', width: 2}},
axisTick: {show: false},
axisLabel: {show: true, interval: chartData.xData.length - 2},
splitLine: {show: true, lineStyle: {color: 'rgba(0, 96, 128, .05)', width: 1, type: 'solid'}},
data: chartData.xData,
},
yAxis: {
name: '数量',
show: true,
type: 'value',
min: 0,
nameTextStyle: {color: '#909399', align: 'center', verticalAlign: 'top', padding: [0, 0]},
axisLine: {show: true, lineStyle: {color: '#006080', width: 2}},
axisTick: {show: false},
axisLabel: {show: false},
splitLine: {show: false, lineStyle: {color: '#D4E8F0', width: 1, type: 'solid'}},
},
series: [{
type: 'line',
symbol: 'none',
smooth: true,
showSymbol: false,
lineStyle: {
color: 'rgb(0, 96, 128)',
width: 2,
shadowColor: 'rgba(0, 96, 128, .3)',
shadowBlur: 10,
shadowOffsetY: 20
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(0, 96, 128, 1)'
}, {
offset: 0.8,
color: 'rgba(255, 255, 255, 0.1)'
}], false)
},
data: chartData.data
}],
};
chart.setOption(option);
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
});
});
}
const getData = (date: any) => {
currentMonth.value = new Date(date)
upMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() - 1, 1)
downMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 1)
const days = getMonthDays(currentMonth.value)
const xData = []
const data = []
let num = 0
for (let i = 0; i < days; i++) {
xData.push((i < 9 ? '0' : '') + (i + 1))
data.push(Math.ceil(Math.random() * 10))
num += data[i]
currentMonth.value = new Date(date)
upMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() - 1, 1)
downMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 1)
const xData: any = []
const data: any = []
let num = 0
const start = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth(), 1)
const end = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 0)
medicineApi.getSurgeryCount(dateFormater("yyyy-MM-dd", start), dateFormater("yyyy-MM-dd", end)).then(res => {
if (res.code == 0) {
res.data.forEach((item: any) => {
xData.push(item.time)
data.push(item.count)
num += item.count
})
total.value = num
}
total.value = num
initChart({ xData, data })
initChart({xData, data})
})
}
</script>
<style lang='scss' scoped>
.chart-box {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.date-btn {
position: absolute;
height: 50px;
top: 0;
left: 20px;
right: 230px;
display: flex;
justify-content: space-between;
z-index: 1;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
&>:deep(.el-date-editor) {
font-size: 14px;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
width: 100%;
height: 100%;
}
.total-mini {
position: absolute;
top: 0;
right: 40px;
color: $main-color;
border: 1px solid $main-color;
font-size: 14px;
padding: 3px 8px;
.chart-box {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.date-btn {
position: absolute;
height: 50px;
top: 0;
left: 20px;
right: 230px;
display: flex;
justify-content: space-between;
z-index: 1;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
& > :deep(.el-date-editor) {
font-size: 14px;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
width: 100%;
height: 100%;
}
.total-mini {
position: absolute;
top: 0;
right: 40px;
color: $main-color;
border: 1px solid $main-color;
font-size: 14px;
padding: 3px 8px;
}
}
</style>

View File

@ -1,27 +1,30 @@
<template>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="getData(upMonth)">
<ArrowLeft />
</el-icon>
<el-date-picker v-model="currentMonth" format="YYYY年MM月" type="month" :editable="false" :clearable="false" @change="getData" />
<el-icon @click="getData(downMonth)">
<ArrowRight />
</el-icon>
</div>
</div>
<div class="total-box">
<div class="total-item" v-for="item in pieData" :key="item.name">{{ item.name }}:{{ item.value }}</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="getData(upMonth)">
<ArrowLeft/>
</el-icon>
<el-date-picker v-model="currentMonth" format="YYYY年MM月" type="month" :editable="false" :clearable="false"
@change="getData"/>
<el-icon @click="getData(downMonth)">
<ArrowRight/>
</el-icon>
</div>
</div>
<div class="total-box">
<div class="total-item" v-for="item in pieData" :key="item.name">{{ item.name }}:{{ item.value }}</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import {onMounted, ref} from 'vue'
import * as echarts from 'echarts';
import { dateFormater, getMonthDays } from '@/utils/date-util';
import * as medicineApi from '@/api/medicine'
import {dateFormater, getWeekDates} from "@/utils/date-util";
import {generateRandomColor} from "@/utils/other";
const chartDom = ref()
@ -31,140 +34,156 @@ const downMonth = ref()
const pieData = ref([] as any)
onMounted(() => {
getData(new Date())
getData(new Date())
})
function initChart(chartData: any) {
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: chartData.color,
tooltip: {
trigger: 'item',
formatter: (params: any) => {
return params.marker + params.name + '<br>数量:' + params.value + '<br>占比:' + params.percent + '%'
}
},
grid: {
left: 0,
right: 0,
bottom: 0,
top: 0,
containLabel: true,
},
series: [{
type: 'pie',
center: ['50%', '50%'],
radius: ['0%', '60%'],
clockwise: true,
label: {
show: true,
position: 'outter',
color: 'inherit',
overflow: 'break',
formatter: function (params: any) {
return params.name + params.percent + '%'
}
},
labelLine: {
show: false,
length: 3,
length2: 0,
smooth: true,
},
data: chartData.data
}],
};
chart.setOption(option);
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: chartData.color,
tooltip: {
trigger: 'item',
formatter: (params: any) => {
return params.marker + params.name + '<br>数量:' + params.value + '<br>占比:' + params.percent + '%'
}
},
grid: {
left: 0,
right: 0,
bottom: 0,
top: 0,
containLabel: true,
},
series: [{
type: 'pie',
center: ['50%', '50%'],
radius: ['0%', '60%'],
clockwise: true,
label: {
show: true,
position: 'outter',
color: 'inherit',
overflow: 'break',
formatter: function (params: any) {
return params.name + params.percent + '%'
}
},
labelLine: {
show: false,
length: 3,
length2: 0,
smooth: true,
},
data: chartData.data
}],
};
chart.setOption(option);
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
});
});
}
const getData = (date: any) => {
currentMonth.value = new Date(date)
upMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() - 1, 1)
downMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 1)
const types = ['心脏病手术', '骨科手术', '阑尾炎手术']
const color = ['#ffde69', '#f8b300', '#006080']
pieData.value = []
types.forEach((item: string, index: number) => {
pieData.value.push({value: Math.ceil(Math.random() * 10), name: item, itemStyle: {
color: color[index]
}})
})
initChart({ data: pieData.value, color })
currentMonth.value = new Date(date)
upMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() - 1, 1)
downMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 1)
const color: any = []
pieData.value = []
const start = dateFormater("yyyy-MM-dd", new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth(), 1))
const end = dateFormater("yyyy-MM-dd", new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 0))
medicineApi.getSurgeryTypeProportion(start, end).then(res => {
if (res.code == 0) {
res.data.forEach((item: any, i: number) => {
const c = generateRandomColor()
color.push(c)
pieData.value.push({
value: item.count,
name: item._id,
itemStyle: {
color: c
}
})
})
}
initChart({data: pieData.value, color})
});
}
</script>
<style lang='scss' scoped>
.chart-box {
position: relative;
width: 100%;
height: 100%;
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.date-btn {
position: absolute;
height: 50px;
top: 0;
left: 20px;
right: 20px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 1;
.date-btn {
position: absolute;
height: 50px;
top: 0;
left: 20px;
right: 20px;
display: flex;
z-index: 1;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
&>:deep(.el-date-editor) {
font-size: 14px;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
width: 70%;
height: 100%;
margin-left: 30%;
}
.total-box {
position: absolute;
width: 30%;
top: 30px;
left: 20px;
bottom: 0;
color: $text2-color;
& > :deep(.el-date-editor) {
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
.total-item {
padding: 5px 0;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
width: 70%;
height: 100%;
margin-left: 30%;
}
.total-box {
position: absolute;
width: 30%;
top: 30px;
left: 20px;
bottom: 0;
color: $text2-color;
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
.total-item {
padding: 5px 0;
}
}
}</style>

View File

@ -1,200 +1,211 @@
<template>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="setDate('up')">
<ArrowLeft />
</el-icon>
<el-date-picker v-model="currentMonth" format="第ww周" type="week" :editable="false" :clearable="false"
@change="getData" />
<el-icon @click="setDate('down')">
<ArrowRight />
</el-icon>
</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="setDate('up')">
<ArrowLeft/>
</el-icon>
<el-date-picker v-model="currentMonth" format="第ww周" type="week" :editable="false" :clearable="false"
@change="getData"/>
<el-icon @click="setDate('down')">
<ArrowRight/>
</el-icon>
</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import {onMounted, ref} from 'vue'
import * as echarts from 'echarts';
import { dateFormater, getFirstDayOfWeek, getCurrentDate, getDays } from '@/utils/date-util';
import {dateFormater, getCurrentDate, getDays, getFirstDayOfWeek, getWeekDates} from '@/utils/date-util';
import * as medicineApi from "@/api/medicine";
const chartDom = ref()
const currentMonth = ref(new Date())
onMounted(() => {
getData(new Date())
getData(new Date())
})
// true
function calcTime(time: any) {
return Boolean(new Date().getTime() < new Date(time).getTime())
return Boolean(new Date().getTime() < new Date(time).getTime())
}
function initChart(chartData: any) {
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: ['#006080', '#f8b300'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params: any) => {
let str = dateFormater('yyyy-MM-dd', params[0].axisValue) + '<b style="padding: 0 10px;">' + getCurrentDate(params[0].axisValue) + '</b>'
params.forEach((item: any) => {
str += '<br>'
str += item.marker + item.seriesName + '' + item.value + '小时'
})
return str
}
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: ['#006080', '#f8b300'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params: any) => {
let str = dateFormater('yyyy-MM-dd', params[0].axisValue) + '<b style="padding: 0 10px;">' + getCurrentDate(params[0].axisValue) + '</b>'
params.forEach((item: any) => {
str += '<br>'
str += item.marker + item.seriesName + '' + item.value + '小时'
})
return str
}
},
legend: {
right: 0,
data: chartData.dataName
},
grid: {
left: 20,
right: 50,
bottom: 5,
top: 50,
containLabel: true,
},
xAxis: {
name: '日期',
show: true,
type: 'category',
nameTextStyle: {color: '#909399'},
axisLine: {show: true, lineStyle: {color: '#006080', width: 2}},
axisTick: {show: false},
axisLabel: {show: true, color: '#909399', formatter: (value: any) => getCurrentDate(value)},
splitLine: {show: true, lineStyle: {color: 'rgba(212, 130, 1, .05)', width: 1, type: 'solid'}},
data: chartData.xData,
},
yAxis: {
name: '时间',
show: true,
type: 'value',
min: 0,
nameTextStyle: {color: '#909399', align: 'center', verticalAlign: 'top', padding: [0, 0]},
axisLine: {show: true, lineStyle: {color: '#006080', width: 2}},
axisTick: {show: false},
axisLabel: {show: false},
splitLine: {show: false, lineStyle: {color: '#D4E8F0', width: 1, type: 'solid'}},
},
series: [{
name: chartData.dataName[0],
type: 'bar',
label: {
show: true,
rotate: -90,
align: 'left',
verticalAlign: 'center',
position: 'insideTop',
color: '#ffffff',
textBorderColor: '#006080',
textBorderWidth: 3,
formatter: (params: any) => params.value.toFixed(0) + 'h' + Number((params.value % 1).toFixed(1)) * 60 + 'm'
},
data: chartData.data[0]
},
{
name: chartData.dataName[1],
type: 'bar',
label: {
show: true,
rotate: -90,
align: 'left',
verticalAlign: 'center',
position: 'insideTop',
color: '#ffffff',
textBorderColor: '#f8b300',
textBorderWidth: 3,
formatter: (params: any) => params.value.toFixed(0) + 'h' + Number((params.value % 1).toFixed(1)) * 60 + 'm'
},
legend: {
right: 0,
data: chartData.dataName
},
grid: {
left: 20,
right: 50,
bottom: 5,
top: 50,
containLabel: true,
},
xAxis: {
name: '日期',
show: true,
type: 'category',
nameTextStyle: { color: '#909399' },
axisLine: { show: true, lineStyle: { color: '#006080', width: 2 } },
axisTick: { show: false },
axisLabel: { show: true, color: '#909399', formatter: (value: any) => getCurrentDate(value) },
splitLine: { show: true, lineStyle: { color: 'rgba(212, 130, 1, .05)', width: 1, type: 'solid' } },
data: chartData.xData,
},
yAxis: {
name: '时间',
show: true,
type: 'value',
min: 0,
nameTextStyle: { color: '#909399', align: 'center', verticalAlign: 'top', padding: [0, 0] },
axisLine: { show: true, lineStyle: { color: '#006080', width: 2 } },
axisTick: { show: false },
axisLabel: { show: false },
splitLine: { show: false, lineStyle: { color: '#D4E8F0', width: 1, type: 'solid' } },
},
series: [{
name: chartData.dataName[0],
type: 'bar',
label: {
show: true,
rotate: -90,
align: 'left',
verticalAlign: 'center',
position: 'insideTop',
color: '#ffffff',
textBorderColor: '#006080',
textBorderWidth: 3,
formatter: (params: any) => params.value.toFixed(0) + 'h' + Number((params.value % 1).toFixed(1)) * 60 + 'm'
},
data: chartData.data[0]
},
{
name: chartData.dataName[1],
type: 'bar',
label: {
show: true,
rotate: -90,
align: 'left',
verticalAlign: 'center',
position: 'insideTop',
color: '#ffffff',
textBorderColor: '#f8b300',
textBorderWidth: 3,
formatter: (params: any) => params.value.toFixed(0) + 'h' + Number((params.value % 1).toFixed(1)) * 60 + 'm'
},
data: chartData.data[1]
}],
};
chart.setOption(option);
data: chartData.data[1]
}],
};
chart.setOption(option);
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
});
});
}
const setDate = (type: string) => {
getData(getDays(currentMonth.value, type === 'up' ? -7 : 7))
getData(getDays(currentMonth.value, type === 'up' ? -7 : 7))
}
const getData = (date: any) => {
currentMonth.value = new Date(date)
const dataName = ['麻醉给药时长', '人工给药时长']
const xData = []
const data = [[], []] as any
for (let i = 1; i < 8; i++) {
xData.push(getFirstDayOfWeek(date, i))
data[0].push(Number((Math.random() * 16 + 2).toFixed(1)))
data[1].push(Number((Math.random() * 16 + 2).toFixed(1)))
currentMonth.value = new Date(date)
const dataName = ['麻醉给药时长', '人工给药时长']
const xData: any = []
const data = [[], []] as any
const weekDates = getWeekDates(currentMonth.value)
const start = dateFormater("yyyy-MM-dd", weekDates.start)
const end = dateFormater("yyyy-MM-dd", weekDates.end.setDate(weekDates.end.getDate() + 1))
medicineApi.getSurgeryOtherDuration(start, end).then(res => {
if (res.code == 0) {
res.data.forEach((item: any) => {
xData.push(new Date(item._id))
data[0].push(item.aicount)
data[1].push(item.doccount)
})
}
initChart({ dataName, xData, data })
initChart({dataName, xData, data})
})
}
</script>
<style lang='scss' scoped>
.chart-box {
position: relative;
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.date-btn {
position: absolute;
width: 150px;
height: 50px;
top: 0;
left: 20px;
display: flex;
z-index: 1;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
& > :deep(.el-date-editor) {
font-size: 14px;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
// width: calc(100% - 180px);
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.date-btn {
position: absolute;
width: 150px;
height: 50px;
top: 0;
left: 20px;
display: flex;
z-index: 1;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
&>:deep(.el-date-editor) {
font-size: 14px;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
// width: calc(100% - 180px);
width: 100%;
height: 100%;
}
}
}</style>

View File

@ -1,25 +1,27 @@
<template>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="getData(upMonth)">
<ArrowLeft />
</el-icon>
<el-date-picker v-model="currentMonth" format="YYYY年MM月" type="month" :editable="false" :clearable="false" @change="getData" />
<el-icon @click="getData(downMonth)">
<ArrowRight />
</el-icon>
</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
<div class="total-mini">本月共计{{ total.toFixed(0) }}小时{{ Number((total % 1).toFixed(1)) * 60 }}分钟</div>
<div class="chart-box">
<div class="date-btn text-color">
<div class="btn-box">
<el-icon @click="getData(upMonth)">
<ArrowLeft/>
</el-icon>
<el-date-picker v-model="currentMonth" format="YYYY年MM月" type="month" :editable="false" :clearable="false"
@change="getData"/>
<el-icon @click="getData(downMonth)">
<ArrowRight/>
</el-icon>
</div>
</div>
<div ref="chartDom" class="chart-dom"></div>
<div class="total-mini">本月共计{{ total.toFixed(0) }}小时{{ Number((total % 1).toFixed(1)) * 60 }}分钟</div>
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import {onMounted, ref} from 'vue'
import * as echarts from 'echarts';
import { dateFormater, getMonthDays } from '@/utils/date-util';
import {dateFormater, getMonthDays} from '@/utils/date-util';
import * as medicineApi from '@/api/medicine';
const chartDom = ref()
@ -29,160 +31,169 @@ const downMonth = ref()
const total = ref(0)
onMounted(() => {
getData(new Date())
getData(new Date())
})
function initChart(chartData: any) {
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: [],
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
return dateFormater('yyyy年MM月', currentMonth.value) + params[0].axisValue + '<br>手术时长 <b>' + params[0].value + '</b> h'
}
},
grid: {
left: 20,
right: 50,
bottom: 5,
top: 50,
containLabel: true,
},
xAxis: {
name: '日期',
show: true,
type: 'category',
boundaryGap: false,
nameTextStyle: { color: '#909399' },
axisLine: { show: true, lineStyle: { color: '#D58301', width: 2 } },
axisTick: { show: false },
axisLabel: { show: true, interval: chartData.xData.length - 2 },
splitLine: { show: true, lineStyle: { color: 'rgba(212, 130, 1, .05)', width: 1, type: 'solid' } },
data: chartData.xData,
},
yAxis: {
name: '时间',
show: true,
type: 'value',
min: 0,
nameTextStyle: { color: '#909399', align: 'center', verticalAlign: 'top', padding: [0, 0] },
axisLine: { show: true, lineStyle: { color: '#D58301', width: 2 } },
axisTick: { show: false },
axisLabel: { show: false },
splitLine: { show: false, lineStyle: { color: '#D4E8F0', width: 1, type: 'solid' } },
},
series: [{
type: 'line',
symbol: 'none',
smooth: true,
showSymbol: false,
lineStyle: {
color: 'rgb(212, 130, 1)',
width: 2,
shadowColor: 'rgba(212, 130, 1, .3)',
shadowBlur: 10,
shadowOffsetY: 20
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(212, 130, 1, 1)'
}, {
offset: 0.8,
color: 'rgba(255, 255, 255, 0.1)'
}], false)
},
data: chartData.data
}],
};
chart.setOption(option);
const chart = echarts.init(chartDom.value as HTMLElement);
chart.clear();
const option = {
color: [],
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
return dateFormater('yyyy年MM月', currentMonth.value) + params[0].axisValue + '<br>手术时长 <b>' + params[0].value + '</b> h'
}
},
grid: {
left: 20,
right: 50,
bottom: 5,
top: 50,
containLabel: true,
},
xAxis: {
name: '日期',
show: true,
type: 'category',
boundaryGap: false,
nameTextStyle: {color: '#909399'},
axisLine: {show: true, lineStyle: {color: '#D58301', width: 2}},
axisTick: {show: false},
axisLabel: {show: true, interval: chartData.xData.length - 2},
splitLine: {show: true, lineStyle: {color: 'rgba(212, 130, 1, .05)', width: 1, type: 'solid'}},
data: chartData.xData,
},
yAxis: {
name: '时间',
show: true,
type: 'value',
min: 0,
nameTextStyle: {color: '#909399', align: 'center', verticalAlign: 'top', padding: [0, 0]},
axisLine: {show: true, lineStyle: {color: '#D58301', width: 2}},
axisTick: {show: false},
axisLabel: {show: false},
splitLine: {show: false, lineStyle: {color: '#D4E8F0', width: 1, type: 'solid'}},
},
series: [{
type: 'line',
symbol: 'none',
smooth: true,
showSymbol: false,
lineStyle: {
color: 'rgb(212, 130, 1)',
width: 2,
shadowColor: 'rgba(212, 130, 1, .3)',
shadowBlur: 10,
shadowOffsetY: 20
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(212, 130, 1, 1)'
}, {
offset: 0.8,
color: 'rgba(255, 255, 255, 0.1)'
}], false)
},
data: chartData.data
}],
};
chart.setOption(option);
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
window.addEventListener('resize', () => {
chart.resize();
});
});
}
const getData = (date: any) => {
currentMonth.value = new Date(date)
upMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() - 1, 1)
downMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 1)
const days = getMonthDays(currentMonth.value)
const xData = []
const data = []
currentMonth.value = new Date(date)
upMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() - 1, 1)
downMonth.value = new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 1)
const start = dateFormater("yyyy-MM-dd", new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth(), 1))
const end = dateFormater("yyyy-MM-dd", new Date(currentMonth.value.getFullYear(), currentMonth.value.getMonth() + 1, 0))
medicineApi.getSurgeryDuration(start, end).then(res => {
const xData: any = []
const data: any = []
let num = 0
for (let i = 0; i < days; i++) {
xData.push((i < 9 ? '0' : '') + (i + 1))
data.push(Number((Math.random() * 16 + 2).toFixed(1)))
num += data[i]
if (res.code == 0) {
res.data.forEach((item: any) => {
xData.push(item._id)
data.push(item.duration)
num += item.duration
})
}
total.value = Number(num.toFixed(1))
initChart({ xData, data })
initChart({xData, data})
})
}
</script>
<style lang='scss' scoped>
.chart-box {
position: relative;
width: 100%;
height: 100%;
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.date-btn {
position: absolute;
height: 50px;
top: 0;
left: 20px;
right: 230px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 1;
.date-btn {
position: absolute;
height: 50px;
top: 0;
left: 20px;
right: 230px;
display: flex;
justify-content: space-between;
z-index: 1;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
.btn-box {
height: 30px;
display: flex;
align-items: center;
color: $text2-color;
&>:deep(.el-date-editor) {
font-size: 14px;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
width: 100%;
height: 100%;
}
.total-mini {
position: absolute;
top: 0;
right: 40px;
color: #D58301;
border: 1px solid #D58301;
& > :deep(.el-date-editor) {
font-size: 14px;
padding: 3px 8px;
.el-input__wrapper {
box-shadow: none;
padding: 0;
.el-input__prefix,
.el-input__suffix {
display: none;
}
.el-input__inner {
cursor: pointer;
width: 100px;
color: $text2-color;
text-align: center;
}
}
}
.el-icon {
cursor: pointer;
}
}
}
.chart-dom {
width: 100%;
height: 100%;
}
.total-mini {
position: absolute;
top: 0;
right: 40px;
color: #D58301;
border: 1px solid #D58301;
font-size: 14px;
padding: 3px 8px;
}
}</style>

View File

@ -7,23 +7,22 @@
<ul class="menu-box">
<li class="menu-item" v-for="item in menus" :key="item.path"
:class="{ 'active': menuActive.indexOf(item.path) === 0 }"
@click="menuToPath(item)"><i :class="item.icon"></i><span>{{
item.label
@click="menuToPath(item)"><i :class="item.meta.icon"></i><span>{{
item.name
}}</span></li>
</ul>
<div class="user-box">
<!-- 超级管理员可切换医院 -->
<el-select v-if="userInfo.permissions === '超级管理员'" class="select-hospital" style="width: 150px;"
:model-value="userInfo.hospital" size="small" @change="selectHospital">
<el-option v-for="item in hospitals" :key="item.value" :label="item.label" :value="item.value"/>
<el-select class="select-hospital" style="width: 150px;"
v-model="hospital" size="small" @change="selectHospital">
<el-option v-for="item in hospitals" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<span v-else class="area">{{ userInfo.hospital }}</span>
<el-button text>
<el-icon>
<Search/>
</el-icon>
</el-button>
<el-button text>
<!-- <el-button text>
<el-icon>
<Search/>
</el-icon>
</el-button>-->
<el-button text @click="userStore.showHomeMsg=true">
<el-badge is-dot>
<el-icon>
<Bell/>
@ -36,12 +35,12 @@
</el-icon>
</el-button>
<el-dropdown trigger="click" @command="userCommand">
<span class="el-dropdown-link">
{{ userInfo.name }}
<el-icon class="el-icon--right">
<arrow-down/>
</el-icon>
</span>
<span class="el-dropdown-link">
{{ userInfo.name }}
<el-icon class="el-icon--right">
<arrow-down/>
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="info">个人信息</el-dropdown-item>
@ -61,59 +60,22 @@
</template>
<script lang='ts' setup>
import {onMounted, reactive, ref, toRefs, watch} from 'vue'
import {useRouter, useRoute} from 'vue-router'
import {onMounted, ref} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {ElMessage, ElMessageBox} from 'element-plus'
import {useLoginStore} from '@/stores/user-info-store'
import {getHospitalsData} from '@/static-data/core'
import {useUserStore} from '@/stores/user-info-store'
import userInfoForm from '@/components/user-info.vue'
import {Session} from "@/utils/storage";
import {logout} from "@/api/login";
import * as hospitalApi from "@/api/hospital";
const router = useRouter()
const route = useRoute()
const userInfo = useLoginStore().getlogin()
const userStore = useUserStore();
const userInfo = userStore.getlogin()
const hospital = ref("");
const preHospital = ref("");
const hospitals = ref([] as any)
getHospitalsData().then((res: any) => {
hospitals.value = res
})
const menus = [] as any
switch (userInfo.permissions) {
case '超级管理员':
menus.push({label: '首页', path: '/home', icon: 'icon-shouye'})
menus.push({label: '权限管理', path: '/permissions-manage', icon: 'icon-users'})
menus.push({label: '患者管理', path: '/patients-manage', icon: 'icon-renyuanguanli'})
menus.push({label: '远程管理', path: '/remote-manage', icon: 'icon-anquanbaozhang'})
menus.push({label: '后台管理', path: '/system-manage', icon: 'icon-houtaiguanli'})
menus.push({label: '系统管理', path: '/logs-manage', icon: 'icon-setting'})
break;
case '高级管理员':
menus.push({label: '首页', path: '/home', icon: 'icon-shouye'})
menus.push({label: '权限管理', path: '/permissions-manage', icon: 'icon-users'})
menus.push({label: '患者管理', path: '/patients-manage', icon: 'icon-renyuanguanli'})
menus.push({label: '远程管理', path: '/remote-manage', icon: 'icon-anquanbaozhang'})
menus.push({label: '系统管理', path: '/logs-manage', icon: 'icon-setting'})
break;
case '中级管理员':
menus.push({label: '首页', path: '/home', icon: 'icon-shouye'})
menus.push({label: '患者管理', path: '/patients-manage', icon: 'icon-renyuanguanli'})
menus.push({label: '远程管理', path: '/remote-manage', icon: 'icon-anquanbaozhang'})
break;
case '普通用户':
menus.push({label: '首页', path: '/home', icon: 'icon-shouye'})
menus.push({label: '患者管理', path: '/patients-manage', icon: 'icon-renyuanguanli'})
break;
default:
menus.push({label: '首页', path: '/home', icon: 'icon-shouye'})
menus.push({label: '权限管理', path: '/permissions-manage', icon: 'icon-users'})
menus.push({label: '患者管理', path: '/patients-manage', icon: 'icon-renyuanguanli'})
menus.push({label: '远程管理', path: '/remote-manage', icon: 'icon-anquanbaozhang'})
menus.push({label: '后台管理', path: '/system-manage', icon: 'icon-houtaiguanli'})
menus.push({label: '系统管理', path: '/logs-manage', icon: 'icon-setting'})
}
const menus = ref([] as any)
const isShowUserInfoDrawer = ref(false)
const menuActive = ref('/')
@ -126,18 +88,45 @@ router.beforeEach((to, from, next) => {
});
onMounted(() => {
init();
})
function init() {
getHospitalList();
handleMenu();
}
function handleMenu() {
useUserStore().getMenuPathList().then((res: any) => {
menus.value = res.menus;
});
}
const menuToPath = (e: any) => {
menuActive.value = e.path
router.push(e.path)
}
async function getHospitalList() {
const data: any = await hospitalApi.getCurrentHospital();
hospitalApi.getMyHospitalList().then((res: any) => {
hospitals.value = [];
if (res.code == 0 && res.data.length > 0) {
hospitals.value = res.data;
if (data.data) {
hospital.value = data.data;
}
preHospital.value = hospital.value
}
})
}
//
const selectHospital = (e: any) => {
if (hospitals.value.length == 0) return;
//
ElMessageBox.confirm(
'是否跳转到登录页面登录到所选择医院?',
'是否跳转到所选择医院?',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
@ -145,9 +134,16 @@ const selectHospital = (e: any) => {
draggable: true
}
).then(() => {
// useLoginStore().setlogin('hospital', e)
router.push('/login')
hospitalApi.changeHospital(hospital.value).then((res: any) => {
if (res.code == 0) {
window.location.reload();
} else {
hospital.value = preHospital.value;
ElMessage.error("切换失败")
}
})
}).catch(() => {
hospital.value = preHospital.value;
})
}
const toggleFullscreen = () => {
@ -157,13 +153,13 @@ const toggleFullscreen = () => {
document.documentElement.requestFullscreen();
}
}
const userCommand = async (e: string) => {
switch (e) {
case 'logout':
// useLoginStore().logout()
await logout()
Session.clear();
window.location.reload();
useUserStore().logout();
window.location.replace("/");
break;
case 'info':
isShowUserInfoDrawer.value = true

View File

@ -8,120 +8,120 @@
</el-select>
</div>
<img v-show="!isShowRegister" class="logo move_2" src="@/assets/imgs/logo.png">
<div v-if="!isShowRegister" class="login-block move_2">
<div class="login-way">
<span :class="passwordLogin && 'active'" @click="passwordLogin = true">密码登录</span>
<span :class="!passwordLogin && 'active'" @click="passwordLogin = false">验证码登录</span>
</div>
<el-form ref="loginFormRef" :model="loginParams" :rules="loginRules" label-width="0" size="small">
<div class="login-form password-login" v-if="passwordLogin">
<el-form-item prop="account">
<el-input v-model="loginParams.account" placeholder="请输入用户名">
<template #prepend>
<el-icon>
<User/>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginParams.password" type="password" show-password placeholder="请输入密码">
<template #prepend>
<el-icon>
<Lock/>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-button :loading="loading" class="login-btn" type="primary" @click="login('password')">登录</el-button>
<span class="register-btn" @click="getCaptchaCode()">注册账号</span>
</div>
<div class="login-form code-login" v-else>
<el-form-item prop="phone">
<el-input v-model="loginParams.phone" placeholder="请输入手机号">
<template #prepend>
<div @click.stop style="display: flex;align-items: center;">
<el-dropdown @command="selectPhoneArea">
<img v-show="!isShowRegister" class="logo move_2" src="@/assets/imgs/logo.png">
<div v-if="!isShowRegister" class="login-block move_2">
<div class="login-way">
<span :class="passwordLogin && 'active'" @click="passwordLogin = true">密码登录</span>
<span :class="!passwordLogin && 'active'" @click="passwordLogin = false">验证码登录</span>
</div>
<el-form ref="loginFormRef" :model="loginParams" :rules="loginRules" label-width="0" size="small">
<div class="login-form password-login" v-if="passwordLogin">
<el-form-item prop="account">
<el-input v-model="loginParams.account" placeholder="请输入用户名">
<template #prepend>
<el-icon>
<User/>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginParams.password" type="password" show-password placeholder="请输入密码">
<template #prepend>
<el-icon>
<Lock/>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-button :loading="loading" class="login-btn" type="primary" @click="login('password')">登录</el-button>
<span class="register-btn" @click="getCaptchaCode()">注册账号</span>
</div>
<div class="login-form code-login" v-else>
<el-form-item prop="phone">
<el-input v-model="loginParams.phone" placeholder="请输入手机号">
<template #prepend>
<div @click.stop style="display: flex;align-items: center;">
<el-dropdown @command="selectPhoneArea">
<span style="color: #909399;">{{ loginParams.phoneArea }}<el-icon>
<DCaret/>
</el-icon></span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in phoneAreas" :command="item">{{
item
}}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-input>
</el-form-item>
<el-form-item prop="code">
<el-input v-model="loginParams.code" placeholder="请输入6位短信验证码">
<template #append>
<span class="send-btn" @click="sendCode">{{ loginParams.sendText }}</span>
</template>
</el-input>
</el-form-item>
<el-button class="login-btn" type="primary" @click="login('code')">登录</el-button>
</div>
</el-form>
</div>
<div v-else class="register-block move_2">
<div class="header-box">
<span class="title">新用户申请</span>
<el-icon @click="isShowRegister = false">
<Close/>
</el-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in phoneAreas" :command="item">{{
item
}}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-input>
</el-form-item>
<el-form-item prop="code">
<el-input v-model="loginParams.code" placeholder="请输入6位短信验证码">
<template #append>
<span class="send-btn" @click="sendCode">{{ loginParams.sendText }}</span>
</template>
</el-input>
</el-form-item>
<el-button class="login-btn" type="primary" @click="login('code')">登录</el-button>
</div>
</el-form>
</div>
<div v-else class="register-block move_2">
<div class="header-box">
<span class="title">新用户申请</span>
<el-icon @click="isShowRegister = false">
<Close/>
</el-icon>
</div>
<el-form ref="registerFormRef" :model="registerParams" :rules="registerRules" label-width="100">
<el-form-item label="用户名" prop="username">
<el-input v-model="registerParams.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="registerParams.password" type="password" show-password
placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="registerParams.name" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="registerParams.phone" placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item label="医院" prop="hospital">
<el-select v-model="registerParams.hospital" style="width: 100%;">
<el-option v-for="item in hospitals" :key="item.id" :label="item.name"
:value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="registerParams.sex">
<el-radio label="男"/>
<el-radio label="女"/>
</el-radio-group>
</el-form-item>
<el-form-item label="验证码" prop="code">
<el-input v-model="registerParams.code" placeholder="请输入验证码"
style="width: calc(100% - 110px);"></el-input>
<img width="100" height="32" style="margin-left: 10px;" :src="captchaImgUrl"/>
<a class="change_img" @click="refreshImg" href="#">看不清</a>
</el-form-item>
<el-form ref="registerFormRef" :model="registerParams" :rules="registerRules" label-width="100">
<el-form-item label="用户名" prop="username">
<el-input v-model="registerParams.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="registerParams.password" type="password" show-password
placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="registerParams.name" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="registerParams.phone" placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item label="医院" prop="hospital">
<el-select v-model="registerParams.hospital" style="width: 100%;">
<el-option v-for="item in hospitals" :key="item.id" :label="item.name"
:value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="registerParams.sex">
<el-radio label="男"/>
<el-radio label="女"/>
</el-radio-group>
</el-form-item>
<el-form-item label="验证码" prop="code">
<el-input v-model="registerParams.code" placeholder="请输入验证码"
style="width: calc(100% - 110px);"></el-input>
<img width="100" height="32" style="margin-left: 10px;" :src="captchaImgUrl"/>
<a class="change_img" @click="refreshImg" href="#">看不清</a>
</el-form-item>
</el-form>
<div class="footer-box">
<el-button type="primary" @click="register"> </el-button>
<span @click="isShowRegister = false">已有账号?</span>
</div>
</div>
</el-form>
<div class="footer-box">
<el-button type="primary" @click="register"> </el-button>
<span @click="isShowRegister = false">已有账号?</span>
</div>
</div>
</div>
</div>
<SliderVerify v-model:isShowSelf="sliderVConf.isShowSelf" :width="sliderVConf.width" :imgUrl="sliderImgUrl"
:height="sliderVConf.height" @success="sliderSuccess" @close="sliderClose"></SliderVerify>
</div>
</div>
<SliderVerify v-model:isShowSelf="sliderVConf.isShowSelf" :width="sliderVConf.width" :imgUrl="sliderImgUrl"
:height="sliderVConf.height" @success="sliderSuccess" @close="sliderClose"></SliderVerify>
</template>
<script lang='ts' setup>
@ -130,7 +130,7 @@ import {ElMessage, ElMessageBox, ElNotification} from 'element-plus';
import {getTime} from '@/utils/time';
import {onMounted, reactive, ref} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {useLoginStore} from '@/stores/user-info-store'
import {useUserStore} from '@/stores/user-info-store'
import {getPhoneAreasData} from '@/static-data/core'
import {v4} from "uuid";
import SliderVerify from "@/components/SliderVerify/index.vue";
@ -144,40 +144,32 @@ import * as hospitalApi from "@/api/hospital";
//let useStore = useUserStore();
const router = useRouter()
const hospitals = ref([])
const hospitals = ref([] as any)
const phoneAreas: any = getPhoneAreasData()
//
let loading = ref(false);
//
const validatorPhone = (rule: any, value: any, callback: any) => {
var isPhone = /^1[34578]\d{9}$/;
if (value.indexOf('****') >= 0) {
return callback().trim();
}
if (!isPhone.test(value)) {
callback(new Error('请输入合法手机号'));
} else {
callback();
}
}
const loginRules = reactive({
account: [
{required: true,message: '请输入用户名', trigger: 'change'},
{ min: 2, max: 18, message: "长度在 6 到 18个字符" },
{required: true, message: '请输入用户名', trigger: 'change'},
{min: 2, max: 18, message: "长度在 6 到 18个字符"},
//{pattern:/^(?![^A-Za-z]+$)(?![^0-9]+$)[0-9A-Za-z_]{4,15}$/, message: '4-15线'}
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'},
{ min: 5,max: 25,message: '长度在 5 到 25个字符'},
{min: 5, max: 25, message: '长度在 5 到 25个字符'},
{pattern: /^(\w){5,25}$/, message: '长度在5-25之间以字母、数字、下划线'}
],
phone: [
@ -190,17 +182,17 @@ const loginRules = reactive({
const registerRules = reactive({
username: [
{required: true, message: '请输入用户名', trigger: 'change'},
{ min: 2, max: 18, message: "长度在 6 到 18个字符" },
{min: 2, max: 18, message: "长度在 6 到 18个字符"},
//{pattern:/^(?![^A-Za-z]+$)(?![^0-9]+$)[0-9A-Za-z_]{4,15}$/, message: '4-15线'}
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'},
{ min: 5,max: 25,message: '长度在 5 到 25个字符'},
{min: 5, max: 25, message: '长度在 5 到 25个字符'},
{pattern: /^(\w){5,25}$/, message: '长度在5-25之间以字母、数字、下划线'}
],
name: [
{required: true, message: '请输入真实姓名', trigger: 'blur'},
{pattern:/^[\u4E00-\u9FA5]{2,6}$/, message: '请输入2-6个汉字'}
{pattern: /^[\u4E00-\u9FA5]{2,6}$/, message: '请输入2-6个汉字'}
],
phone: [
{required: true, validator: validatorPhone, trigger: 'change'},
@ -212,15 +204,10 @@ const registerRules = reactive({
{required: true, message: '请输入验证码', trigger: 'blur'},
]
})
//
let $router = useRouter();
//
let $route = useRoute();
const loginFormRef = ref()
const registerFormRef = ref()
const slideVerifyRef = ref()
const currentHospital = ref(useLoginStore().getlogin().hospital)
const currentHospital = ref("")
const isShowRegister = ref(false)
const passwordLogin = ref(true)
const loginParams = ref({
@ -264,8 +251,8 @@ onMounted(() => {
})
const selectHospital = (e: string) => {
// console.log(e)
}
const register = async () => {
await registerFormRef.value.validate((valid: any, fields: any) => {
if (valid) {
@ -313,20 +300,19 @@ const sendCode = () => {
}, 1000);
}
const login = async (type: string) => {
//:
loading.value = true;
/*if (!currentHospital.value) {
ElMessage.warning('请在右上角选择院区')
return
}*/
//:
loading.value = true;
//
await loginFormRef.value.validate((valid: any, fields: any) => {
if (valid) {
sliderVConf.value.isShowSelf = true;
}
})
//:
if (!currentHospital.value) {
ElMessage.warning('请在右上角选择院区')
return
}
//:
loading.value = true;
//
await loginFormRef.value.validate((valid: any, fields: any) => {
if (valid) {
sliderVConf.value.isShowSelf = true;
}
})
}
@ -344,7 +330,8 @@ function sliderSuccess() {
loginApi.login({
username: loginParams.value.account, //
password: loginParams.value.password, //
randomStr: v4()
randomStr: v4(),
hospitalId: currentHospital.value
}).then((data: any) => {
sliderVConf.value.isShowSelf = false
if (data.code == 1 || data.error) {
@ -352,28 +339,33 @@ function sliderSuccess() {
loading.value = false
} else {
// token
console.log(data)
ElNotification({
type: 'success',
message: '欢迎回来',
title: `HI,${getTime()}`
});
loginPost(data)
router.push('/home')
}
})
ElNotification({
type: 'success',
message: '欢迎回来',
title: `HI,${getTime()}`
});
loginPost(data)
router.removeRoute("root");
router.addRoute({
name: "root",
path: "/",
redirect: "/home"
})
router.replace('/home')
}
})
}
function sliderClose() {
loading.value = false
loading.value = false
}
const loginPost = (data: any) => {
Session.set('token', data.access_token);
Session.set('refresh_token', data.refresh_token);
useLoginStore().setlogin('account', data.username)
console.log(data)
useLoginStore().setlogin('name', data.user_info.name || '暂未设置姓名')
Session.set('token', data.access_token);
Session.set('refresh_token', data.refresh_token);
useUserStore().setlogin('account', data.username)
useUserStore().setlogin('name', data.user_info.chineseName || '暂未设置姓名')
useUserStore().setlogin("hospitalId", data.user_info.hospitalId)
}
</script>
@ -405,6 +397,11 @@ const loginPost = (data: any) => {
position: absolute;
top: 25px;
right: 25px;
.select-hospital {
width: 12rem;
margin-right: 20px;
}
}
.logo {

View File

@ -0,0 +1,185 @@
<template>
<div class="table-page">
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">词典名称</span>
<el-input v-model="queryParams" placeholder="请输入名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="search">查询</el-button>
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addDict()">新增</el-button>
</div>
<TableAbility :isDownload="false" @searchBtn="isSearch = !isSearch" @refreshBtn="init()"></TableAbility>
</div>
<div class="table-part">
<el-table ref="tableRef"
:data="tableData"
row-key="id" height="100%"
border
show-overflow-tooltip
>
<el-table-column property="description" label="描述" width="300" align="center"/>
<el-table-column property="label" label="标签名" width="260" align="center"/>
<el-table-column property="value" label="值" width="260" align="center"/>
<el-table-column property="remarks" label="备注" width="260" align="center"/>
<el-table-column property="dictType" label="类型" width="180" align="center"/>
<el-table-column property="sortOrder" label="排序" width="80" align="center"/>
<el-table-column label="操作" align="center">
<template #default="scope">
<div @click.stop>
<el-button link icon="EditPen" v-if="!scope.row.dictId" @click="addDictItem(scope.row)">添加字典项
</el-button>
<el-button link icon="EditPen" @click="edit(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="remove(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-part">
<CommonPagination :total="total" @paginationChange="paginationChange"/>
</div>
</div>
<el-dialog v-model="showFormDialog" :title="formDialogTitle" width="30%">
<DictForm ref="dictFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'"
@save-or-update-event="dictFormEvent" @close="showFormDialog = false"/>
</el-dialog>
<el-dialog v-model="showItemDialog" :title="formDialogTitle" width="30%">
<DictItemForm ref="dictItemFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'"
@save-or-update-event="dictItemFormEvent" @close="showItemDialog = false"/>
</el-dialog>
</template>
<script setup lang="ts">
import DictForm from "@/views/logs-manage/form/dict-form.vue";
import CommonPagination from "@/components/common-pagination.vue";
import {onMounted, ref} from "vue";
import * as dictApi from "@/api/dict";
import DictItemForm from "@/views/logs-manage/form/dict-item-form.vue";
import {ElMessage, ElMessageBox} from "element-plus";
const tableData = ref([] as any)
let current = 0
let size = 10
const total = ref(0)
const queryParams = ref("")
const isSearch = ref(true)
const showFormDialog = ref(false)
const showItemDialog = ref(false)
const formDialogTitle = ref('')
const tableRef = ref()
const dictFormRef = ref();
const dictItemFormRef = ref();
onMounted(() => {
init()
})
function init() {
current = 0;
total.value = 0;
queryParams.value = "";
getList()
}
function getList() {
dictApi.dictPage(current, size, queryParams.value).then((res: any) => {
if (res.code == 0) {
total.value = res.data.total
tableData.value = res.data.records;
}
})
}
function search() {
}
function resetSearch() {
}
function addDict() {
formDialogTitle.value = "添加";
showFormDialog.value = true;
setTimeout(() => {
dictFormRef.value.resetData();
})
}
function dictFormEvent() {
showFormDialog.value = false;
getList()
}
function dictItemFormEvent() {
showItemDialog.value = false;
getList()
}
function addDictItem(row: any) {
formDialogTitle.value = "添加";
showItemDialog.value = true;
setTimeout(() => {
dictItemFormRef.value.resetData(row);
})
}
function edit(row: any) {
formDialogTitle.value = "编辑";
if (row.dictId) {
showItemDialog.value = true;
setTimeout(() => {
dictItemFormRef.value.resetData(row);
})
} else {
showFormDialog.value = true;
setTimeout(() => {
dictFormRef.value.resetData(row);
})
}
}
function remove(row: any) {
ElMessageBox.confirm("是否删除?", "提示", {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true
}).then(() => {
if (row.dictId) {
dictApi.deleteDictItem(row.id).then(res => {
if (res.code == 0) {
ElMessage.success("删除成功")
getList()
} else {
ElMessage.error(res.msg ? res.msg : "删除失败")
}
})
} else {
dictApi.deleteDict([row.id]).then(res => {
if (res.code == 0) {
ElMessage.success("删除成功")
getList()
} else {
ElMessage.error(res.msg ? res.msg : "删除失败")
}
})
}
}).catch(() => {
})
}
const paginationChange = (page: number, s: number) => {
current = page - 1
size = s
getList()
}
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,114 @@
<template>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100">
<el-form-item label="名称" prop="description">
<el-input v-model="formData.description" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item v-if="props.type == 'edit'" label="字典类型">
<span>{{ formData.dictType }}</span>
</el-form-item>
<el-form-item v-else label="字典类型" prop="dictType">
<el-input v-model="formData.dictType" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="是否系统内置" prop="systemFlag">
<el-radio-group v-model="formData.systemFlag">
<el-radio value="1" border></el-radio>
<el-radio value="0" border></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="formData.remarks" placeholder="请输入"></el-input>
</el-form-item>
<div class="submit-btn">
<el-button class="f18" @click="close">取消</el-button>
<el-button class="f18" type="primary" @click="saveData">确认</el-button>
</div>
</el-form>
</template>
<script setup lang="ts">
import {reactive, ref} from "vue";
import * as dictApi from "@/api/dict";
import {ElMessage} from "element-plus";
const emit = defineEmits(['close', "saveOrUpdateEvent"])
const props = defineProps({
type: String
})
defineExpose({
resetData
})
const rules = reactive({
description: [
{required: true, message: '请输入名称', trigger: ['blur', 'change']},
],
dictType: [
{required: true, message: '请输入字典类型', trigger: ['blur', 'change']},
]
})
const formData = ref({} as any)
const formRef = ref()
function resetData(item?: any) {
if (item) {
formData.value = {
id: item.id,
description: item.description,
dictType: item.dictType,
systemFlag: item.systemFlag,
remarks: item.remarks
}
} else {
formData.value = {
id: "",
description: "",
dictType: "",
systemFlag: "0",
remarks: ""
}
}
}
function saveData() {
formRef.value.validate((valid: any, fields: any) => {
if (valid) {
const form = Object.assign({}, formData.value);
if (form.id) {
dictApi.updateDict(form).then(res => {
if (res.code == 0) {
ElMessage.success("更新成功")
emit("saveOrUpdateEvent");
} else {
ElMessage.error(res.msg ? res.msg : "更新失败")
}
})
} else {
dictApi.saveDict(form).then(res => {
if (res.code == 0) {
ElMessage.success("保存成功")
emit("saveOrUpdateEvent")
} else {
ElMessage.error(res.msg ? res.msg : "保存失败")
}
})
}
}
})
}
function close() {
emit('close')
}
</script>
<style scoped lang="scss">
.submit-btn {
text-align: right;
padding-top: 0;
}
</style>

View File

@ -0,0 +1,138 @@
<template>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100">
<el-form-item label="名称" prop="description">
<el-input v-model="formData.description" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="字典类型">
<span>{{ formData.dictType }}</span>
</el-form-item>
<el-form-item label="标签名" prop="label">
<el-input v-model="formData.label" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="数据值" prop="value">
<el-input v-model="formData.value" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="排序" prop="sortOrder">
<el-input class="number-input" v-model="formData.sortOrder"
type="number" input-style="text-align: center;"
@change="numberInputChange">
<template #prepend>
<el-button text icon="Minus" :disabled="formData.sortOrder <= 0"
@click="formData.sortOrder--"/>
</template>
<template #append>
<el-button text icon="Plus" @click="formData.sortOrder++"/>
</template>
</el-input>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="formData.remarks" placeholder="请输入"></el-input>
</el-form-item>
<div class="submit-btn">
<el-button class="f18" @click="close">取消</el-button>
<el-button class="f18" type="primary" @click="saveData">确认</el-button>
</div>
</el-form>
</template>
<script setup lang="ts">
import {reactive, ref} from "vue";
import * as dictApi from "@/api/dict";
import {ElMessage} from "element-plus";
const emit = defineEmits(['close', "saveOrUpdateEvent"])
const props = defineProps({
type: String
})
defineExpose({
resetData,
})
const rules = reactive({
description: [
{required: true, message: '请输入名称', trigger: ['blur', 'change']},
],
dictType: [
{required: true, message: '请输入字典类型', trigger: ['blur', 'change']},
]
})
const formData = ref({} as any)
const formRef = ref()
function resetData(item?: any) {
if (item.id && item.dictId) {
formData.value = {
id: item.id,
dictId: item.dictId,
description: item.description,
dictType: item.dictType,
label: item.label,
value: item.value,
sortOrder: item.sortOrder,
remarks: item.remarks
}
} else {
formData.value = {
id: "",
dictId: item.id,
description: "",
dictType: item.dictType,
label: "",
value: "",
sortOrder: "",
remarks: ""
}
}
}
function saveData() {
formRef.value.validate((valid: any, fields: any) => {
if (valid) {
const form = Object.assign({}, formData.value);
if (form.id) {
dictApi.updateDictItem(form).then(res => {
if (res.code == 0) {
ElMessage.success("更新成功")
emit("saveOrUpdateEvent");
} else {
ElMessage.error(res.msg ? res.msg : "更新失败")
}
})
} else {
dictApi.saveDictItem(form).then(res => {
if (res.code == 0) {
ElMessage.success("保存成功")
emit("saveOrUpdateEvent")
} else {
ElMessage.error(res.msg ? res.msg : "保存失败")
}
})
}
}
})
}
function close() {
emit('close')
}
const numberInputChange = (e: any) => {
if (!Number(e)) {
formData.value.sortOrder = 0
}
}
</script>
<style scoped lang="scss">
.submit-btn {
text-align: right;
padding-top: 0;
}
</style>

View File

@ -1,76 +1,78 @@
<template>
<div class="permissions-manage-page">
<div class="menu-box">
<CommonMenu :menuData="menuData" @selectMenu="selectMenu" />
</div>
<div class="content-box">
<!-- <div class="header-box">
<PageTabs ref="pageTabsRef" />
</div> -->
<div class="main-box">
<div class="background-block">
<RouterView />
</div>
</div>
</div>
<div class="permissions-manage-page">
<div class="menu-box">
<CommonMenu :menuData="menuData" @selectMenu="selectMenu"/>
</div>
<div class="content-box">
<!-- <div class="header-box">
<PageTabs ref="pageTabsRef" />
</div> -->
<div class="main-box">
<div class="background-block">
<RouterView/>
</div>
</div>
</div>
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import type { MenuItem } from '@/utils/public-interface'
import {onMounted, reactive, ref, toRefs, watch} from 'vue'
import type {MenuItem} from '@/utils/public-interface'
import CommonMenu from '@/components/common-menu.vue'
import PageTabs from '@/components/page-tabs.vue'
const menuData = [
{ name: '消息管理', path: '/logs-manage/message-manage' },
{ name: '日志管理', path: '/logs-manage/logs-manage' }
{name: '消息管理', path: '/logs-manage/message-manage'},
{name: '日志管理', path: '/logs-manage/logs-manage'},
{name: "词典管理", path: '/logs-manage/dict-manage'}
]
const pageTabsRef = ref()
const selectMenu = (e: MenuItem) => {
// pageTabsRef.value.setTab(e)
// pageTabsRef.value.setTab(e)
}
</script>
<style lang='scss' scoped>
.permissions-manage-page {
width: 100%;
height: 100%;
display: flex;
overflow-y: auto;
width: 100%;
height: 100%;
display: flex;
overflow-y: auto;
.menu-box {
width: 200px;
.menu-box {
width: 200px;
height: 100%;
background: white;
overflow-x: hidden;
overflow-y: auto;
}
.content-box {
width: calc(100% - 200px);
height: 100%;
.header-box {
width: 100%;
height: 50px;
}
.main-box {
width: 100%;
// height: calc(100% - 50px);
height: 100%;
padding: 10px;
.background-block {
width: 100%;
height: 100%;
background: white;
overflow-x: hidden;
overflow-y: auto;
}
.content-box {
width: calc(100% - 200px);
height: 100%;
.header-box {
width: 100%;
height: 50px;
}
.main-box {
width: 100%;
// height: calc(100% - 50px);
height: 100%;
padding: 10px;
.background-block {
width: 100%;
height: 100%;
background: white;
border-radius: 15px;
padding: 20px;
min-height: 510px;
}
}
border-radius: 15px;
padding: 20px;
min-height: 510px;
}
}
}
}</style>

View File

@ -93,6 +93,8 @@ onMounted(() => {
function init() {
loading.value = true;
current = 0;
total.value = 0;
getList();
loading.value = false;
}

View File

@ -27,7 +27,8 @@ const props = {
label: 'name',
children: 'children'
}
const treeIds: Array<string> = []
//
const treeIds: Array<string> = [];
const treeRef = ref()
const dialogVisible = ref(false)
const toggleExpand = ref(false)
@ -45,7 +46,6 @@ function open(role: any) {
dialogVisible.value = true
roleObj = role;
getMenuTree();
getRoleMenu();
}
function close() {
@ -56,11 +56,25 @@ const getMenuTree = () => {
menuApi.getMenuTree().then((res: any) => {
treeData.value = res.data;
setTreeIds(res.data, treeIds);
getRoleMenu();
});
}
const getRoleMenu = () => {
menuApi.getRoleTree(roleObj.roleId).then((res: any) => {
treeData.value.forEach((row: any) => {
let childrenCount = 0;
if (res.data.indexOf(row.id) != -1) {
row.children.forEach((child: any) => {
if (res.data.indexOf(child.id) != -1) {
childrenCount++;
}
})
if (row.children.length != childrenCount) {
res.data.splice(res.data.indexOf(row.id), 1);
}
}
})
treeRef.value.setCheckedKeys(res.data);
})
}
@ -93,8 +107,9 @@ const toggleSelectAllChange = (e: boolean) => {
}
const updateData = () => {
const menuIds = treeRef.value.getCheckedKeys().join(",");
roleApi.saveRoleMenus(roleObj.roleId, menuIds)
const menuIds = treeRef.value.getCheckedKeys();
const halfMenuIds = treeRef.value.getHalfCheckedKeys();
roleApi.saveRoleMenus(roleObj.roleId, menuIds.concat(halfMenuIds).join(','))
.then((res: any) => {
if (res.code == 0) {
ElMessage.success('更新成功');

View File

@ -1,61 +1,73 @@
<template>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100">
<el-form-item label="类型" prop="type">
<el-radio-group v-model="formData.menuType" :disabled="type === 'edit'">
<el-radio label="0" border>菜单</el-radio>
<el-radio label="1" border v-if="formData.parentId != -1">按钮</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="上级菜单" prop="parentName">
<!-- <el-select v-model="formData.parentMenu" placeholder="请选择上级菜单" clearable>
<el-option v-for="item in parentMenu" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>-->
<el-input v-model="formData.parentName" readonly placeholder="请输入菜单名称"></el-input>
</el-form-item>
<el-form-item label="菜单名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入菜单名称"></el-input>
</el-form-item>
<el-form-item label="图标" prop="icon">
<i :class="formData.icon" style="font-size: 26px;margin: 0 10px;"></i>
<el-button @click="isIconDialog = true">选择图标</el-button>
</el-form-item>
<el-form-item label="路由" prop="route">
<el-input v-model="formData.path" placeholder="请输入路由"></el-input>
</el-form-item>
<el-form-item label="排序" prop="order">
<el-input class="number-input" v-model="formData.sortOrder"
type="number" input-style="text-align: center;"
@change="numberInputChange">
<template #prepend>
<el-button text icon="Minus" :disabled="formData.sortOrder <= 1"
@click="formData.sortOrder--"/>
</template>
<template #append>
<el-button text icon="Plus" @click="formData.sortOrder++"/>
</template>
</el-input>
</el-form-item>
<el-form-item label="是否显示" prop="visible">
<el-radio-group v-model="formData.visible">
<el-radio label="1" border></el-radio>
<el-radio label="0" border></el-radio>
</el-radio-group>
</el-form-item>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100">
<div style="text-align: right;padding-top: 0;">
<el-button class="f18" @click="close">取消</el-button>
<el-button class="f18" type="primary" @click="saveData">确认</el-button>
</div>
</el-form>
<el-form-item label="上级菜单">
<span>{{ formData.parentName }}</span>
</el-form-item>
<el-dialog v-model="isIconDialog" title="选择图标" top="20vh" width="25%">
<div class="iconfont-box">
<div class="iconfont-item" v-for="item in iconfont.glyphs"
:key="item.font_class" @click="iconClick(item)">
<i :class="'icon-' + item.font_class"></i>
</div>
</div>
</el-dialog>
<el-form-item label="类型" v-if="type === 'edit'">
<span>{{ formData.menuType == 0 ? "菜单" : "按钮" }}</span>
</el-form-item>
<el-form-item label="类型" prop="type" v-else>
<el-radio-group v-model="formData.menuType">
<el-radio value="0" border>菜单</el-radio>
<el-radio value="1" border v-if="formData.parentId != -1">按钮</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="菜单名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入菜单名称"></el-input>
</el-form-item>
<el-form-item label="图标" prop="icon">
<i :class="formData.icon" style="font-size: 26px;margin: 0 10px;"></i>
<el-button @click="isIconDialog = true">选择图标</el-button>
</el-form-item>
<el-form-item label="路由" prop="route">
<el-input v-model="formData.path" placeholder="请输入路由"></el-input>
</el-form-item>
<el-form-item label="权限标识" prop="permission">
<el-input v-model="formData.permission" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="排序" prop="order">
<el-input class="number-input" v-model="formData.sortOrder"
type="number" input-style="text-align: center;"
@change="numberInputChange">
<template #prepend>
<el-button text icon="Minus" :disabled="formData.sortOrder <= 0"
@click="formData.sortOrder--"/>
</template>
<template #append>
<el-button text icon="Plus" @click="formData.sortOrder++"/>
</template>
</el-input>
</el-form-item>
<el-form-item label="是否显示" prop="visible">
<el-radio-group v-model="formData.visible">
<el-radio value="1" border></el-radio>
<el-radio value="0" border></el-radio>
</el-radio-group>
</el-form-item>
<div style="text-align: right;padding-top: 0;">
<el-button class="f18" @click="close">取消</el-button>
<el-button class="f18" type="primary" @click="saveData">确认</el-button>
</div>
</el-form>
<el-dialog v-model="isIconDialog" title="选择图标" top="20vh" width="25%">
<div class="iconfont-box">
<div class="iconfont-item" v-for="item in iconfont.glyphs"
:key="item.font_class" @click="iconClick(item)">
<i :class="'icon-' + item.font_class"></i>
</div>
</div>
</el-dialog>
</template>
<script lang='ts' setup>
@ -66,18 +78,18 @@ import * as menuApi from "@/api/menu";
const emit = defineEmits(['close', "saveOrUpdateEvent"])
const props = defineProps({
type: String
type: String
})
defineExpose({
resetData,
resetData,
})
const rules = reactive({
name: [
{required: true, message: '请输入菜单名称', trigger: ['blur', 'change']},
],
path: [
{required: true, message: '请输入路由地址', trigger: ['blur', 'change']},
]
name: [
{required: true, message: '请输入菜单名称', trigger: ['blur', 'change']},
],
path: [
{required: true, message: '请输入路由地址', trigger: ['blur', 'change']},
]
})
const formRef = ref()
const isIconDialog = ref(false)
@ -85,115 +97,117 @@ const formData = ref({} as any)
onMounted(() => {
resetData()
resetData()
})
function close() {
emit('close')
emit('close')
}
function resetData(menu?: any, form?: any) {
formRef.value.resetFields()
if (form) {
formData.value = {
menuId: form.id,
menuType: form.menuType,
parentName: form.parentName,
parentId: form.parentId,
name: form.name,
icon: form.meta.icon,
path: form.path,
sortOrder: form.sortOrder,
visible: form.visible
}
formRef.value.resetFields()
if (form) {
formData.value = {
menuId: form.id,
menuType: form.menuType,
parentName: form.parentName,
parentId: form.parentId,
name: form.name,
icon: form.meta.icon,
path: form.path,
sortOrder: form.sortOrder,
visible: form.visible,
permission: form.permission
}
} else {
formData.value = {
menuId: '',
menuType: '0',
parentName: menu ? menu.name : "无",
parentId: menu ? menu.id : "-1",
name: '',
icon: '',
path: '',
sortOrder: 1,
visible: "1"
}
}
} else {
formData.value = {
menuId: '',
menuType: '0',
parentName: menu ? menu.name : "无",
parentId: menu ? menu.id : "-1",
name: '',
icon: '',
path: '',
sortOrder: 0,
visible: "1",
permission: ""
}
}
}
const numberInputChange = (e: any) => {
if (!Number(e)) {
formData.value.sortOrder = 1
}
if (!Number(e)) {
formData.value.sortOrder = 0
}
}
const iconClick = (e: any) => {
formData.value.icon = 'icon-' + e.font_class
isIconDialog.value = false
formData.value.icon = 'icon-' + e.font_class
isIconDialog.value = false
}
const saveData = async () => {
await formRef.value.validate((valid: any, fields: any) => {
if (valid) {
const form = Object.assign({}, formData.value);
if (form.menuId) {
menuApi.updateMenu(form).then((res: any) => {
if (res.code == 0) {
ElMessage.success("修改成功");
emit("saveOrUpdateEvent");
} else {
ElMessage.error("修改失败");
}
})
} else {
menuApi.saveMenu(form).then((res: any) => {
if (res.code == 0) {
ElMessage.success('保存成功!');
emit("saveOrUpdateEvent");
} else {
ElMessage.error("保存失败");
}
})
}
}
})
await formRef.value.validate((valid: any, fields: any) => {
if (valid) {
const form = Object.assign({}, formData.value);
if (form.menuId) {
menuApi.updateMenu(form).then((res: any) => {
if (res.code == 0) {
ElMessage.success("修改成功");
emit("saveOrUpdateEvent");
} else {
ElMessage.error("修改失败");
}
})
} else {
menuApi.saveMenu(form).then((res: any) => {
if (res.code == 0) {
ElMessage.success('保存成功!');
emit("saveOrUpdateEvent");
} else {
ElMessage.error("保存失败");
}
})
}
}
})
}
</script>
<style lang='scss' scoped>
:deep(.el-form-item) {
.el-form-item__label {
display: block;
// text-align: justify;
// text-align-last: justify;
padding: 0 10px 0 20px;
.el-form-item__label {
display: block;
// text-align: justify;
// text-align-last: justify;
padding: 0 10px 0 20px;
&:before {
display: none;
}
}
&:before {
display: none;
}
}
}
.iconfont-box {
display: flex;
flex-wrap: wrap;
max-height: 300px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
max-height: 300px;
overflow-y: auto;
.iconfont-item {
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
font-size: 30px;
.iconfont-item {
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
font-size: 30px;
&:hover {
background: rgba($main-color, .1);
}
&:hover {
background: rgba($main-color, .1);
}
&:focus, &:active {
background: rgba($main-color, .2);
color: $main-color;
}
}
&:focus, &:active {
background: rgba($main-color, .2);
color: $main-color;
}
}
}
</style>

View File

@ -3,8 +3,11 @@
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="formData.roleName" placeholder="请输入角色名称"></el-input>
</el-form-item>
<el-form-item label="角色标识" prop="roleCode">
<el-input v-model="formData.roleCode" placeholder="请输入角色标识" :disabled="type === 'edit'"></el-input>
<el-form-item label="角色标识" v-if="type == 'edit'">
{{formData.roleCode}}
</el-form-item>
<el-form-item v-else label="角色标识" prop="roleCode">
<el-input v-model="formData.roleCode" placeholder="请输入角色标识"></el-input>
</el-form-item>
<el-form-item label="角色描述" prop="roleDesc">
<el-input v-model="formData.roleDesc" type="textarea" :rows="3" placeholder="请输入角色描述"></el-input>

View File

@ -17,8 +17,8 @@
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import type { MenuItem } from '@/utils/public-interface'
import {ref} from 'vue'
import type {MenuItem} from '@/utils/public-interface'
import CommonMenu from '@/components/common-menu.vue'
import PageTabs from '@/components/page-tabs.vue'

View File

@ -1,46 +1,52 @@
<template>
<div class="table-page">
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">菜单名称</span>
<el-input v-model="queryParams" placeholder="请输入菜单名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="searchList">查询</el-button>
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addData()">新增</el-button>
<el-button @click="expandTable">展开/折叠</el-button>
</div>
<TableAbility :isDownload="false" @searchBtn="isSearch = !isSearch" @refreshBtn="initData()"></TableAbility>
</div>
<div class="table-part">
<el-table ref="tableRef" :data="tableData" row-key="id" height="100%" border show-overflow-tooltip
@row-click="tableRowClick">
<el-table-column property="name" label="菜单名称" width="180" align="center"/>
<el-table-column property="sortOrder" label="排序" width="80" align="center"/>
<el-table-column label="图标" width="80" align="center">
<template #default="scope"><i class="main-color f20" :class="scope.row.meta.icon"></i></template>
</el-table-column>
<el-table-column property="path" label="路由" width="260" align="center"/>
<el-table-column property="menuType" label="类型" width="180" align="center"/>
<el-table-column label="操作" align="center">
<template #default="scope">
<div @click.stop>
<el-button link icon="EditPen" @click="addData(scope.row)">新建下级</el-button>
<el-button link icon="EditPen" @click="editData(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="removeData(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-dialog v-model="isFormDialog" :title="formDialogTitle" width="30%">
<MenuForm ref="menuFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'"
@save-or-update-event="formSaveUpdateEvent" @close="isFormDialog = false"/>
</el-dialog>
<div class="table-page">
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">菜单名称</span>
<el-input v-model="queryParams" placeholder="请输入菜单名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="searchList">查询</el-button>
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addData()">新增</el-button>
<el-button @click="expandTable">展开/折叠</el-button>
</div>
<TableAbility :isDownload="false" @searchBtn="isSearch = !isSearch" @refreshBtn="initData()"></TableAbility>
</div>
<div class="table-part">
<el-table ref="tableRef" :data="tableData" row-key="id" height="100%" border show-overflow-tooltip
@row-click="tableRowClick">
<el-table-column property="name" label="菜单名称" width="180" align="center"/>
<el-table-column label="图标" width="80" align="center">
<template #default="scope"><i class="main-color f20" :class="scope.row.meta.icon"></i></template>
</el-table-column>
<el-table-column property="path" label="路由" width="260" align="center"/>
<el-table-column label="类型" width="180" align="center">
<template #default="scope">
<span>{{ scope.row.menuType == 0 ? "菜单" : "按钮" }}</span>
</template>
</el-table-column>
<el-table-column property="sortOrder" label="排序" width="80" align="center"/>
<el-table-column label="操作" align="center">
<template #default="scope">
<div @click.stop>
<el-button link icon="EditPen" v-if="scope.row.menuType == 0" @click="addData(scope.row)">
新建下级
</el-button>
<el-button link icon="EditPen" @click="editData(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="removeData(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-dialog v-model="isFormDialog" :title="formDialogTitle" width="30%">
<MenuForm ref="menuFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'"
@save-or-update-event="formSaveUpdateEvent" @close="isFormDialog = false"/>
</el-dialog>
</template>
<script lang='ts' setup>
@ -50,7 +56,6 @@ import * as menuApi from "@/api/menu";
import {ElMessage, ElMessageBox} from "element-plus";
let isExpand = false //
const tableRef = ref()
const menuFormRef = ref()
const isSearch = ref(true)
@ -62,102 +67,102 @@ const tableData = ref([] as any)
const tableMap: any = {};
onMounted(() => {
initData();
initData();
})
function initData() {
loading.value = true;
tableData.value = [];
getMenuTree();
loading.value = true;
tableData.value = [];
getMenuTree();
}
function searchList() {
loading.value = true;
tableData.value = [];
getMenuTree(queryParams.value)
loading.value = true;
tableData.value = [];
getMenuTree(queryParams.value)
}
function resetSearch() {
queryParams.value = '';
initData();
queryParams.value = '';
initData();
}
function getMenuTree(name?: string) {
menuApi.getMenuTree(name).then((res: any) => {
tableData.value = res.data;
initTableMap(tableData.value);
})
menuApi.getMenuTree(name).then((res: any) => {
tableData.value = res.data ? res.data : [];
initTableMap(tableData.value);
})
}
function initTableMap(data: any) {
data.forEach((row: any, i: number) => {
handleInitTree(row, i, []);
});
data.forEach((row: any, i: number) => {
handleInitTree(row, i, []);
});
}
function handleInitTree(row: any, i: number, pArr: any) {
//
const cArr: any = [].concat(pArr);
cArr.push(i);
//
const cArr: any = [].concat(pArr);
cArr.push(i);
tableMap[row.id] = cArr;
row.indexArr = cArr;
if (row.children) {
row.children.forEach((row: any, i: number) => {
handleInitTree(row, i, cArr);
})
}
tableMap[row.id] = cArr;
row.indexArr = cArr;
if (row.children) {
row.children.forEach((row: any, i: number) => {
handleInitTree(row, i, cArr);
})
}
}
const addData = (menu?: any) => {
isFormDialog.value = true
formDialogTitle.value = '添加'
setTimeout(() => {
menuFormRef.value.resetData(menu)
}, 0)
isFormDialog.value = true
formDialogTitle.value = '添加'
setTimeout(() => {
menuFormRef.value.resetData(menu)
}, 0)
}
const expandTable = () => {
isExpand = !isExpand
tableData.value.forEach((row: any) => {
tableRef.value.toggleRowExpansion(row, isExpand)
})
isExpand = !isExpand
tableData.value.forEach((row: any) => {
tableRef.value.toggleRowExpansion(row, isExpand)
})
}
const removeData = (e?: any) => {
ElMessageBox.confirm(
'是否删除?',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true
}
).then(() => {
menuApi.deleteById(e.id).then((res: any) => {
if (res.code == 0) {
ElMessage.success("删除成功");
initData();
} else {
ElMessage.error(res.msg ? res.msg : "删除失败");
}
})
}).catch(() => {
})
ElMessageBox.confirm(
'是否删除?',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true
}
).then(() => {
menuApi.deleteById(e.id).then((res: any) => {
if (res.code == 0) {
ElMessage.success("删除成功");
initData();
} else {
ElMessage.error(res.msg ? res.msg : "删除失败");
}
})
}).catch(() => {
})
}
const editData = (e: any) => {
isFormDialog.value = true
formDialogTitle.value = '修改'
setTimeout(() => {
menuFormRef.value.resetData(null, JSON.parse(JSON.stringify(e)))
}, 0)
isFormDialog.value = true
formDialogTitle.value = '修改'
setTimeout(() => {
menuFormRef.value.resetData(null, JSON.parse(JSON.stringify(e)))
}, 0)
}
const tableRowClick = (row: any) => {
tableRef.value.toggleRowExpansion(row)
tableRef.value.toggleRowExpansion(row)
}
function formSaveUpdateEvent() {
isFormDialog.value = false;
initData();
isFormDialog.value = false;
initData();
}
</script>

View File

@ -1,68 +1,61 @@
<template>
<div class="table-page">
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">角色名称</span>
<el-input v-model="queryParams" placeholder="请输入角色名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="search">查询</el-button>
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addData">新增</el-button>
<el-button icon="FirstAidKit" @click="importData">导入</el-button>
<el-button icon="Delete" @click="removeData()">删除</el-button>
</div>
<TableAbility @searchBtn="isSearch = !isSearch" @refreshBtn="init"
@downloadBtn="exportData('角色数据', tableData)"></TableAbility>
</div>
<div class="table-part">
<el-table ref="tableRef" v-loading="loading" :data="tableData" height="100%" border show-overflow-tooltip
@row-click="tableRowClick">
<el-table-column type="selection" width="55"/>
<el-table-column type="index" label="#" width="55" align="center"/>
<el-table-column property="roleName" label="角色名称" width="120" align="center"/>
<el-table-column property="roleCode" label="角色标识" width="180" align="center"/>
<el-table-column property="roleDesc" label="角色描述" width="120" align="center"/>
<el-table-column label="数据权限" width="120" align="center">
<template #default="scope">
<el-popover
trigger="hover"
:content="scope.row.menu"
>
<template #reference>
<div class="cell-content">
{{ scope.row.menu }}
</div>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column label="创建时间" width="220" align="center">
<template #default="scope">{{ scope.row.createTime }}</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<div @click.stop>
<el-button link icon="RefreshLeft" @click="empower(scope.row)">授权</el-button>
<el-button link icon="EditPen" @click="editData(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="removeData(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-part">
<CommonPagination :total="total" @paginationChange="paginationChange"/>
</div>
</div>
<el-dialog v-model="isFormDialog" :title="formDialogTitle" width="30%">
<RoleForm ref="RoleFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'" @close="isFormDialog = false"/>
</el-dialog>
<ImportDialog ref="importDialogRef" title="角色导入" templateUrl="/admin/sys-file/local/file/role.xlsx"
importUrl="/admin/role/import" @success="importSuccessEvent"/>
<EmpowerDialog ref="empowerDialogRef"/>
<div class="table-page">
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">角色名称</span>
<el-input v-model="queryParams" placeholder="请输入角色名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="search">查询</el-button>
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addData">新增</el-button>
<el-button icon="FirstAidKit" @click="importData">导入</el-button>
<el-button icon="Delete" @click="removeData()">删除</el-button>
</div>
<TableAbility @searchBtn="isSearch = !isSearch" @refreshBtn="init"
@downloadBtn="exportData('角色数据', tableData)"></TableAbility>
</div>
<div class="table-part">
<el-table ref="tableRef" v-loading="loading" :data="tableData" height="100%" border show-overflow-tooltip
@row-click="tableRowClick">
<el-table-column type="selection" width="55"/>
<el-table-column type="index" label="#" width="55" align="center"/>
<el-table-column property="roleName" label="角色名称" width="120" align="center"/>
<el-table-column property="roleCode" label="角色标识" width="180" align="center"/>
<el-table-column property="roleDesc" label="角色描述" width="120" align="center"/>
<el-table-column label="数据权限" width="120" align="center">
<template #default="scope">
<div class="cell-content">
{{ scope.row.menu }}
</div>
</template>
</el-table-column>
<el-table-column label="创建时间" width="220" align="center">
<template #default="scope">{{ scope.row.createTime }}</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<div @click.stop>
<el-button link icon="RefreshLeft" @click="empower(scope.row)">授权</el-button>
<el-button link icon="EditPen" @click="editData(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="removeData(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-part">
<CommonPagination :total="total" @paginationChange="paginationChange"/>
</div>
</div>
<el-dialog v-model="isFormDialog" :title="formDialogTitle" width="30%">
<RoleForm ref="RoleFormRef" :type="formDialogTitle === '添加' ? 'add' : 'edit'" @close="isFormDialog = false"/>
</el-dialog>
<ImportDialog ref="importDialogRef" title="角色导入" templateUrl="/admin/sys-file/local/file/role.xlsx"
importUrl="/admin/role/import" @success="importSuccessEvent"/>
<EmpowerDialog ref="empowerDialogRef"/>
</template>
<script lang='ts' setup>
@ -91,108 +84,108 @@ let size = 10;
const total = ref(0);
onMounted(() => {
init();
init();
})
function init() {
loading.value = true
total.value = 0;
current = 0;
tableData.value = [];
getRoleList();
loading.value = false
loading.value = true
total.value = 0;
current = 0;
tableData.value = [];
getRoleList();
loading.value = false
}
function getRoleList() {
roleApi.getRoleTree(current, size, queryParams.value).then((res: any) => {
if (res.code == 0) {
total.value = res.data.total
tableData.value = res.data.records
}
})
roleApi.getRoleTree(current, size, queryParams.value).then((res: any) => {
if (res.code == 0) {
total.value = res.data.total
tableData.value = res.data.records
}
})
}
function search() {
init();
init();
}
function resetSearch() {
queryParams.value = "";
init();
queryParams.value = "";
init();
}
const addData = () => {
isFormDialog.value = true
formDialogTitle.value = '添加'
setTimeout(() => {
RoleFormRef.value.resetData()
}, 0)
isFormDialog.value = true
formDialogTitle.value = '添加'
setTimeout(() => {
RoleFormRef.value.resetData()
}, 0)
}
const importData = () => {
setTimeout(() => {
importDialogRef.value.open()
}, 0);
setTimeout(() => {
importDialogRef.value.open()
}, 0);
}
const removeData = (e?: any) => {
const selectRow = [e] || tableRef.value.getSelectionRows();
tableRemoveRow({data: selectRow}, (res: boolean) => {
if (res) {
const ids: any = [];
selectRow.forEach((row: any) => {
ids.push(row.roleId);
})
roleApi.deleteById(ids).then((res: any) => {
if (res.code == 0) {
ElMessage.success("删除成功");
init();
} else {
ElMessage.error(res.msg ? res.msg : "删除失败");
}
})
}
})
const selectRow = [e] || tableRef.value.getSelectionRows();
tableRemoveRow({data: selectRow}, (res: boolean) => {
if (res) {
const ids: any = [];
selectRow.forEach((row: any) => {
ids.push(row.roleId);
})
roleApi.deleteById(ids).then((res: any) => {
if (res.code == 0) {
ElMessage.success("删除成功");
init();
} else {
ElMessage.error(res.msg ? res.msg : "删除失败");
}
})
}
})
}
const empower = (e: any) => {
setTimeout(() => {
empowerDialogRef.value.open(e)
})
setTimeout(() => {
empowerDialogRef.value.open(e)
})
}
const editData = (e: any) => {
isFormDialog.value = true
formDialogTitle.value = '修改'
setTimeout(() => {
RoleFormRef.value.resetData(JSON.parse(JSON.stringify(e)))
}, 0)
isFormDialog.value = true
formDialogTitle.value = '修改'
setTimeout(() => {
RoleFormRef.value.resetData(JSON.parse(JSON.stringify(e)))
}, 0)
}
const tableRowClick = (row: any) => {
tableRef.value.toggleRowSelection(row)
tableRef.value.toggleRowSelection(row)
}
const paginationChange = (page: number, s: number) => {
current = page
size = s
getRoleList()
current = page
size = s
getRoleList()
}
const importSuccessEvent = () => {
init();
init();
}
</script>
<style lang='scss' scoped>
:deep(.el-table__body) {
.cell {
max-height: 49px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cell {
max-height: 49px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cell-content {
max-height: 49px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cell-content {
max-height: 49px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
</style>

View File

@ -49,9 +49,10 @@ defineExpose({
close,
})
function open(e: number) {
patientInfo.value = remoteWsStore.remoteTasks[e]
dialogVisible.value = true
function open(i: number) {
patientInfo.value = remoteWsStore.remoteTasks[i];
patientInfo.value.date = new Date();
dialogVisible.value = true;
}
function close() {

View File

@ -138,7 +138,7 @@ onUnmounted(() => {
remoteWsStore.unsubscribeVital(remoteItem.value.patient, remoteItem.value.patientId, remoteItem.value.date);
})
function showData(i: number) {
function showData(i: any) {
const lastTaskIndex = remoteWsStore.currentTaskIndex;
const lastTask: any = remoteWsStore.remoteTasks[lastTaskIndex];
if (lastTask) {

View File

@ -130,7 +130,7 @@
:disabled="tableDataStore[scope.$index].speed === scope.row.speed">确定-->
<el-button size="small" color="#006080" @click="tableItemConfirm(scope, varTableData)">确定
</el-button>
<el-button size="small" color="#006080" @click="tableItemCancel(scope, varTableData)">取消
<el-button size="small" color="#006080" @click="tableItemCancel(scope)">取消
</el-button>
</div>
</template>
@ -163,7 +163,7 @@
</el-button>
<el-button size="small" color="#006080" @click="tableItemConfirm(scope, fixedTableData)">确定
</el-button>
<el-button size="small" color="#006080" @click="tableItemCancel(scope, fixedTableData)">取消
<el-button size="small" color="#006080" @click="tableItemCancel(scope)">取消
</el-button>
</div>
</template>
@ -203,7 +203,7 @@ import imgHeart from '@/assets/imgs/heart.png';
import imgLungAlarm from '@/assets/imgs/lung_alarm.png';
import imgHeartAlarm from '@/assets/imgs/heart_alarm.png';
import {useRemoteWsStore} from "@/stores/remote-ws-store";
import {useLoginStore} from "@/stores/user-info-store";
import {useUserStore} from "@/stores/user-info-store";
import {getPatientInfo} from "@/api/patient";
@ -222,7 +222,7 @@ const medicineCustom: any[] = [
]
const remoteWsStore = useRemoteWsStore()
const currentRemote = ref(remoteWsStore.remoteTasks[remoteWsStore.currentTaskIndex])
const userInfo = useLoginStore()
const userInfo = useUserStore()
const chartDom1 = ref(),
chartDom2 = ref(),
@ -237,7 +237,7 @@ const isPatientDialog = ref(false)
const database = ref('')
const databaseOptions = ref([] as { value: string, label: string }[])
const messageSum = ref(10)
const userName = ref(userInfo.login.name)
const userName = ref(userInfo.userInfo.name)
const setDatabaseDialog = ref(false);
const featureTable = ref([] as any[]);
let chartNowData = reactive({ID: 0});
@ -546,7 +546,7 @@ const tableItemCancel = (e: any) => {
}
function startAI() {
const params = {
const params: any = {
name: currentRemote.value.patient,
id: currentRemote.value.patientId,
date: currentRemote.value.date,

View File

@ -1,180 +1,277 @@
<template>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100" :disabled="type === 'view'" style="padding: 20px;">
<el-row>
<el-col :span="12">
<el-form-item label="医院名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入医院名称" :disabled="type === 'edit'"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入编码"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker v-model="formData.startTime" type="datetime" format="YYYY-MM-DD HH:mm" placeholder="请输入开始时间" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间" prop="endTime">
<el-date-picker v-model="formData.endTime" type="datetime" format="YYYY-MM-DD HH:mm" placeholder="请输入结束时间" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="域名" prop="DNS">
<el-input v-model="formData.DNS" placeholder="请输入域名"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="state">
<el-radio-group v-model="formData.state">
<el-radio label="正常" border>正常</el-radio>
<el-radio label="冻结" border>冻结</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-divider border-style="dashed" />
<h3 class="main-color f20" style="margin: 10px 0 20px 0;">高级管理员</h3>
<el-row>
<el-col :span="12">
<el-form-item label="用户名" prop="admin.name">
<el-select v-model="formData.admin.name" filterable placeholder="请输入选择姓名"
remote :remote-method="remoteSearchName" @change="selectNameChange">
<el-option v-for="(item, index) in userOption" :key="'name-' + index"
:label="item.code" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="电话" prop="admin.phone">
<el-input v-model="formData.admin.phone" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="姓名" prop="admin.name">
<el-input v-model="formData.admin.name" disabled></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
</el-col>
</el-row>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100" :disabled="type === 'view'"
style="padding: 20px;">
<el-row>
<el-col :span="12">
<el-form-item label="医院名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入医院名称" :disabled="type === 'edit'"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入编码"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker v-model="formData.startTime" type="datetime" format="YYYY-MM-DD HH:mm"
placeholder="请输入开始时间"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间" prop="endTime">
<el-date-picker v-model="formData.endTime" type="datetime" format="YYYY-MM-DD HH:mm"
placeholder="请输入结束时间"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="域名" prop="domain">
<el-input v-model="formData.domain" placeholder="请输入域名"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio value="0">正常</el-radio>
<el-radio value="1" border>冻结</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="所在城市" prop="selectedOptions">
<el-cascader
size="large"
:options="provinceAndCityData"
v-model="formData.selectedOptions">
</el-cascader>
</el-form-item>
</el-col>
</el-row>
<div style="position: absolute;bottom: 50px;right: 50px;" v-if="type !== 'view'">
<el-button class="f18" @click="close">取消</el-button>
<el-button class="f18" type="primary" @click="saveData">确认</el-button>
</div>
</el-form>
<el-divider border-style="dashed"/>
<div v-if="props.type != 'add'">
<h3 class="main-color f20" style="margin: 10px 0 20px 0;">高级管理员</h3>
<el-row>
<el-col :span="12">
<el-form-item label="用户" prop="manager.id">
<el-select v-model="manager.id" filterable placeholder="请输入选择姓名"
remote :remote-method="remoteSearchName">
<el-option v-for="(item, index) in userOption" :key="'name-' + item.id"
:label="item.name" :value="item.id" @click="selectNameChange(item)"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="电话" prop="manager.phone">
<el-input v-model="manager.phone" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<!-- <el-col :span="12">
<el-form-item label="姓名" prop="manager.name">
<el-input v-model="manager.name" disabled></el-input>
</el-form-item>
</el-col>-->
<el-col :span="12">
</el-col>
</el-row>
</div>
<div style="position: absolute;bottom: 50px;right: 50px;" v-if="type != 'view'">
<el-button class="f18" @click="close">取消</el-button>
<el-button class="f18" type="primary" @click="saveData">确认</el-button>
</div>
</el-form>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getusers } from '@/static-data/core'
import {onMounted, reactive, ref} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import * as hospitalApi from "@/api/hospital";
import {dateFormater} from "@/utils/date-util";
import * as userApi from "@/api/user";
import {
provinceAndCityData,
pcTextArr,
regionData,
pcaTextArr,
codeToText,
} from "element-china-area-data";
const emit = defineEmits(['close'])
const props = defineProps({
type: String
type: String
})
const users = getusers()
const rules = reactive({
name: [
{ required: true, message: '请输入医院名称', trigger: ['blur', 'change'] },
],
name: [
{required: true, message: '请输入医院名称', trigger: ['blur', 'change']},
],
selectedOptions: [
{required: true, message: '请选择地区', trigger: ['blur', 'change']},
]
})
const formRef = ref()
const formData = ref({
name: '',
code: '',
admin: {
userName: '',
name: '',
phone: '',
},
DNS: '',
startTime: '',
endTime: '',
state: ''
name: '',
code: '',
domain: '',
startTime: '',
endTime: '',
selectedOptions: [],
status: 0
} as any)
const manager = ref({
id: "",
name: "",
phone: ""
} as any)
const userOption = ref([] as any)
let userList: any = []
onMounted(() => {
resetData()
resetData()
})
defineExpose({
formData,
resetData,
resetData
})
function close() {
emit('close')
emit('close')
}
function resetData() {
formRef.value.resetFields()
formData.value = {
name: '',
code: '',
admin: {
userName: '',
name: '',
phone: '',
},
DNS: '',
startTime: '',
endTime: '',
state: ''
async function resetData(form?: any) {
formRef.value.resetFields()
if (form) {
manager.value = {
id: "",
name: '',
phone: '',
}
await getUserList(form.id);
getHospitalAdmin(form.id);
form.selectedOptions = [form.province, form.city]
formData.value = form;
} else {
formData.value = {
name: '',
code: '',
domain: '',
startTime: '',
endTime: '',
status: 0,
selectedOptions: []
}
manager.value = {
id: "",
name: '',
phone: '',
}
}
}
const remoteSearchName = (e: any) => {
if (e) {
userOption.value = users.filter((o: any) => o.name.indexOf(e) !== -1)
}
if (e) {
userOption.value = userList.filter((user: any) => user.name.indexOf(e) != -1)
}
}
const selectNameChange = (e: any) => {
manager.value.phone = e.phone;
manager.value.name = e.name;
}
const saveData = async () => {
await formRef.value.validate((valid: any, fields: any) => {
if (valid) {
ElMessage.success('保存成功!')
close()
const saveData = () => {
console.log(formData.value)
ElMessageBox.confirm(
"是否保存",
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true
}
).then(() => {
formRef.value.validate(async (valid: any, fields: any) => {
if (valid) {
const form = Object.assign({}, formData.value);
form.startTime = dateFormater("yyyy-MM-dd hh:mm:ss", form.startTime)
form.endTime = dateFormater("yyyy-MM-dd hh:mm:ss", form.endTime)
form.province = form.selectedOptions[0];
form.city = form.selectedOptions[1];
if (props.type == "edit") {
const hosRes: any = await hospitalApi.updateHospital(form)
const manRes: any = await hospitalApi.saveHospitalManager(form.id, manager.value.id)
if ((hosRes.code == 0 && hosRes.data) || (manRes.code == 0 && manRes.data)) {
ElMessage.success("更新成功")
close();
} else {
ElMessage.error("更新失败")
}
} else {
// console.log('error submit!', fields)
hospitalApi.saveHospital(form).then((res: any) => {
if (res.code == 0) {
ElMessage.success('保存成功!');
close();
} else {
ElMessage.error(res.msg ? res.msg : "保存失败");
}
});
}
}
})
}).catch(() => {
})
}
function getHospitalAdmin(hospitalId: string) {
hospitalApi.getHospitalManager(hospitalId).then((res: any) => {
if (res.code == 0 && res.data.length > 0) {
manager.value = res.data[0];
}
})
}
async function getUserList(hospitalId: string) {
const res: any = await userApi.getUserListByHospital(hospitalId)
if (res.code == 0) {
userOption.value = res.data;
userOption.value.unshift({
id: "",
name: '无',
phone: '',
})
userList = userOption.value;
}
}
</script>
<style lang='scss' scoped>
:deep(.el-form-item) {
.el-form-item__label {
display: block;
text-align: justify;
text-align-last: justify;
padding: 0 10px 0 20px;
.el-form-item__label {
display: block;
text-align: justify;
text-align-last: justify;
padding: 0 10px 0 20px;
&:before {
display: none;
}
&:before {
display: none;
}
}
.el-radio-group {
width: 100%;
justify-content: space-between;
.el-radio {
margin: 0;
}
.el-radio-group {
width: 100%;
justify-content: space-between;
.el-radio {
margin: 0;
}
}
}
</style>

View File

@ -1,71 +1,76 @@
<template>
<div class="table-page">
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">医院名称</span>
<el-input v-model="queryParams.userName" placeholder="请输入医院名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="queryData(queryParams)">查询</el-button>
<el-button icon="Refresh" @click="queryParams = {}">重置</el-button>
</div>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addData">新增</el-button>
<el-button icon="Delete" @click="removeData()">删除</el-button>
</div>
<TableAbility @searchBtn="isSearch = !isSearch" @refreshBtn="queryData({})"
@downloadBtn="exportData('医生数据', tableData)"></TableAbility>
</div>
<div class="table-part">
<el-table ref="tableRef" v-loading="loading" :data="tableData" height="100%" border show-overflow-tooltip
@row-click="tableRowClick">
<el-table-column type="selection" width="55" />
<el-table-column type="index" label="#" width="55" align="center" />
<el-table-column property="name" label="医院名称" width="200" align="center" />
<el-table-column property="code" label="编码" width="60" align="center" />
<el-table-column label="高级管理员" width="200" align="center">
<template #default="scope">{{ scope.row.admin?.name }}</template>
</el-table-column>
<el-table-column property="DNS" label="域名" width="200" align="center" />
<el-table-column label="开始时间" width="200" align="center">
<template #default="scope">{{ dateFormater('yyyy-MM-dd HH:mm', scope.row.startTime) }}</template>
</el-table-column>
<el-table-column label="结束时间" width="200" align="center">
<template #default="scope">{{ dateFormater('yyyy-MM-dd HH:mm', scope.row.endTime) }}</template>
</el-table-column>
<el-table-column property="state" label="状态" width="60" align="center" />
<el-table-column label="操作" align="center">
<template #default="scope">
<span @click.stop>
<el-button link icon="View" @click="viewData(scope.row)">医院信息</el-button>
<el-button link icon="EditPen" @click="editData(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="removeData(scope.row)">删除</el-button>
</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-part">
<CommonPagination :total="100" @paginationChange="paginationChange" />
</div>
<div class="table-page">
<div class="search-part" v-show="isSearch">
<div class="search-cell">
<span class="label">医院名称</span>
<el-input v-model="queryParams" placeholder="请输入医院名称"></el-input>
</div>
<el-button type="primary" icon="Search" @click="search">查询</el-button>
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<el-drawer v-model="isFormDialog" size="660px">
<template #header>
<h3 class="main-color f20" style="padding: 20px 20px 0 20px;">{{ formDialogTitle }}</h3>
</template>
<HospitalForm ref="hospitalFormRef"
:type="formDialogTitle === '添加' ? 'add' : formDialogTitle === '修改' ? 'edit' : 'view'"
@close="isFormDialog = false" />
</el-drawer>
<div class="button-part" style="justify-content: space-between;">
<div>
<el-button type="primary" icon="FirstAidKit" @click="addData">新增</el-button>
<el-button icon="Delete" @click="removeData()">删除</el-button>
</div>
<TableAbility @searchBtn="isSearch = !isSearch" @refreshBtn="refresh"
@downloadBtn="exportData('医生数据', tableData)"></TableAbility>
</div>
<div class="table-part">
<el-table ref="tableRef" v-loading="loading" :data="tableData" height="100%" border show-overflow-tooltip
@row-click="tableRowClick">
<el-table-column type="selection" width="55"/>
<el-table-column type="index" label="#" width="55" align="center"/>
<el-table-column property="name" label="医院名称" width="200" align="center"/>
<el-table-column property="code" label="编码" width="60" align="center"/>
<el-table-column label="高级管理员" width="200" align="center">
<template #default="scope">{{ scope.row.admin?.name }}</template>
</el-table-column>
<el-table-column property="domain" label="域名" width="200" align="center"/>
<el-table-column label="开始时间" width="200" align="center">
<template #default="scope">{{ dateFormater('yyyy-MM-dd HH:mm', scope.row.startTime) }}</template>
</el-table-column>
<el-table-column label="结束时间" width="200" align="center">
<template #default="scope">{{ dateFormater('yyyy-MM-dd HH:mm', scope.row.endTime) }}</template>
</el-table-column>
<el-table-column label="状态" width="60" align="center">
<template #default="scope">
{{ scope.row.status == 1 ? "锁定" : "正常" }}
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<div @click.stop>
<el-button link icon="View" @click="viewData(scope.row)">医院信息</el-button>
<el-button link icon="EditPen" @click="editData(scope.row)">修改</el-button>
<el-button link icon="Delete" @click="removeData(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-part">
<CommonPagination :total="total" @paginationChange="paginationChange"/>
</div>
</div>
<el-drawer v-model="isFormDialog" size="660px">
<template #header>
<h3 class="main-color f20" style="padding: 20px 20px 0 20px;">{{ formDialogTitle }}</h3>
</template>
<HospitalForm ref="hospitalFormRef"
:type="formDialogTitle === '添加' ? 'add' : formDialogTitle === '修改' ? 'edit' : 'view'"
@close="isFormDialog = false"/>
</el-drawer>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { tableRemoveRow, exportData } from '@/utils/table-util'
import {onMounted, ref} from 'vue'
import {exportData, tableRemoveRow} from '@/utils/table-util'
import CommonPagination from '@/components/common-pagination.vue'
import HospitalForm from './form/hospital-form.vue'
import { dateFormater } from '@/utils/date-util'
import {dateFormater} from '@/utils/date-util'
import * as hospitalApi from "@/api/hospital";
import {ElMessage, ElMessageBox} from "element-plus";
const tableRef = ref()
const hospitalFormRef = ref()
@ -73,71 +78,93 @@ const isSearch = ref(true)
const loading = ref(true)
const isFormDialog = ref(false)
const formDialogTitle = ref('')
const queryParams = ref({
userName: ''
} as any)
const queryParams = ref()
const tableData = ref([] as any)
let current = 0;
let size = 10;
const total = ref(0);
queryData({ name: '测试' })
onMounted(() => {
init();
})
function queryData(e: any) {
loading.value = true
tableData.value = []
setTimeout(() => {
while (tableData.value.length < 10) {
tableData.value.push({
name: e.name || '测试',
code: '',
admin: {
userName: 'admin',
name: '管理员',
phone: '12345678',
},
DNS: '***.****.***',
startTime: new Date(),
endTime: new Date(),
state: '正常'
})
}
loading.value = false
}, 200);
function init() {
loading.value = true
current = 0;
total.value = 0;
getList();
loading.value = false
}
function getList() {
hospitalApi.getHospitalPage(current, size, queryParams.value).then((res: any) => {
if (res.code == 0) {
tableData.value = res.data.list;
total.value = res.data.total;
}
})
}
function refresh() {
init();
}
function search() {
init();
}
function resetSearch() {
queryParams.value = "";
init();
}
const addData = () => {
isFormDialog.value = true
formDialogTitle.value = '添加'
setTimeout(() => {
hospitalFormRef.value.resetData()
}, 0)
isFormDialog.value = true
formDialogTitle.value = '添加'
setTimeout(() => {
hospitalFormRef.value.resetData()
}, 0)
}
const removeData = (e?: any) => {
const selectRow = e || tableRef.value.getSelectionRows()
tableRemoveRow({ data: selectRow }, (res: boolean) => {
if (res) {
// console.log('', selectRow)
const selectRow = e ? [e] : tableRef.value.getSelectionRows()
tableRemoveRow({data: selectRow}, (res: boolean) => {
if (res) {
const ids: string[] = [];
selectRow.forEach((row: any) => {
ids.push(row.id);
})
hospitalApi.deleteHospitalByIds(ids.join(",")).then((res: any) => {
if (res.code == 0 && res.data) {
ElMessage.success("删除成功")
} else {
ElMessage.error(res.msg ? res.msg : "删除失败")
}
})
})
}
})
}
const viewData = (e: any) => {
isFormDialog.value = true
formDialogTitle.value = '医院信息'
setTimeout(() => {
hospitalFormRef.value.resetData()
hospitalFormRef.value.formData = JSON.parse(JSON.stringify(e))
}, 0)
isFormDialog.value = true
formDialogTitle.value = '医院信息'
setTimeout(() => {
hospitalFormRef.value.resetData(JSON.parse(JSON.stringify(e)))
}, 0)
}
const editData = (e: any) => {
isFormDialog.value = true
formDialogTitle.value = '修改'
setTimeout(() => {
hospitalFormRef.value.resetData()
hospitalFormRef.value.formData = JSON.parse(JSON.stringify(e))
}, 0)
isFormDialog.value = true
formDialogTitle.value = '修改'
setTimeout(() => {
hospitalFormRef.value.resetData(JSON.parse(JSON.stringify(e)))
}, 0)
}
const tableRowClick = (row: any) => {
tableRef.value.toggleRowSelection(row)
tableRef.value.toggleRowSelection(row)
}
const paginationChange = (page: number, size: number) => {
const paginationChange = (page: number, s: number) => {
current = page;
size = s;
getList();
}
</script>