init:库尔勒妇幼项目初始化

This commit is contained in:
Elliott
2025-07-23 14:39:14 +08:00
commit 8c9d4af4e0
55 changed files with 13218 additions and 0 deletions

8
.env.development Normal file
View File

@@ -0,0 +1,8 @@
# 页面标题
VITE_APP_TITLE = 妇幼小程序拓展
# 开发环境配置
VITE_APP_ENV = 'development'
# 妇幼小程序拓展/开发环境
VITE_APP_BASE_API = '/dev-api'

11
.env.production Normal file
View File

@@ -0,0 +1,11 @@
# 页面标题
VITE_APP_TITLE = 妇幼小程序拓展
# 生产环境配置
VITE_APP_ENV = 'production'
# 妇幼小程序拓展/生产环境
VITE_APP_BASE_API = '/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

11
.env.staging Normal file
View File

@@ -0,0 +1,11 @@
# 页面标题
VITE_APP_TITLE = 妇幼小程序拓展
# 生产环境配置
VITE_APP_ENV = 'staging'
# 妇幼小程序拓展/生产环境
VITE_APP_BASE_API = '/stage-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/star.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>mini-expand</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

5129
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
package.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "miniexpand",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.7.7",
"crypto-js": "^4.2.0",
"echarts": "^5.5.1",
"element-plus": "^2.8.3",
"moment": "^2.30.1",
"pinia": "^2.3.1",
"sass": "^1.78.0",
"vite-plugin-svg-icons": "2.0.1",
"vue": "^3.4.37",
"vue-router": "^4.4.3"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.2",
"unplugin-auto-import": "^0.18.2",
"vite": "^5.4.1"
}
}

1
public/star.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

18
src/App.vue Normal file
View File

@@ -0,0 +1,18 @@
<script setup>
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
dayjs.locale("zh-cn");
</script>
<template>
<el-config-provider :locale="zhCn">
<router-view/>
</el-config-provider>
</template>
<style scoped>
</style>

24
src/api/auth.js Normal file
View File

@@ -0,0 +1,24 @@
import request from '@/utils/request.js'
export function getAuther(req) {
return request({
url: '/countAutho/getAuther',
method: 'get',
params: req
})
}
export function getPerm(req) {
return request({
url: '/countAutho/getPerm',
method: 'get',
params: req
})
}
export function getDeptPerm(req) {
return request({
url: '/countAutho/getDeptPerm',
method: 'get',
params: req
})
}

10
src/api/dept.js Normal file
View File

@@ -0,0 +1,10 @@
import request from '@/utils/request.js'
//收入数值统计
export function getDeptData(req) {
return request({
url: '/his/getUniqueLb25Data',
method: 'get',
params: req
})
}

View File

@@ -0,0 +1,66 @@
import request from '@/utils/request.js'
//测试接口
export function getHosData(req) {
return request({
url: '/his/countData',
method: 'get',
params: req
})
}
export function getLB21Options(req) {
return request({
url: '/his/getLB21Options',
method: 'get',
params: req
})
}
export function getLB24Options(req) {
return request({
url: '/his/getLB24Options',
method: 'get',
params: req
})
}
export function getLB23Options(req) {
return request({
url: '/his/getLB24Options',
method: 'get',
params: req
})
}
export function getLB22Options(req) {
return request({
url: '/his/getLB22Options',
method: 'get',
params: req
})
}
export function getLB20Options(req) {
return request({
url: '/his/getLB20Options',
method: 'get',
params: req
})
}
export function getLB19Options(req) {
return request({
url: '/his/getLB19Options',
method: 'get',
params: req
})
}

63
src/api/outpatient.js Normal file
View File

@@ -0,0 +1,63 @@
import request from '@/utils/request.js'
//测试接口
export function getOutPatientData(req) {
return request({
url: '/his/countData',
method: 'get',
params: req
})
}
export function getSpecialData(req) {
return request({
url: '/his/specialSingleCount',
method: 'get',
params: req
})
}
export function getSingleDeptCount(req) {
return request({
url: '/his/getSingleDeptCount',
method: 'get',
params: req
})
}
export function getChildSpecialSingleCount(req) {
return request({
url: '/his/getChildSpecialSingleCount',
method: 'get',
params: req
})
}
export function getChildSingleBudget(req) {
return request({
url: '/his/getChildSingleBudget',
method: 'get',
params: req
})
}
export function getTMB(req) {
return request({
url: '/his/getTMB',
method: 'get',
params: req
})
}
export function getCDB(req) {
return request({
url: '/his/getCDB',
method: 'get',
params: req
})
}
export function getEMDB(req) {
return request({
url: '/his/getEMDB',
method: 'get',
params: req
})
}

19
src/api/synthesis.js Normal file
View File

@@ -0,0 +1,19 @@
import request from '@/utils/request.js'
//收入数值统计
export function getCountData(req) {
return request({
url: '/his/countData',
method: 'get',
params: req
})
}
//收入饼图统计
export function getSynPieCountData(req) {
return request({
url: '/his/synPieCountData',
method: 'get',
params: req
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg t="1733301290071" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5121" width="48" height="48"><path d="M1008.64 993.28h-117.76v-460.8c0-8.704-6.656-15.36-15.36-15.36s-15.36 6.656-15.36 15.36v460.8h-150.016v-209.92c0-8.704-6.656-15.36-15.36-15.36s-15.36 6.656-15.36 15.36v209.92h-150.016v-250.88c0-8.704-6.656-15.36-15.36-15.36s-15.36 6.656-15.36 15.36v250.88H349.696v-296.96c0-8.704-6.656-15.36-15.36-15.36s-15.36 6.656-15.36 15.36v296.96H168.96v-174.08c0-8.704-6.656-15.36-15.36-15.36s-15.36 6.656-15.36 15.36v174.08H30.72V107.52c0-8.704-6.656-15.36-15.36-15.36s-15.36 6.656-15.36 15.36v901.12c0 8.192 6.656 15.36 15.36 15.36h993.28c8.704 0 15.36-7.168 15.36-15.36 0-8.704-6.656-15.36-15.36-15.36z" p-id="5122" fill="#1296db"></path><path d="M153.6 450.56c3.584 0 7.168-1.536 10.24-4.096l169.984-155.136 168.96 125.44c2.56 2.048 5.12 3.072 8.192 3.072l184.32 10.24c5.12 0.512 10.24-2.048 13.312-6.656l179.2-256c5.12-7.168 3.072-16.384-3.584-21.504-7.168-5.12-16.384-3.072-21.504 3.584l-174.08 248.832-171.008-9.728-175.616-129.536c-6.144-4.608-14.336-4.096-19.456 1.024l-179.2 163.84c-6.144 5.632-6.656 15.36-1.024 21.504 3.072 3.584 7.168 5.12 11.264 5.12z" p-id="5123" fill="#1296db"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,99 @@
@import './variables.module.scss';
@mixin colorBtn($color) {
background: $color;
&:hover {
color: $color;
&:before,
&:after {
background: $color;
}
}
}
.blue-btn {
@include colorBtn($blue)
}
.light-blue-btn {
@include colorBtn($light-blue)
}
.red-btn {
@include colorBtn($red)
}
.pink-btn {
@include colorBtn($pink)
}
.green-btn {
@include colorBtn($green)
}
.tiffany-btn {
@include colorBtn($tiffany)
}
.yellow-btn {
@include colorBtn($yellow)
}
.pan-btn {
font-size: 14px;
color: #fff;
padding: 14px 36px;
border-radius: 8px;
border: none;
outline: none;
transition: 600ms ease all;
position: relative;
display: inline-block;
&:hover {
background: #fff;
&:before,
&:after {
width: 100%;
transition: 600ms ease all;
}
}
&:before,
&:after {
content: '';
position: absolute;
top: 0;
right: 0;
height: 2px;
width: 0;
transition: 400ms ease all;
}
&::after {
right: inherit;
top: inherit;
left: 0;
bottom: 0;
}
}
.custom-button {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
color: #fff;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: 0;
margin: 0;
padding: 10px 15px;
font-size: 14px;
border-radius: 4px;
}

View File

@@ -0,0 +1,109 @@
// cover some element-ui styles
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
}
.el-upload {
input[type="file"] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
.cell {
.el-tag {
margin-right: 0px;
}
}
.small-padding {
.cell {
padding-left: 5px;
padding-right: 5px;
}
}
.fixed-width {
.el-button--mini {
padding: 7px 10px;
width: 60px;
}
}
.status-col {
.cell {
padding: 0 10px;
text-align: center;
.el-tag {
margin-right: 0px;
}
}
}
// refine element ui upload
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
// dropdown
.el-dropdown-menu {
a {
display: block
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner {
display: inline-flex !important;
}
// to fix el-date-picker css style
.el-range-separator {
box-sizing: content-box;
}
.el-menu--collapse
> div
> .el-submenu
> .el-submenu__title
.el-submenu__icon-arrow {
display: none;
}
.el-dropdown .el-dropdown-link{
color: var(--el-color-primary) !important;
}
//.el-dialog {
// display: flex !important;
// flex-direction: column !important;
// margin: 0 !important;
// position: absolute !important;
// top: 50% !important;
// left: 50% !important;
// transform: translate(-50%, -50%) !important;
// overflow-y: scroll !important;
// max-height: 100vh !important;
//}
// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
position: relative;
margin: 0 auto;
}

View File

@@ -0,0 +1,184 @@
@import './variables.module.scss';
@import './mixin.scss';
@import './transition.scss';
@import './element-ui.scss';
@import './sidebar.scss';
@import './btn.scss';
@import './ruoyi.scss';
body {
height: 100%;
margin: 0;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
label {
font-weight: 700;
}
html {
height: 100%;
box-sizing: border-box;
}
#app {
height: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.no-padding {
padding: 0px !important;
}
.padding-content {
padding: 4px 0;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
div:focus {
outline: none;
}
.fr {
float: right;
}
.fl {
float: left;
}
.pr-5 {
padding-right: 5px;
}
.pl-5 {
padding-left: 5px;
}
.block {
display: block;
}
.pointer {
cursor: pointer;
}
.inlineBlock {
display: block;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
aside {
background: #eef1f6;
padding: 8px 24px;
margin-bottom: 20px;
border-radius: 2px;
display: block;
line-height: 32px;
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
color: #2c3e50;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
a {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
}
}
//main-container全局样式
.app-container {
padding: 20px;
}
.components-container {
margin: 30px 50px;
position: relative;
}
.pagination-container {
margin-top: 30px;
}
.text-center {
text-align: center
}
.sub-navbar {
height: 50px;
line-height: 50px;
position: relative;
width: 100%;
text-align: right;
padding-right: 20px;
transition: 600ms ease position;
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
.subtitle {
font-size: 20px;
color: #fff;
}
&.draft {
background: #d0d0d0;
}
&.deleted {
background: #d0d0d0;
}
}
.link-type,
.link-type:focus {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
}
.filter-container {
padding-bottom: 10px;
.filter-item {
display: inline-block;
vertical-align: middle;
margin-bottom: 10px;
}
}

View File

@@ -0,0 +1,66 @@
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
@mixin relative {
position: relative;
width: 100%;
height: 100%;
}
@mixin pct($pct) {
width: #{$pct};
position: relative;
margin: 0 auto;
}
@mixin triangle($width, $height, $color, $direction) {
$width: $width/2;
$color-border-style: $height solid $color;
$transparent-border-style: $width solid transparent;
height: 0;
width: 0;
@if $direction==up {
border-bottom: $color-border-style;
border-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==right {
border-left: $color-border-style;
border-top: $transparent-border-style;
border-bottom: $transparent-border-style;
}
@else if $direction==down {
border-top: $color-border-style;
border-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==left {
border-right: $color-border-style;
border-top: $transparent-border-style;
border-bottom: $transparent-border-style;
}
}

View File

@@ -0,0 +1,281 @@
/**
* 通用css样式布局处理
* Copyright (c) 2019 ruoyi
*/
/** 基础通用 **/
.pt5 {
padding-top: 5px;
}
.pr5 {
padding-right: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
}
.el-form .el-form-item__label {
font-weight: 700;
}
.el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important;
}
.el-dialog.scrollbar .el-dialog__body {
overflow: auto;
overflow-x: hidden;
max-height: 70vh;
padding: 10px 20px 0;
}
.el-table {
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
th {
word-break: break-word;
background-color: #f8f8f9 !important;
color: #515a6e;
height: 15px !important;
font-size: 10px;
}
}
.el-table__body-wrapper {
.el-button [class*="el-icon-"] + span {
margin-left: 1px;
}
}
}
/** 表单布局 **/
.form-header {
font-size:15px;
color:#6379bb;
border-bottom:1px solid #ddd;
margin:8px 10px 25px 10px;
padding-bottom:5px
}
/** 表格布局 **/
.pagination-container {
position: relative;
height: 25px;
margin-bottom: 10px;
margin-top: 15px;
padding: 10px 20px !important;
}
.el-dialog .pagination-container {
position: static !important;
}
/* tree border */
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7;
background: #FFFFFF none;
border-radius:4px;
width: 100%;
}
.pagination-container .el-pagination {
right: 0;
position: absolute;
}
@media ( max-width : 768px) {
.pagination-container .el-pagination > .el-pagination__jump {
display: none !important;
}
.pagination-container .el-pagination > .el-pagination__sizes {
display: none !important;
}
}
.el-table .fixed-width .el-button--small {
padding-left: 0;
padding-right: 0;
width: inherit;
}
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link {
cursor: pointer;
color: #409EFF;
margin-left: 10px;
}
.el-table .el-dropdown, .el-icon-arrow-down {
font-size: 12px;
}
.el-tree-node__content > .el-checkbox {
margin-right: 8px;
}
.list-group-striped > .list-group-item {
border-left: 0;
border-right: 0;
border-radius: 0;
padding-left: 0;
padding-right: 0;
}
.list-group {
padding-left: 0px;
list-style: none;
}
.list-group-item {
border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec;
margin-bottom: -1px;
padding: 11px 0px;
font-size: 13px;
}
.pull-right {
float: right !important;
}
.el-card__header {
padding: 14px 15px 7px !important;
min-height: 40px;
}
.el-card__body {
padding: 15px 20px 20px 20px !important;
}
.card-box {
padding-right: 15px;
padding-left: 15px;
margin-bottom: 10px;
}
/* button color */
.el-button--cyan.is-active,
.el-button--cyan:active {
background: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
.el-button--cyan:focus,
.el-button--cyan:hover {
background: #48D1CC;
border-color: #48D1CC;
color: #FFFFFF;
}
.el-button--cyan {
background-color: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
/* text color */
.text-navy {
color: #1ab394;
}
.text-primary {
color: inherit;
}
.text-success {
color: #1c84c6;
}
.text-info {
color: #23c6c8;
}
.text-warning {
color: #f8ac59;
}
.text-danger {
color: #ed5565;
}
.text-muted {
color: #888888;
}
/* image */
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
/* 拖拽列样式 */
.sortable-ghost{
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
/* 表格右侧工具栏样式 */
.top-right-btn {
margin-left: auto;
}

View File

@@ -0,0 +1,238 @@
#app {
.main-container {
height: 100%;
transition: margin-left .28s;
margin-left: $base-sidebar-width;
position: relative;
}
.sidebarHide {
margin-left: 0!important;
}
.sidebar-container {
-webkit-transition: width .28s;
transition: width 0.28s;
width: $base-sidebar-width !important;
background-color: $base-menu-background;
height: 100%;
position: fixed;
font-size: 0px;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
-webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);
box-shadow: 2px 0 6px rgba(0,21,41,.35);
// reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
.scrollbar-wrapper {
overflow-x: hidden !important;
}
.el-scrollbar__bar.is-vertical {
right: 0px;
}
.el-scrollbar {
height: 100%;
}
&.has-logo {
.el-scrollbar {
height: calc(100% - 50px);
}
}
.is-horizontal {
display: none;
}
a {
display: inline-block;
width: 100%;
overflow: hidden;
}
.svg-icon {
margin-right: 16px;
}
.el-menu {
border: none;
height: 100%;
width: 100% !important;
}
.el-menu-item, .menu-title {
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
.el-menu-item .el-menu-tooltip__trigger {
display: inline-block !important;
}
// menu hover
.sub-menu-title-noDropdown,
.el-sub-menu__title {
&:hover {
background-color: rgba(0, 0, 0, 0.06) !important;
}
}
& .theme-dark .is-active > .el-sub-menu__title {
color: $base-menu-color-active !important;
}
& .nest-menu .el-sub-menu>.el-sub-menu__title,
& .el-sub-menu .el-menu-item {
min-width: $base-sidebar-width !important;
&:hover {
background-color: rgba(0, 0, 0, 0.06) !important;
}
}
& .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
& .theme-dark .el-sub-menu .el-menu-item {
background-color: $base-sub-menu-background !important;
&:hover {
background-color: $base-sub-menu-hover !important;
}
}
}
.hideSidebar {
.sidebar-container {
width: 54px !important;
}
.main-container {
margin-left: 54px;
}
.sub-menu-title-noDropdown {
padding: 0 !important;
position: relative;
.el-tooltip {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
}
}
.el-sub-menu {
overflow: hidden;
&>.el-sub-menu__title {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
}
}
.el-menu--collapse {
.el-sub-menu {
&>.el-sub-menu__title {
&>span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
&>i {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
}
}
}
.el-menu--collapse .el-menu .el-sub-menu {
min-width: $base-sidebar-width !important;
}
// mobile responsive
.mobile {
.main-container {
margin-left: 0px;
}
.sidebar-container {
transition: transform .28s;
width: $base-sidebar-width !important;
}
&.hideSidebar {
.sidebar-container {
pointer-events: none;
transition-duration: 0.3s;
transform: translate3d(-$base-sidebar-width, 0, 0);
}
}
}
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
}
}
// when menu collapsed
.el-menu--vertical {
&>.el-menu {
.svg-icon {
margin-right: 16px;
}
}
.nest-menu .el-sub-menu>.el-sub-menu__title,
.el-menu-item {
&:hover {
// you can use $sub-menuHover
background-color: rgba(0, 0, 0, 0.06) !important;
}
}
// the scroll bar appears when the sub-menu is too long
>.el-menu--popup {
max-height: 100vh;
overflow-y: auto;
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
}

View File

@@ -0,0 +1,49 @@
// global transition css
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/* fade-transform */
.fade-transform--move,
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all .5s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all .5s;
}
.breadcrumb-leave-active {
position: absolute;
}

View File

@@ -0,0 +1,65 @@
// base color
$blue: #324157;
$light-blue: #3A71A8;
$red: #C03639;
$pink: #E65D6E;
$green: #30B08F;
$tiffany: #4AB7BD;
$yellow: #FEC171;
$panGreen: #30B08F;
// 默认菜单主题风格
$base-menu-color: #bfcbd9;
$base-menu-color-active: #f4f4f5;
$base-menu-background: #304156;
$base-logo-title-color: #ffffff;
$base-menu-light-color: rgba(0, 0, 0, 0.7);
$base-menu-light-background: #ffffff;
$base-logo-light-title-color: #001529;
$base-sub-menu-background: #1f2d3d;
$base-sub-menu-hover: #001528;
// 自定义暗色菜单风格
/**
$base-menu-color:hsla(0,0%,100%,.65);
$base-menu-color-active:#fff;
$base-menu-background:#001529;
$base-logo-title-color: #ffffff;
$base-menu-light-color:rgba(0,0,0,.70);
$base-menu-light-background:#ffffff;
$base-logo-light-title-color: #001529;
$base-sub-menu-background:#000c17;
$base-sub-menu-hover:#001528;
*/
$--color-primary: #409EFF;
$--color-success: #67C23A;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;
$--color-info: #909399;
$base-sidebar-width: 200px;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
menuColor: $base-menu-color;
menuLightColor: $base-menu-light-color;
menuColorActive: $base-menu-color-active;
menuBackground: $base-menu-background;
menuLightBackground: $base-menu-light-background;
subMenuBackground: $base-sub-menu-background;
subMenuHover: $base-sub-menu-hover;
sideBarWidth: $base-sidebar-width;
logoTitleColor: $base-logo-title-color;
logoLightTitleColor: $base-logo-light-title-color;
primaryColor: $--color-primary;
successColor: $--color-success;
dangerColor: $--color-danger;
infoColor: $--color-info;
warningColor: $--color-warning;
}

View File

@@ -0,0 +1,53 @@
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName" :fill="color" />
</svg>
</template>
<script>
export default defineComponent({
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
},
color: {
type: String,
default: ''
},
},
setup(props) {
return {
iconName: computed(() => `#icon-${props.iconClass}`),
svgClass: computed(() => {
if (props.className) {
return `svg-icon ${props.className}`
}
return 'svg-icon'
})
}
}
})
</script>
<style scope lang="scss">
.sub-el-icon,
.nav-icon {
display: inline-block;
font-size: 15px;
margin-right: 12px;
position: relative;
}
.svg-icon {
width: 1em;
height: 1em;
position: relative;
fill: currentColor;
vertical-align: -2px;
}
</style>

View File

@@ -0,0 +1,10 @@
import * as components from '@element-plus/icons-vue'
export default {
install: (app) => {
for (const key in components) {
const componentConfig = components[key];
app.component(componentConfig.name, componentConfig);
}
},
};

5
src/directive/index.js Normal file
View File

@@ -0,0 +1,5 @@
import hasPermi from './permission/hasPermi'
export default function directive(app){
app.directive('hasPermi', hasPermi)
}

View File

@@ -0,0 +1,51 @@
/**
* v-hasPermi 操作权限处理
* Copyright (c) 2019 ruoyi
*/
import useUserStore from '@/store/modules/user'
export default {
mounted(el, binding, vnode) {
const {value} = binding
const all_permission = "*:*:*";
const permissions = useUserStore().permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
// mounted(el, binding, vnode) {
// const {value} = binding
// const all_permission = "*:*:*";
// setTimeout(()=>{
//
// const permissions = useUserStore().permissions
//
// if (value && value instanceof Array && value.length > 0) {
// const permissionFlag = value
// const hasPermissions = permissions.some(permission => {
// console.log("permission", permission)
// return all_permission === permission || permissionFlag.includes(permission)
// })
//
// if (!hasPermissions) {
// console.log("mei")
// el.parentNode && el.parentNode.removeChild(el)
// }
// } else {
// throw new Error(`请设置操作权限标签值`)
// }
// },1000)
//
// }
}

27
src/main.js Normal file
View File

@@ -0,0 +1,27 @@
import {createApp} from 'vue'
import '@/assets/styles/index.scss' // global css
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from './router'
import directive from './directive' // directive
// svg图标
import 'virtual:svg-icons-register'
import SvgIcon from '@/components/SvgIcon'
import elementIcons from '@/components/SvgIcon/svgicon'
import store from './store'
const app = createApp(App)
app.use(router)
// 使用element-plus 并且设置全局的大小
app.use(ElementPlus)
app.use(store)
app.use(elementIcons)
app.component('svg-icon', SvgIcon)
directive(app)
app.mount('#app')

70
src/router/index.js Normal file
View File

@@ -0,0 +1,70 @@
import {createRouter, createWebHashHistory, createWebHistory} from "vue-router";
import {getAuther, getPerm} from "@/api/auth.js";
import useUserStore from "@/store/modules/user.js";
export const constantRoutes = [
{
path: '/synthesis',
component: () => import('@/views/synthesis/index.vue'),
meta: {title: '综合统计'}
},
{
path: '/integrative',
component: () => import('@/views/integrative/index.vue'),
meta: {title: '全院综合统计'}
},
{
path: '/hospitalization',
component: () => import('@/views/hospitalization/index.vue'),
meta: {title: '住院统计'}
}, {
path: '/inhos',
component: () => import('@/views/inhos/index.vue'),
meta: {title: '住院统计'}
},
{
path: '/outpatient',
component: () => import('@/views/outpatient/index.vue'),
meta: {title: '门诊统计'}
},
{
path: '/opd',
component: () => import('@/views/opd/index.vue'),
meta: {title: '门诊统计'}
},
{
path: '/dept',
component: () => import('@/views/dept/index.vue'),
meta: {title: '科室统计'}
},
{
path: '/401',
component: () => import('@/views/error/401.vue'),
},
]
const router = createRouter(
{
history: createWebHashHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {top: 0}
}
},
}
)
router.beforeEach(async (to, from, next) => {
await useUserStore().getInfo(to)
if (to.meta.title) {
document.title = to.meta.title
}
next()
})
export default router;

3
src/store/index.js Normal file
View File

@@ -0,0 +1,3 @@
const store = createPinia()
export default store

34
src/store/modules/user.js Normal file
View File

@@ -0,0 +1,34 @@
import {getPerm} from "@/api/auth.js";
const useUserStore = defineStore(
'user',
{
state: () => ({
id: '',
name: '',
avatar: '',
roles: [],
permissions: [],
}),
actions: {
async getInfo(to) {
return new Promise((resolve, reject) => {
const a = to.fullPath
var strings = a.split('=');
if (strings[1] === '' || strings[1] === null || strings[1] === undefined) {
reject("没有openid")
}
let req = {
'openid': strings[1]
}
getPerm(req).then(res => {
this.permissions = res.data
resolve(res)
})
})
},
}
})
export default useUserStore

6
src/utils/errorCode.js Normal file
View File

@@ -0,0 +1,6 @@
export default {
'401': '认证失败,无法访问系统资源',
'403': '当前操作没有权限',
'404': '访问资源不存在',
'default': '系统未知错误,请反馈给管理员'
}

67
src/utils/request.js Normal file
View File

@@ -0,0 +1,67 @@
import axios from 'axios'
import errorCode from '@/utils/errorCode'
import {tansParams} from '@/utils/ruoyi'
import {ElNotification, ElMessage} from 'element-plus'
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时
timeout: 30000
})
// request拦截器
service.interceptors.request.use(config => {
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 20000;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
if (code === 401) {
//提示校验失败openid不对重定向
ElMessage({message: msg, type: 'error'})
return Promise.reject('无效的会话,请确认身份!')
} else if (code === 500) {
ElMessage({message: msg, type: 'error'})
return Promise.reject(new Error(msg))
} else if (code === 601) {
ElMessage({message: msg, type: 'warning'})
return Promise.reject(new Error(msg))
} else if (code !== 20000) {
ElNotification.error({title: msg})
return Promise.reject('error')
} else {
return Promise.resolve(res.data)
}
},
error => {
console.log('err' + error)
let {message} = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
ElMessage({message: message, type: 'error', duration: 5 * 1000})
return Promise.reject(error)
}
)
export default service

246
src/utils/ruoyi.js Normal file
View File

@@ -0,0 +1,246 @@
/**
* 通用js方法封装处理
* Copyright (c) 2019 ruoyi
*/
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
// 表单重置
export function resetForm(refName) {
if (this.$refs[refName]) {
this.$refs[refName].resetFields();
}
}
// 添加日期范围
export function addDateRange(params, dateRange, propName) {
let search = params;
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
dateRange = Array.isArray(dateRange) ? dateRange : [];
if (typeof (propName) === 'undefined') {
search.params['beginTime'] = dateRange[0];
search.params['endTime'] = dateRange[1];
} else {
search.params['begin' + propName] = dateRange[0];
search.params['end' + propName] = dateRange[1];
}
return search;
}
// 回显数据字典
export function selectDictLabel(datas, value) {
if (value === undefined) {
return "";
}
var actions = [];
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + value)) {
actions.push(datas[key].label);
return true;
}
})
if (actions.length === 0) {
actions.push(value);
}
return actions.join('');
}
// 回显数据字典(字符串数组)
export function selectDictLabels(datas, value, separator) {
if (value === undefined || value.length ===0) {
return "";
}
if (Array.isArray(value)) {
value = value.join(",");
}
var actions = [];
var currentSeparator = undefined === separator ? "," : separator;
var temp = value.split(currentSeparator);
Object.keys(value.split(currentSeparator)).some((val) => {
var match = false;
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + temp[val])) {
actions.push(datas[key].label + currentSeparator);
match = true;
}
})
if (!match) {
actions.push(temp[val] + currentSeparator);
}
})
return actions.join('').substring(0, actions.join('').length - 1);
}
// 字符串格式化(%s )
export function sprintf(str) {
var args = arguments, flag = true, i = 1;
str = str.replace(/%s/g, function () {
var arg = args[i++];
if (typeof arg === 'undefined') {
flag = false;
return '';
}
return arg;
});
return flag ? str : '';
}
// 转换字符串undefined,null等转化为""
export function parseStrEmpty(str) {
if (!str || str == "undefined" || str == "null") {
return "";
}
return str;
}
// 数据合并
export function mergeRecursive(source, target) {
for (var p in target) {
try {
if (target[p].constructor == Object) {
source[p] = mergeRecursive(source[p], target[p]);
} else {
source[p] = target[p];
}
} catch (e) {
source[p] = target[p];
}
}
return source;
};
/**
* 构造树型结构数据
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
};
var childrenListMap = {};
var nodeIds = {};
var tree = [];
for (let d of data) {
let parentId = d[config.parentId];
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = [];
}
nodeIds[d[config.id]] = d;
childrenListMap[parentId].push(d);
}
for (let d of data) {
let parentId = d[config.parentId];
if (nodeIds[parentId] == null) {
tree.push(d);
}
}
for (let t of tree) {
adaptToChildrenList(t);
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]];
}
if (o[config.childrenList]) {
for (let c of o[config.childrenList]) {
adaptToChildrenList(c);
}
}
}
return tree;
}
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && value !== "" && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&";
}
}
} else {
result += part + encodeURIComponent(value) + "&";
}
}
}
return result
}
// 返回项目路径
export function getNormalPath(p) {
if (p.length === 0 || !p || p == 'undefined') {
return p
};
let res = p.replace('//', '/')
if (res[res.length - 1] === '/') {
return res.slice(0, res.length - 1)
}
return res;
}
// 验证是否为blob格式
export function blobValidate(data) {
return data.type !== 'application/json'
}

39
src/utils/vertify.js Normal file
View File

@@ -0,0 +1,39 @@
import CryptoJS from 'crypto-js';
const aecConfig = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
var key = 'hqe0nb384oyjwnprd386jmslvoyt2io1'
var iv = 'udfe1j8woobdmy51xbz7j4814aw9atdx'
key = CryptoJS.enc.Utf8.parse(key)
iv = CryptoJS.enc.Utf8.parse(iv)
//加密方法
function AES_EN(text) {
const encrypted = CryptoJS.AES.encrypt(text, key, {
iv,
...aecConfig
})
return encrypted.ciphertext.toString()
}
//解密方法
function AES_DE(text) {
text = CryptoJS.enc.Hex.parse(text)
const decrypt = CryptoJS.AES.decrypt({
ciphertext: text
}, key, {
iv,
...aecConfig
})
const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
return decryptedStr.toString()
}
export {
AES_EN,
AES_DE
}

125
src/views/dept/index.vue Normal file
View File

@@ -0,0 +1,125 @@
<script setup>
import {onMounted, ref} from "vue";
import moment from "moment/moment.js";
import {getDeptData} from "@/api/dept.js";
import {getDeptPerm} from "@/api/auth.js";
import {useRoute, useRouter} from "vue-router";
import {AES_DE} from "@/utils/vertify.js";
onMounted(() => {
deptPermList()
})
// 设置初始时间
let today = new Date();
today = moment(today).format("YYYYMMDD");
const value1 = ref([today, today]);
const showData = ref({
depts: "",
deptTableData: [],
})
function deptPermList() {
// 优先查询该openid的权限包含哪些科室
var route = useRoute();
var openid = route.query.openid;//从链接中获取openid
if (typeof (openid) != "undefined" && openid != null && openid != '') {
let req = {
openid: openid
}
getDeptPerm(req).then(res => {
// console.log(res)
showData.value.depts = res.data
tableInit()
})
} else {
//跳转到错误页面
var router = useRouter();
router.replace({path: "/401"})
}
}
function tableInit() {
let req = {
startTime: value1.value[0].toString(),
endTime: value1.value[1].toString(),
lb: "25",
Dept: showData.value.depts
}
getDeptData(req).then(res => {
// console.log(res)
showData.value.deptTableData = res.data
})
}
</script>
<template>
<div class="app-container">
<!--日期选择器-->
<div class="dateChoose">
<div class="dateText">周期</div>
<div class="dateWrap">
<el-date-picker
v-model="value1"
type="daterange"
style="width: 50vw"
value-format="YYYYMMDD"
range-separator="-"
start-placeholder="起始日期"
end-placeholder="结束日期"
size="small"
@change="tableInit"
/>
</div>
<div class="placeHolder"></div>
</div>
<div>
<!-- 科室统计-->
<span class="table-title">科室统计</span>
<el-table :data="showData.deptTableData" size="small" flexible
style="width: 100%; margin-top: 20px;font-size: 10px" stripe border>
<el-table-column label="项目名称" prop="dxmmc" fixed align="center"></el-table-column>
<el-table-column label="项目数量" prop="sl" align="center"></el-table-column>
<el-table-column label="合计金额" prop="zje" align="center"></el-table-column>
</el-table>
</div>
</div>
</template>
<style scoped>
.dateChoose {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-top: 0.5vh;
margin-bottom: 1vh;
font-size: small;
text-align: center;
}
.dateText {
width: 15vw;
}
.dateWrap {
width: 15vw;
}
.placeHolder {
width: 42vw;
}
.placeHolder2 {
width: 25vw;
}
.table-title {
text-align: center;
display: block;
margin-top: 1vh;
margin-bottom: 1vh;
opacity: 0.7 !important;
}
</style>

82
src/views/error/401.vue Normal file
View File

@@ -0,0 +1,82 @@
<template>
<div class="errPage-container">
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
返回
</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">
401错误!
</h1>
<h2>您没有访问权限</h2>
<h6>对不起您没有访问权限请不要进行非法操作您可以返回主页面</h6>
<ul class="list-unstyled">
<li class="link-type">
<router-link to="/">
回首页
</router-link>
</li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
</div>
</template>
<script setup>
import errImage from "@/assets/401_images/401.gif";
let { proxy } = getCurrentInstance();
const errGif = ref(errImage + "?" + +new Date());
function back() {
if (proxy.$route.query.noGoBack) {
proxy.$router.push({ path: "/" });
} else {
proxy.$router.go(-1);
}
}
</script>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none !important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

227
src/views/error/404.vue Normal file
View File

@@ -0,0 +1,227 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">
404错误!
</div>
<div class="bullshit__headline">
{{ message }}
</div>
<div class="bullshit__info">
对不起您正在寻找的页面不存在尝试检查URL的错误然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容
</div>
<router-link to="/index" class="bullshit__return-home">
返回首页
</router-link>
</div>
</div>
</div>
</template>
<script setup>
let message = computed(() => {
return '找不到网页!'
})
</script>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@@ -0,0 +1,603 @@
<script setup>
import * as echarts from 'echarts';
import {onMounted, ref} from "vue";
import {useRoute, useRouter} from "vue-router";
import {AES_DE} from "@/utils/vertify.js";
import moment from "moment/moment.js";
import {getHosData} from '@/api/hospitalization.js'
onMounted(() => {
generateLine()
generateNowBar()
generateTodayBar()
changeHosHeaderDate()
getNowData()
changeHosBottomDate()
verifyOpenid();
})
//设置展示的数据
const showData = ref({
outpatients:"",
daysAvg:"",
cwsyl:"",
inpatients:"",
badpatients:"",
inhospatients:"",
outhospatients:"",
lastOuthospatients:"",
lastInhospatients:"",
deptOptions:[],
inHosOptions:[],
outHosOptions:[]
});
// 设置初始时间
let today = new Date();
today= moment(today).format("YYYYMMDD");
const value1 = ref([today+"00:00:00",today+"23:59:59"]);
const value2 = ref(today);
//设置可以访问的人员
const releaseUser = ref(['o-ZxO47Otvo5Rsq7kN-4PHvZIOt8', 'o-ZxO4-oIddDT0ecgxNEkvApJ-N0','o-ZxO4xd9dV7Dd6OOMKD6ukomD8c','o-ZxO47fz4FjMvL5ESLKcl0YqVJ8','o-ZxO49keb-3wV3CLAuFDpvrx-LQ',
'o-ZxO47o9FYvO-Eor_JPCwzXS3ig','o-ZxO49XU81BKOSnDzKVGWrAZXGg']);
function verifyOpenid() {
var route = useRoute();
var openid = route.query.openid;//从链接中获取openid
if (typeof (openid) != "undefined" && openid != null && openid != '') {
var de = AES_DE(openid);//解密
if (!releaseUser.value.includes(de)) {
//跳转到错误页面
var router = useRouter();
router.replace({path: "/401"})
}
} else {
//跳转到错误页面
var router = useRouter();
router.replace({path: "/401"})
}
}
function changeHosHeaderDate(){
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "15"
}
for (let i = 15; i < 23; i++) {
switch (i) {
case 15:
getHosData(req).then(res=>{
showData.value.outpatients = res.data.outpatients
})
break;
case 16:
req.lb="16"
getHosData(req).then(res=>{
showData.value.daysAvg = res.data
})
break;
case 17:
req.lb="17"
getHosData(req).then(res=>{
showData.value.cwsyl = res.data
})
break;
}
}
getEachDeptInHos()
}
var eachDeptOption = {
title: {
text: '各科入院概况',
top: '10%',
textStyle: {
fontSize: 13,
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
left: 'right',
data: ['出院', '入院'],
top: "10%"
},
grid: {
left: '3%',
right:
'4%',
bottom:
'3%',
containLabel:
true
}
,
xAxis: [
{
type: 'value'
}
],
yAxis:
[
{
type: 'category',
axisTick: {
show: false
},
data: [],
}
],
series:
[
{
name: '入院',
type: 'bar',
stack: 'Total',
label: {
show: true,
position: 'inside'
},
emphasis: {
focus: 'series'
},
data: []
},
{
name: '出院',
type: 'bar',
stack: 'Total',
label: {
show: true,
position: 'inside'
},
emphasis: {
focus: 'series'
},
data: []
}
]
}
const ec_today_bar = ref(null)
var ecTodayBar = {}
function generateTodayBar() {
ecTodayBar = echarts.init(ec_today_bar.value)
}
function getEachDeptInHos(){
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "22"
}
getHosData(req).then(res=>{
let inDeptMap = res.inPatients
var deptArray = []
var inHosArray = []
var outHosArray = []
for (const Key in inDeptMap) {
deptArray.push(Key)
inHosArray.push(inDeptMap[Key])
}
let OUTDeptMap = res.outPatients
for (const Key in OUTDeptMap) {
outHosArray.push(-OUTDeptMap[Key])
}
eachDeptOption.yAxis[0].data=deptArray
eachDeptOption.series[0].data= inHosArray
eachDeptOption.series[1].data= outHosArray
eachDeptOption && ecTodayBar.setOption(eachDeptOption)
})
}
function getNowData(){
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "18"
}
for (let i = 18; i <=19; i++) {
switch (i) {
case 18:
getHosData(req).then(res=>{
showData.value.inpatients = res.data.inpatients
})
break;
case 19:
req.lb= "19"
getHosData(req).then(res=>{
showData.value.badpatients = res.data.badpatients
})
}
}
getNowInHosData();
getNowAndYesInHosData();
}
function getNowAndYesInHosData(){
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "21"
}
getHosData(req).then(res=>{
console.log(res)
showData.value.inhospatients= res.data.inhospatients
showData.value.outhospatients= res.data.outhospatients
})
req.startTime= moment(value1.value[0].toString().substring(0,8)).subtract(1,'days').format('YYYYMMDD')+"00:00:00"
req.endTime = moment(value1.value[0].toString().substring(0,8)).subtract(1,'days').format('YYYYMMDD')+"23:59:59"
getHosData(req).then(res=>{
showData.value.lastInhospatients= res.data.inhospatients
showData.value.lastOuthospatients= res.data.outhospatients
})
}
var nowInHosOption = {
title: {
text: '当前在院人数',
top: '10%',
textStyle: {
fontSize: 13,
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01],
},
yAxis: {
type: 'category',
data: ['孕产保健部', '妇女保健部', '儿童保健部统计'],
axisLabel: {
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
series: [
{
name: '人次',
type: 'bar',
data: [2, 18, 10]
},
]
};
const ec_now_bar = ref(null)
var ecNowBar ={}
function generateNowBar() {
ecNowBar = echarts.init(ec_now_bar.value)
}
function getNowInHosData(){
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "20"
}
getHosData(req).then(res=>{
let tempArray = res.data
var deptArray = []
var inHosArray = []
for (const key in tempArray) {
deptArray.push(key)
inHosArray.push(tempArray[key])
}
nowInHosOption.yAxis.data = deptArray
nowInHosOption.series[0].data = inHosArray
nowInHosOption && ecNowBar.setOption(nowInHosOption)
})
}
var inAndOutHosMonthOption = {
title: {
show: true,
text: '出入院人数变化情况近12月',
left: 'center',
top: "3%",
textStyle: {
fontSize: 13,
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
left: 'right',
top: "12%"
},
xAxis: {
type: 'category',
data: [],
axisLabel: {
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
yAxis: {
type: 'value'
},
series: [
{
name: "入院",
data: [],
type: 'line',
smooth: true
},
{
name: "出院",
data: [],
type: 'line',
smooth: true
}
]
};
const ec_month_line = ref(null)
var ecMonthLine = {}
function generateLine() {
ecMonthLine = echarts.init(ec_month_line.value);
}
function changeHosBottomDate(){
let startDate = moment(value2.value).subtract(11,'month').format("YYYYMMDD")+"00:00:00"
let endDate = value2.value.toString()+"23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "23"
}
getHosData(req).then(res=>{
let tempInHosMap = res.inPatients
let tempOutHosMap = res.outPatients
let dateArray = []
let inHosArray = []
let outHosArrray = []
for (const key in tempInHosMap) {
dateArray.push(key)
inHosArray.push(tempInHosMap[key])
}
for (const key in tempOutHosMap) {
outHosArrray.push(tempOutHosMap[key])
}
inAndOutHosMonthOption.xAxis.data = dateArray
inAndOutHosMonthOption.series[0].data = inHosArray
inAndOutHosMonthOption.series[1].data = outHosArrray
inAndOutHosMonthOption && ecMonthLine.setOption(inAndOutHosMonthOption);
})
}
</script>
<template>
<div class="app-container">
<!--日期选择器-->
<div class="dateChoose">
<div class="dateText">周期</div>
<div class="dateWrap">
<el-date-picker
v-model="value1"
type="daterange"
style="width: 50vw"
value-format="YYYYMMDD00:00:00"
range-separator="-"
start-placeholder="起始日期"
end-placeholder="结束日期"
size="small"
@change="changeHosHeaderDate"
/>
</div>
<div class="placeHolder"></div>
</div>
<!--数值-->
<div class="number-group">
<div>
<div>出院人次</div>
<div class="place-holder"></div>
<div>{{ showData.outpatients }}</div>
</div>
<div>
<div>平均住院日</div>
<div class="place-holder"></div>
<div>{{showData.daysAvg}}</div>
</div>
<div>
<div>病床使用率</div>
<div class="place-holder"></div>
<div>{{showData.cwsyl}}%</div>
</div>
</div>
<div class="ec-group">
<div class="ec_class" ref="ec_today_bar"></div>
</div>
<el-divider/>
<!--数值-->
<div class="number-group">
<div>
<div>当前在院</div>
<div class="place-holder"></div>
<div>{{ showData.inpatients }}</div>
</div>
<div>
<div>当前危重</div>
<div class="place-holder"></div>
<div>{{ showData.badpatients }}</div>
</div>
</div>
<div class="ec-group">
<div class="ec_class" ref="ec_now_bar"></div>
</div>
<div class="dynamic">
<div>住院动态</div>
</div>
<!--数值人次-->
<div class="person-times-group">
<div class="person-times-item">
<div class="person-times">
<div>入院人次</div>
<div>{{ showData.inhospatients }}</div>
</div>
<div class="person-times-yes">
<div>昨日</div>
<div>{{ showData.lastInhospatients }}</div>
</div>
</div>
<div class="person-times-item">
<div class="person-times">
<div>出院人次</div>
<div>{{ showData.outhospatients }}</div>
</div>
<div class="person-times-yes">
<div>昨日</div>
<div>{{ showData.lastOuthospatients }}</div>
</div>
</div>
<el-divider/>
</div>
<div class="dateChoose1">
<div class="dateText">周期</div>
<div class="dateWrap">
<el-date-picker
v-model="value2"
type="date"
style="width: 35vw"
value-format="YYYYMMDD"
placeholder="时间节点"
size="small"
@change="changeHosBottomDate"
/>
</div>
<div class="placeHolder2"></div>
</div>
<div class="ec-group">
<div class="ec_class" ref="ec_month_line"></div>
</div>
</div>
</template>
<style scoped>
.person-times-group {
width: 98vw;
height: 7vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
margin-top: 1.5vh;
}
.person-times-item {
display: flex;
flex-direction: row;
align-items: center;
font-size: small;
}
.person-times {
width: 58vw;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.person-times-yes {
width: 40vw;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.number-group {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-top: 3vh;
font-size: xx-small;
text-align: center;
}
.place-holder {
height: 1vh;
}
.dynamic {
width: 92vw;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
font-size: small;
font-weight: bold;
}
.dateChoose {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-top: 0.5vh;
font-size: small;
text-align: center;
}
.dateChoose1 {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-top: 0.5vh;
font-size: small;
text-align: center;
margin-top: 3vh;
}
.dateText{
width: 15vw;
}
.dateWrap{
width: 15vw;
}
.placeHolder{
width: 42vw;
}
.placeHolder2{
width:25vw ;
}
.ec-group {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ec_class {
width: 95vw;
height: 40vh;
}
</style>

1424
src/views/inhos/index.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,743 @@
<script setup>
import moment from "moment/moment.js";
import * as echarts from 'echarts';
import {onMounted, ref} from "vue";
import {getOutPatientData, getSpecialData} from "@/api/outpatient.js"
onMounted(() => {
generateEachMonthLine()
generateThreeYearLine()
generateMTMonthLine()
generateSPMTLineLine()
initData()
})
// 设置初始时间
let today = new Date();
today = moment(today).format("YYYYMMDD");
const value2 = ref(today);
const showData = ref({
completenessData: [],
integrativeData: [],
threeYearsData: [],
threeYearsTableHeader: [],
MTNumHeader: [],
MTNumData: [],
MTWorkHeader: [],
MTWorkData: [],
MTWorkTableData: [],
})
function getMTWorkCount() {
let startDate = moment(value2.value).subtract(11, 'month').format("YYYYMMDD") + "00:00:00"
let endDate = value2.value.toString() + "23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "7"
}
ecMTMonthLine.showLoading()
getOutPatientData(req).then(res => {
showData.value.MTWorkHeader = res.data.tableHeader
showData.value.MTWorkData = res.data.tableData
MTMonthOption.legend.data = res.data.legendList
MTMonthOption.xAxis.data = res.data.lineX
let tempObject = res.data.lineData
for (let key in tempObject) {
var tempSeries = {
name: key,
// stack: 'Total',
type: 'line',
data: tempObject[key]
}
MTMonthOption.series.push(tempSeries)
}
MTMonthOption && ecMTMonthLine.setOption(MTMonthOption)
ecMTMonthLine.hideLoading()
})
}
function getCompletenessData() {
let startDate = value2.value.toString() + "00:00:00"
let endDate = startDate.substring(0, 8) + "23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "1"
}
getOutPatientData(req).then(res => {
showData.value.completenessData = res.completeness;
})
}
function getIntegrativeData() {
let startDate = moment(value2.value).subtract(11, 'month').format("YYYYMMDD") + "00:00:00"
let endDate = value2.value.toString() + "23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "2"
}
ecMonthLine.showLoading()
getOutPatientData(req).then(res => {
showData.value.integrativeData = res.data.integrative;
eachMonthOption.xAxis.data = res.data.dates
eachMonthOption.series[0].data = res.data.mzwces
eachMonthOption.series[1].data = res.data.zywce
eachMonthOption.series[2].data = res.data.zwces
ecMonthLine.hideLoading()
eachMonthOption && ecMonthLine.setOption(eachMonthOption);
})
}
function getThreeYearsData() {
let startDate = moment(value2.value).subtract(2, 'year').format("YYYYMMDD") + "00:00:00"
let endDate = value2.value.toString() + "23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "3"
}
ecYearLine.showLoading()
getOutPatientData(req).then(res => {
showData.value.threeYearsData = res.data.tableData
showData.value.threeYearsTableHeader = res.data.tableHeader
threeYearOption.legend.data = res.data.legend
threeYearOption.xAxis.data = res.data.xAxis
let tempObject = res.data.seriesData
for (let key in tempObject) {
var tempSeries = {
name: key,
type: 'line',
data: tempObject[key]
}
threeYearOption.series.push(tempSeries)
}
ecYearLine.hideLoading()
threeYearOption && ecYearLine.setOption(threeYearOption);
})
}
var ec_month_line = ref(null);
var ec_year_line = ref(null);
const ec_MTMonth_line = ref(null);
const ec_spmt_line = ref(null);
var ecSPMTLine = {};
var ecMonthLine = {};
var ecYearLine = {};
var ecMTMonthLine = {};
var MTMonthOption = {
tooltip: {
trigger: 'axis',
position: function (point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是以外层div的左上角那一点为原点x轴向右y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX) {
x = 5;
} else { // 左边放的下
x = pointX - boxWidth;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight > pointY) {
y = 5;
} else { // 上边放得下
y = pointY - boxHeight;
}
return [x, y];
}
},
legend: {
top: 20,
data: []
},
grid: {
left: '3%',
right: '4%',
y: 80,
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [],
axisLabel: {
//x轴文字的配置
show: true,
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
yAxis: {
type: 'value',
},
series: []
}
var eachMonthOption = {
tooltip: {
trigger: 'axis',
position: function (point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是以外层div的左上角那一点为原点x轴向右y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX) {
x = 5;
} else { // 左边放的下
x = pointX - boxWidth;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight > pointY) {
y = 5;
} else { // 上边放得下
y = pointY - boxHeight;
}
return [x, y];
}
},
legend: {
top: 20,
data: ['门诊', '住院', '合计',]
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [],
axisLabel: {
//x轴文字的配置
show: true,
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
yAxis: {
type: 'value'
},
series: [
{
name: '门诊',
type: 'line',
data: []
},
{
name: '住院',
type: 'line',
data: []
},
{
name: '合计',
type: 'line',
data: []
},
]
}
var threeYearOption = {
tooltip: {
trigger: 'axis',
position: function (point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是以外层div的左上角那一点为原点x轴向右y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX) {
x = 5;
} else { // 左边放的下
x = pointX - boxWidth;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight > pointY) {
y = 5;
} else { // 上边放得下
y = pointY - boxHeight;
}
return [x, y];
}
},
legend: {
top: 20,
data: []
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [],
axisLabel: {
//x轴文字的配置
show: true,
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
yAxis: {
type: 'value'
},
series: []
}
var SPMTOption = {
title: {
show: true,
text: '',
left: 'center',
top: "3%",
textStyle: {
fontSize: 15,
opacity: 0.7
},
},
tooltip: {
trigger: 'axis',
position: function (point, params, dom, rect, size) {
// 鼠标坐标和提示框位置的参考坐标系是以外层div的左上角那一点为原点x轴向右y轴向下
// 提示框位置
var x = 0; // x坐标位置
var y = 0; // y坐标位置
// 当前鼠标位置
var pointX = point[0];
var pointY = point[1];
// 外层div大小
// var viewWidth = size.viewSize[0];
// var viewHeight = size.viewSize[1];
// 提示框大小
var boxWidth = size.contentSize[0];
var boxHeight = size.contentSize[1];
// boxWidth > pointX 说明鼠标左边放不下提示框
if (boxWidth > pointX) {
x = 5;
} else { // 左边放的下
x = pointX - boxWidth;
}
// boxHeight > pointY 说明鼠标上边放不下提示框
if (boxHeight > pointY) {
y = 5;
} else { // 上边放得下
y = pointY - boxHeight;
}
return [x, y];
}
},
legend: {
top: 40,
data: []
},
grid: {
left: '3%',
right: '4%',
y: 80,
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
axisLabel: {
//x轴文字的配置
show: true,
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
yAxis: {
type: 'value',
},
series: []
}
const objectSpanMethod = ({
row,
column,
rowIndex,
columnIndex,
}) => {
if (columnIndex === 0) {
if (rowIndex % 3 === 0) {
return {
rowspan: 3,
colspan: 1,
}
} else {
return {
rowspan: 0,
colspan: 0,
}
}
}
}
function getMTWorkTableData() {
let startDate = moment(value2.value).subtract(2, 'year').format("YYYYMMDD") + "00:00:00"
let endDate = value2.value.toString() + "23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "9"
}
getOutPatientData(req).then(res => {
showData.value.MTWorkTableData = res.data
})
}
function clickMTCell(e) {
let startDate = moment(value2.value).subtract(2, 'year').format("YYYYMMDD") + "00:00:00"
let endDate = value2.value.toString() + "23:59:59"
var req = {}
if (e) {
SPMTOption.title.text = e.ks
req = {
startTime: startDate,
endTime: endDate,
lb: "9",
Dept: e.ks
}
} else {
SPMTOption.title.text = '医学影像科'
req = {
startTime: startDate,
endTime: endDate,
lb: "9",
Dept: '医学影像科'
}
}
ecSPMTLine.showLoading()
getSpecialData(req).then(res => {
SPMTOption.legend.data = res.data.legend
let tempObj = res.data.seriesDatas
SPMTOption.series = []
for (let key in tempObj) {
var tempSeries = {
name: key,
// stack: 'Total',
type: 'line',
data: tempObj[key]
}
SPMTOption.series.push(tempSeries)
}
ecSPMTLine.hideLoading()
SPMTOption && ecSPMTLine.setOption(SPMTOption)
})
}
function getMTNums() {
// let startDate = moment().startOf('month').format('YYYYMMDD') + "00:00:00"
let startDate = value2.value.toString() + "00:00:00"
let endDate = startDate.substring(0, 8) + "23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "5"
}
getOutPatientData(req).then(res => {
showData.value.MTNumHeader = res.data.tableHeader
showData.value.MTNumData = res.data.tableData
})
}
function generateEachMonthLine() {
ecMonthLine = echarts.init(ec_month_line.value);
}
function generateThreeYearLine() {
ecYearLine = echarts.init(ec_year_line.value);
}
function generateMTMonthLine() {
ecMTMonthLine = echarts.init(ec_MTMonth_line.value)
}
function generateSPMTLineLine() {
ecSPMTLine = echarts.init(ec_spmt_line.value)
}
function initData() {
getIntegrativeData()
getThreeYearsData()
getCompletenessData()
clickMTCell()
getMTNums()
getMTNums()
getMTWorkTableData()
getMTWorkCount()
}
</script>
<template>
<!-- 日期选择框-->
<div class="app-container">
<div class="dateChoose">
<div class="dateText">今日日期</div>
<div class="dateWrap">
<el-date-picker
v-model="value2"
type="date"
style="width: 35vw"
value-format="YYYYMMDD"
placeholder="时间节点"
size="small"
@change="initData"
/>
</div>
<div class="placeHolder2"></div>
</div>
<!-- 表格1-->
<div v-hasPermi="['inte:completeness']">
<el-table :data="showData.completenessData" size="small" style="width: 100%; margin-top: 20px;font-size: 10px"
border stripe>
<el-table-column align="center" prop="lb" fixed/>
<el-table-column align="center" prop="mzwcd" label="门诊"/>
<el-table-column align="center" prop="zywcd" label="住院"/>
<el-table-column align="center" prop="zwcd" label="合计"/>
</el-table>
<el-divider/>
</div>
<!-- 表格二-->
<div v-hasPermi="['inte:integrative']">
<span class="table-title">近12个月每月预算完成额对比</span>
<el-table :data="showData.integrativeData" size="small" style="width: 100%; margin-top: 20px;font-size: 10px"
border height="230">
<el-table-column align="center" prop="ny"/>
<el-table-column align="center" prop="mzwce" label="门诊"/>
<el-table-column align="center" prop="zywce" label="住院"/>
<el-table-column align="center" prop="zwce" label="合计"/>
</el-table>
<!-- 折线图-->
<div class="ec-group">
<div class="ec_class" ref="ec_month_line"></div>
</div>
<el-divider/>
</div>
<!-- 表格三-->
<div v-hasPermi="['inte:threeYears']">
<span class="table-title">近三年每月总预算完成额对比</span>
<el-table :data="showData.threeYearsData" border size="small"
style="width: 100%; margin-top: 20px;font-size: 10px" height="250" stripe>
<el-table-column fixed="left" label="年" show-overflow-tooltip width="40" align="right">
<el-table-column fixed="left" prop="月" label="月" show-overflow-tooltip width="40"
align="center">
</el-table-column>
</el-table-column>
<el-table-column :label="item" :prop="item"
v-for="item in showData.threeYearsTableHeader" align="center"></el-table-column>
</el-table>
<div class="ec-group">
<div class="ec_class" ref="ec_year_line"></div>
</div>
</div>
<div v-hasPermi="['opd:MTNum']">
<span class="table-title">医技科室工作量</span>
<!-- <el-table :data="showData.MTNumData" size="small" height="200"-->
<!-- style="width: 100%; margin-top: 20px;font-size: 10px" stripe border>-->
<!-- <el-table-column label="分类" prop="分类" fixed align="center"></el-table-column>-->
<!-- <el-table-column :label="item" :prop="item"-->
<!-- v-for="item in showData.MTNumHeader" align="center"></el-table-column>-->
<!-- </el-table> -->
<el-table :data="showData.MTNumData" size="small" height="200"
style="width: 100%; margin-top: 20px;font-size: 10px" stripe border>
<el-table-column prop="lb" fixed align="center"></el-table-column>
<el-table-column prop="yxyxk" label="医学影像科" align="center"></el-table-column>
<el-table-column prop="yxjyk" label="医学检验科" align="center"></el-table-column>
<el-table-column prop="kfyxk" label="康复医学科" align="center"></el-table-column>
<el-table-column prop="hj" label="合计" align="center"></el-table-column>
<!-- <el-table-column :label="item" :prop="item"-->
<!-- v-for="item in showData.MTNumHeader" align="center"></el-table-column>-->
</el-table>
<el-divider/>
</div>
<div v-hasPermi="['opd:MTWork']">
<span class="table-title">近12个月医技科室每月工作量</span>
<el-table :data="showData.MTWorkData" size="small" height="275"
style="width: 100%; margin-top: 20px;font-size: 10px" stripe border>
<el-table-column fixed label="科室" align="right">
<el-table-column fixed prop="日期" label="日期" show-overflow-tooltip
align="center">
</el-table-column>
</el-table-column>
<el-table-column :label="item" :prop="item"
v-for="item in showData.MTWorkHeader" align="center"></el-table-column>
</el-table>
<div>
<div class="ec-group">
<div class="ec-mt-class" ref="ec_MTMonth_line"></div>
</div>
</div>
<el-divider/>
</div>
<div v-hasPermi="['opd:MTWorkTable']">
<span class="table-title">近三年医技科室每月总工作量</span>
<el-table
:data="showData.MTWorkTableData"
:span-method="objectSpanMethod"
@cell-click="clickMTCell"
border
size="small"
style="width: 100%; margin-top: 20px;font-size: 10px"
height="220"
>
<el-table-column prop="ks" align="center" label="科室" width="70" fixed>
</el-table-column>
<el-table-column prop="nf" fixed align="center" label="年" width="50"/>
<el-table-column prop="one" align="center" label="1月" width="50"/>
<el-table-column prop="two" align="center" label="2月" width="50"/>
<el-table-column prop="three" align="center" label="3月" width="50"/>
<el-table-column prop="four" align="center" label="4月" width="50"/>
<el-table-column prop="five" align="center" label="5月" width="50"/>
<el-table-column prop="six" align="center" label="6月" width="50"/>
<el-table-column prop="seven" align="center" label="7月" width="50"/>
<el-table-column prop="eight" align="center" label="8月" width="50"/>
<el-table-column prop="nine" align="center" label="9月" width="50"/>
<el-table-column prop="ten" align="center" label="10月" width="50"/>
<el-table-column prop="eleven" align="center" label="11月" width="50"/>
<el-table-column prop="twelve" align="center" label="12月" width="50"/>
</el-table>
<div class="ec-group">
<div class="ec-special-mt" ref="ec_spmt_line"></div>
</div>
<!-- <el-divider/>-->
</div>
</div>
</template>
<style scoped>
.table-title {
text-align: center;
display: block;
margin-top: 1vh;
margin-bottom: 1vh;
opacity: 0.7 !important;
}
.dateChoose {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-top: 0.5vh;
margin-bottom: 1vh;
font-size: small;
text-align: center;
}
.dateText {
width: 30vw;
}
.dateWrap {
width: 30vw;
}
.placeHolder2 {
width: 5vw;
}
.ec-group {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ec_class {
width: 90vw;
height: 40vh;
}
.ec-special-mt {
width: 90vw;
height: 40vh;
}
.ec-mt-class {
width: 90vw;
height: 50vh;
}
</style>

2001
src/views/opd/index.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,540 @@
<script setup>
import * as echarts from 'echarts';
import {onMounted, ref} from "vue";
import {useRoute, useRouter} from "vue-router";
import {AES_DE} from "@/utils/vertify.js";
import {getOutPatientData} from "@/api/outpatient.js"
import moment from "moment/moment.js";
import {ElLoading} from "element-plus";
onMounted(() => {
generateMonthLine();
generateDayLine();
generateDayBar();
outPatientDateChange();
getVisitsNumber();
getEmergencyVisitsNumber();
changeDate2();
verifyOpenid();
})
//设置可以访问的人员
const releaseUser = ref(['o-ZxO47Otvo5Rsq7kN-4PHvZIOt8', 'o-ZxO4-oIddDT0ecgxNEkvApJ-N0','o-ZxO4xd9dV7Dd6OOMKD6ukomD8c','o-ZxO47fz4FjMvL5ESLKcl0YqVJ8','o-ZxO49keb-3wV3CLAuFDpvrx-LQ',
'o-ZxO47o9FYvO-Eor_JPCwzXS3ig','o-ZxO49XU81BKOSnDzKVGWrAZXGg']);
// 设置初始时间
let today = new Date();
// let yesterday = new Date(today.getTime()-24*60*60*1000);
today= moment(today).format("YYYYMMDD");
// yesterday=moment(yesterday).format("YYYYMMDD");
const value1 = ref([today+"00:00:00",today+"23:59:59"]);
const value2 = ref(today);
// const value1 = ref([yesterday+"00:00:00",today+"00:00:00"]);
//设置展示的数据
const showData = ref({
numberPeopleOutp:'',
medfeeAvg: '',
outMedfeeAvg: '',
visitsNumber: '',
lastVisitsNumber: '',
emergencyVisitsNumber: '',
lastEmergencyVisitsNumber: '',
monthOptions:[],
monthDataOptions:[],
weekOptions:[],
weekDataOptions:[],
eachDeptOptions:[],
eachDeptDataOptions:[],
});
//定义获取dom准备dom初始化容器
const ec_month_line= ref(null) //ref标签获取dom
const ec_day_bar = ref(null)
const ec_day_line = ref(null)
var ecDayLine = {}
var ecMonthLine = {}
var ecDayBar = {}
// 图形的options
var eachDeptOption = {
title: {
text: '各科门诊人次',
top: '10%',
textStyle: {
fontSize: 13,
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01],
},
yAxis: {
type: 'category',
data: [],
axisLabel: {
interval: 0, // 设置数据间隔
rotate: 40, // 标题倾斜
}
},
series: [
{
name: '人次',
type: 'bar',
data: []
},
]
};
var weekOption = {
title: {
show: true,
text: '七日门诊变化情况',
left: 'center',
top: "10%",
textStyle: {
fontSize: 13,
}
},
tooltip: {
trigger: 'axis',
},
xAxis: {
type: 'category',
data: [],
axisLabel: {
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
yAxis: {
type: 'value'
},
series: [
{
data: [],
type: 'line',
smooth: true
}
]
};
var twelveMonthOption = {
title: {
show: true,
text: '门诊人次变化情况近12月',
left: 'center',
top: "10%",
textStyle: {
fontSize: 13,
}
},
tooltip: {
trigger: 'axis',
},
xAxis: {
type: 'category',
data: [],
axisLabel: {
interval: 0, // 设置数据间隔
rotate: 38, // 标题倾斜
}
},
yAxis: {
type: 'value',
axisLabel: {
interval: 0, // 设置数据间隔
rotate: 48, // 标题倾斜
}
},
series: [
{
data: [],
type: 'line',
smooth: true
}
]
}
// 临时展示测试数据
const workloadData = ref([])
function verifyOpenid() {
var route = useRoute();
var openid = route.query.openid;//从链接中获取openid
if (typeof (openid) != "undefined" && openid != null && openid != '') {
var de = AES_DE(openid);//解密
if (!releaseUser.value.includes(de)) {
//跳转到错误页面
var router = useRouter();
router.replace({path: "/401"})
}
} else {
//跳转到错误页面
var router = useRouter();
router.replace({path: "/401"})
}
}
// 初始化echarts
function generateDayBar() {
ecDayBar = echarts.init(ec_day_bar.value);
}
function generateDayLine() {
ecDayLine = echarts.init(ec_day_line.value);
}
function generateMonthLine() {
ecMonthLine = echarts.init(ec_month_line.value);
}
function outPatientDateChange() {
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "7"
}
for (var i = 7; i <= 14; i++) {
if (i === 7) {
getOutPatientData(req).then(res => {
showData.value.numberPeopleOutp = res.data.numberPeopleOutp
})
}
if (i === 12) {
req.lb = "12"
getOutPatientData(req).then(res => {
showData.value.medfeeAvg = res.data.medfeeAvg
})
}
if (i === 13) {
req.lb="13"
getOutPatientData(req).then(res => {
showData.value.outMedfeeAvg=res.data.outMedfeeAvg
})
}
}
changeMonthLine()
changeWeekLine()
changeEachDeptBar()
}
function changeMonthLine(){
let startDate = moment(value2.value).subtract(11,'month').format("YYYYMMDD")+"00:00:00"
let endDate = value2.value.toString()+"23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "14"
}
var loadIngOptions={lock: true,
text: '数据加载中',
background: 'rgba(0, 0, 0, 0.1)'}
const loadingInstance=ElLoading.service(loadIngOptions)
getOutPatientData(req).then(res => {
showData.value.monthOptions = []
showData.value.monthDataOptions = []
var map = res.data
for (let key in map) {
showData.value.monthOptions.push(key)
showData.value.monthDataOptions.push(map[key])
}
twelveMonthOption.xAxis.data = showData.value.monthOptions
twelveMonthOption.series[0].data = showData.value.monthDataOptions
twelveMonthOption && ecMonthLine.setOption(twelveMonthOption);
loadingInstance.close()
})
}
function changeWeekLine(){
let startDate = moment(value2.value).subtract(6,"days").format("YYYYMMDD")+"00:00:00"
let endDate = value2.value.toString()+"23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "10"
}
getOutPatientData(req).then(res => {
showData.value.weekOptions = []
showData.value.weekDataOptions = []
var map = res.data
for (let key in map) {
showData.value.weekOptions.push(key)
showData.value.weekDataOptions.push(map[key])
}
weekOption.xAxis.data = showData.value.weekOptions
weekOption.series[0].data = showData.value.weekDataOptions
weekOption && ecDayLine.setOption(weekOption);
})
}
function changeEachDeptBar(){
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "11"
}
getOutPatientData(req).then(res => {
showData.value.eachDeptOptions = []
showData.value.eachDeptDataOptions = []
var map = res.data
for (let key in map) {
showData.value.eachDeptOptions.push(key)
showData.value.eachDeptDataOptions.push(map[key])
}
eachDeptOption.yAxis.data = showData.value.eachDeptOptions
eachDeptOption.series[0].data = showData.value.eachDeptDataOptions
eachDeptOption && ecDayBar.setOption(eachDeptOption)
})
}
function getVisitsNumber(){
let startDate = value1.value[0].toString()
let endDate = startDate.substring(0,8)+"23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "7"
}
getOutPatientData(req).then(res => {
showData.value.visitsNumber = res.data.numberPeopleOutp
})
let lastStartDate = moment(value1.value[0].toString().substring(0,8)).subtract(1,'days').format('YYYYMMDD')+"00:00:00"
let lastEndDate = moment(value1.value[0].toString().substring(0,8)).subtract(1,'days').format('YYYYMMDD')+"23:59:59"
let lastReq = {
startTime: lastStartDate,
endTime: lastEndDate,
lb: "7"
}
getOutPatientData(lastReq).then(res => {
showData.value.lastVisitsNumber = res.data.numberPeopleOutp
})
}
function getEmergencyVisitsNumber(){
let startDate = value1.value[0].toString()
let endDate = startDate.substring(0,8)+"23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "8"
}
getOutPatientData(req).then(res => {
showData.value.emergencyVisitsNumber = res.data.numberPeopleOutp
})
let lastStartDate = moment(value1.value[0].toString().substring(0,8)).subtract(1,'days').format('YYYYMMDD')+"00:00:00"
let lastEndDate = moment(value1.value[0].toString().substring(0,8)).subtract(1,'days').format('YYYYMMDD')+"23:59:59"
let lastReq = {
startTime: lastStartDate,
endTime: lastEndDate,
lb: "8"
}
getOutPatientData(lastReq).then(res => {
showData.value.lastEmergencyVisitsNumber = res.data.numberPeopleOutp
})
}
function changeDate2(){
changeWeekLine()
changeMonthLine()
getTableData()
}
function getTableData(){
let startDate = value2.value.toString()+"00:00:00"
let endDate = startDate.substring(0,8)+"23:59:59"
let req = {
startTime: startDate,
endTime: endDate,
lb: "24"
}
getOutPatientData(req).then(res => {
workloadData.value=res.workDetails
})
}
</script>
<template>
<div class="app-container">
<!--日期选择器-->
<div class="dateChoose">
<div class="dateText">周期</div>
<div class="dateWrap">
<el-date-picker
v-model="value1"
type="daterange"
style="width: 50vw"
value-format="YYYYMMDD00:00:00"
range-separator="-"
start-placeholder="起始日期"
end-placeholder="结束日期"
size="small"
@change="outPatientDateChange"
/>
</div>
<div class="placeHolder"></div>
</div>
<!--数值-->
<div class="number-group">
<div>
<div>门诊人次</div>
<div class="place-holder"></div>
<div>{{showData.numberPeopleOutp}}</div>
</div>
<div>
<div>门诊次均费用</div>
<div class="place-holder"></div>
<div>{{showData.medfeeAvg}}</div>
</div>
<div>
<div>门诊次均药费</div>
<div class="place-holder"></div>
<div>{{showData.outMedfeeAvg}}</div>
</div>
</div>
<div class="ec-group">
<div class="ec_class" ref="ec_day_bar"></div>
</div>
<el-divider/>
<!--数值人次-->
<div class="person-times-group">
<div class="person-times-item">
<div class="person-times">
<div>门诊人次</div>
<div>{{showData.visitsNumber}}</div>
</div>
<div class="person-times-yes">
<div>昨日</div>
<div>{{ showData.lastVisitsNumber }}</div>
</div>
</div>
<div class="person-times-item">
<div class="person-times">
<div>急诊人次</div>
<div>{{showData.emergencyVisitsNumber}}</div>
</div>
<div class="person-times-yes">
<div>昨日</div>
<div>{{showData.lastEmergencyVisitsNumber}}</div>
</div>
</div>
</div>
<el-divider />
<div class="dateChoose">
<div class="dateText">周期</div>
<div class="dateWrap">
<el-date-picker
v-model="value2"
type="date"
style="width: 35vw"
value-format="YYYYMMDD"
placeholder="时间节点"
size="small"
@change="changeDate2"
/>
</div>
<div class="placeHolder2"></div>
</div>
<div class="ec-group">
<div class="ec_class" ref="ec_day_line"></div>
</div>
<div class="ec-group">
<div class="ec_class" ref="ec_month_line"></div>
</div>
<el-table :data="workloadData" style="width: 100%">
<el-table-column align="center" label="门诊工作量明细统计(儿童保健部)">
<el-table-column fixed prop="tjlb" label="分类"/>
<el-table-column fixed prop="ettjxyss" label="儿童体检≤3岁"/>
<el-table-column prop="ettjdyss" label="儿童体检3岁"/>
<el-table-column prop="etrttj" label="儿童入托体检"/>
<el-table-column fixed="right" prop="hj" label="合计"/>
</el-table-column>
</el-table>
</div>
</template>
<style scoped>
.person-times-group {
width: 98vw;
height: 6vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
margin-top: 3vh;
}
.person-times-item {
display: flex;
flex-direction: row;
align-items: center;
font-size: small;
}
.person-times {
width: 58vw;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.person-times-yes {
width: 40vw;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.dateChoose {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-top: 0.5vh;
font-size: small;
text-align: center;
}
.dateText{
width: 15vw;
}
.dateWrap{
width: 15vw;
}
.placeHolder{
width: 42vw;
}
.placeHolder2{
width:25vw ;
}
.place-holder {
height: 1vh;
}
.number-group {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-top: 3vh;
font-size: xx-small;
text-align: center;
}
.ec-group {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ec_class {
width: 89vw;
height: 48vh;
}
</style>

View File

@@ -0,0 +1,241 @@
<script setup>
import * as echarts from 'echarts';
import {ref, onMounted} from "vue";
import {useRoute, useRouter} from "vue-router";
import {AES_DE} from "../../utils/vertify.js";
import {toRaw} from "vue";
import {getCountData, getSynPieCountData} from "@/api/synthesis.js"
import moment from 'moment';
onMounted(() => {
generateMediclPie();
synthesisDateChange();
verifyOpenid();
})
let today = new Date();
today= moment(today).format("YYYYMMDD");
const value1 = ref([today+"00:00:00",today+"23:59:59"]);
//设置可以访问的人员
const releaseUser = ref(['o-ZxO47Otvo5Rsq7kN-4PHvZIOt8', 'o-ZxO4-oIddDT0ecgxNEkvApJ-N0','o-ZxO4xd9dV7Dd6OOMKD6ukomD8c','o-ZxO47fz4FjMvL5ESLKcl0YqVJ8','o-ZxO49keb-3wV3CLAuFDpvrx-LQ',
'o-ZxO47o9FYvO-Eor_JPCwzXS3ig','o-ZxO49XU81BKOSnDzKVGWrAZXGg']);
const showData = ref({
totalIncome: '',
outpatientBudget: '',
inpMedfee: '',
pieData: [],
});
const medical_pie= ref(null) //ref标签获取dom
var option = {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'horizontal',
left: 'center',
bottom: '0.01',
textStyle: {
fontSize: 10,
}
},
series: [
{
name: '',
type: 'pie',
center: ['50%', '40%'],
radius: '35%',
data:[],
label: {
formatter: function (data) {
return `${data.name}:${data.value}%`
},
overflow: "break"
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
var medicalPie={}
function verifyOpenid() {
var route = useRoute();
var openid = route.query.openid;//从链接中获取openid
if (typeof (openid) != "undefined" && openid != null && openid != '') {
var de = AES_DE(openid);//解密
if (!releaseUser.value.includes(de)) {
//跳转到错误页面
var router = useRouter();
router.replace({path: "/401"})
}
} else {
//跳转到错误页面
var router = useRouter();
router.replace({path: "/401"})
}
}
function generateMediclPie() {
medicalPie = echarts.init(medical_pie.value);
}
function synthesisDateChange() {
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
lb: "1"
}
for (var i = 1; i <= 6; i++) {
if (i === 1) {
getCountData(req).then(res => {
showData.value.totalIncome = res.data.totalIncome;
})
}
if (i === 2) {
req.lb = "2"
getCountData(req).then(res => {
showData.value.outpatientBudget = res.data.outpatientBudget;
})
}
if (i === 3) {
req.lb = "3"
getCountData(req).then(res => {
showData.value.inpMedfee = res.data.inpMedfee;
})
}
}
changePie()
}
function changePie(){
let startDate = value1.value[0].toString()
let endDate = value1.value[1].toString()
let req = {
startTime: startDate,
endTime: endDate,
}
getSynPieCountData(req).then(res=>{
showData.value.pieData=[]
showData.value.pieData.push({
name:'药类占比',
value:res.data.yzb
})
showData.value.pieData.push({
name:'百元耗占比',
value:res.data.byhczb
})
showData.value.pieData.push({
name:'医务预算完成度占比',
value:res.data.ywsrzb
})
option.series[0].data=toRaw(showData.value.pieData)
option && medicalPie.setOption(option,true);
})
}
</script>
<template>
<div class="app-container">
<!--日期选择器-->
<div class="dateChoose">
<div class="dateText">周期</div>
<div class="dateWrap">
<el-date-picker
v-model="value1"
type="daterange"
style="width: 50vw"
value-format="YYYYMMDD00:00:00"
range-separator="-"
start-placeholder="起始日期"
end-placeholder="结束日期"
size="small"
@change="synthesisDateChange"
/>
</div>
<div class="placeHolder"></div>
</div>
<!--数值-->
<div class="number-group">
<div>
<div>总预算完成度</div>
<div class="place-holder"></div>
<div>{{ showData.totalIncome }} </div>
</div>
<div>
<div>门诊预算完成度</div>
<div class="place-holder"></div>
<div>{{ showData.outpatientBudget }} </div>
</div>
<div>
<div>住院预算完成度</div>
<div class="place-holder"></div>
<div>{{ showData.inpMedfee}}</div>
</div>
</div>
<!--三个饼图 金额占比-->
<div>
<div class="ec-group">
<div ref="medical_pie" class="ec_class"></div>
</div>
</div>
</div>
</template>
<style scoped>
.dateChoose {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-top: 0.5vh;
font-size: small;
text-align: center;
}
.dateText{
width: 20vw;
}
.dateWrap{
width: 15vw;
}
.placeHolder{
width: 42vw;
}
.place-holder {
height: 1vh;
}
.number-group {
width: 80vw;
display: flex;
flex-direction: row;
align-items: center;
text-align: center;
justify-content: space-between;
margin-top: 3vh;
font-size: small;
}
.ec-group {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 3vh;
}
.ec_class {
width: 80vw;
height: 25vh;
}
</style>

62
vite.config.js Normal file
View File

@@ -0,0 +1,62 @@
import {defineConfig, loadEnv} from 'vite'
import path from 'path'
import createVitePlugins from './vite/plugins'
export default defineConfig(({mode, command}) => {
const env = loadEnv(mode, process.cwd())
const {VITE_APP_ENV} = env
return {
base: VITE_APP_ENV === 'production' ? '/' : '/',
plugins: createVitePlugins(env, command === 'build'),
resolve: {
// https://cn.vitejs.dev/config/#resolve-alias
alias: {
// 设置路径
'~':
path.resolve(__dirname, './'),
// 设置别名
'@':
path.resolve(__dirname, './src')
}
,
// https://cn.vitejs.dev/config/#resolve-extensions
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}
,
// vite 相关配置
server: {
hmr: true,
port:
8099,
host:
true,
open:
true,
proxy:
{
// https://cn.vitejs.dev/config/#server-proxy
'/dev-api':
{
target: 'http://localhost:8083',
// target: 'http://110.153.1.138:8081',
changeOrigin:
true,
rewrite:
(p) => p.replace(/^\/dev-api/, ''),
}
,
'/prod-api':
{
target: 'http://110.153.1.138:8081',
changeOrigin:
true,
rewrite:
(p) => p.replace(/^\/prod-api/, '/prod-api/'),
}
}
}
,
}
})

View File

@@ -0,0 +1,12 @@
import autoImport from 'unplugin-auto-import/vite'
export default function createAutoImport() {
return autoImport({
imports: [
'vue',
'vue-router',
'pinia'
],
dts: false
})
}

10
vite/plugins/index.js Normal file
View File

@@ -0,0 +1,10 @@
import vue from '@vitejs/plugin-vue'
import createAutoImport from './auto-import.js'
import createSvgIcon from './svg-icon'
export default function createVitePlugins(viteEnv, isBuild) {
const vitePlugins = [vue()]
vitePlugins.push(createAutoImport())
vitePlugins.push(createSvgIcon(isBuild))
return vitePlugins
}

10
vite/plugins/svg-icon.js Normal file
View File

@@ -0,0 +1,10 @@
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default function createSvgIcon(isBuild) {
return createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: isBuild
})
}