47 changed files with 10574 additions and 0 deletions
@ -0,0 +1,14 @@ |
|||
# 页面标题 |
|||
VUE_APP_TITLE = 针灸管理系统 |
|||
|
|||
# 开发环境配置 |
|||
ENV = 'development' |
|||
|
|||
# 大唐会议管理系统/开发环境 |
|||
VUE_APP_BASE_API = '/dev-api' |
|||
VUE_APP_IMG_URL = 'https://test.tall.wiki/acupuncture' |
|||
VUE_APP_API_QZURL = 'https://test.tall.wiki/' |
|||
# 路由懒加载 |
|||
VUE_CLI_BABEL_TRANSPILE_MODULES = true |
|||
# 访问路径 |
|||
VUE_APP_PUBLIC_PATH = '/' |
@ -0,0 +1,11 @@ |
|||
# 页面标题 |
|||
VUE_APP_TITLE = 针灸管理系统 |
|||
|
|||
# 生产环境配置 |
|||
NODE_ENV = 'production' |
|||
# 因孚生产 |
|||
VUE_APP_BASE_API = 'https://test.tall.wiki/acupuncture' |
|||
VUE_APP_API_QZURL = 'https://test.tall.wiki/' |
|||
|
|||
# 访问路径 |
|||
VUE_APP_PUBLIC_PATH = '/acupunctureClient/' |
@ -0,0 +1,12 @@ |
|||
# 页面标题 |
|||
VUE_APP_TITLE = 针灸管理系统 |
|||
|
|||
BABEL_ENV = production |
|||
|
|||
NODE_ENV = production |
|||
|
|||
# 测试环境配置 |
|||
ENV = 'staging' |
|||
|
|||
# 针灸管理系统/测试环境 |
|||
VUE_APP_BASE_API = '/stage-api' |
@ -0,0 +1,30 @@ |
|||
## 开发 |
|||
|
|||
```bash |
|||
# 克隆项目 |
|||
git clone https://gitee.com/y_project/RuoYi-Vue |
|||
|
|||
# 进入项目目录 |
|||
cd ruoyi-ui |
|||
|
|||
# 安装依赖 |
|||
npm install |
|||
|
|||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 |
|||
npm install --registry=https://registry.npmmirror.com |
|||
|
|||
# 启动服务 |
|||
npm run dev |
|||
``` |
|||
|
|||
浏览器访问 http://localhost:80 |
|||
|
|||
## 发布 |
|||
|
|||
```bash |
|||
# 构建测试环境 |
|||
npm run build:stage |
|||
|
|||
# 构建生产环境 |
|||
npm run build:prod |
|||
``` |
Binary file not shown.
@ -0,0 +1,91 @@ |
|||
{ |
|||
"name": "ruoyi", |
|||
"version": "3.8.9", |
|||
"description": "针灸管理系统", |
|||
"author": "若依", |
|||
"license": "MIT", |
|||
"scripts": { |
|||
"dev": "vue-cli-service serve", |
|||
"build:prod": "vue-cli-service build", |
|||
"build:stage": "vue-cli-service build --mode staging", |
|||
"preview": "node build/index.js --preview", |
|||
"lint": "eslint --ext .js,.vue src" |
|||
}, |
|||
"husky": { |
|||
"hooks": { |
|||
"pre-commit": "lint-staged" |
|||
} |
|||
}, |
|||
"lint-staged": { |
|||
"src/**/*.{js,vue}": [ |
|||
"eslint --fix", |
|||
"git add" |
|||
] |
|||
}, |
|||
"keywords": [ |
|||
"vue", |
|||
"admin", |
|||
"dashboard", |
|||
"element-ui", |
|||
"boilerplate", |
|||
"admin-template", |
|||
"management-system" |
|||
], |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://gitee.com/y_project/RuoYi-Vue.git" |
|||
}, |
|||
"dependencies": { |
|||
"@riophae/vue-treeselect": "0.4.0", |
|||
"axios": "0.28.1", |
|||
"clipboard": "2.0.8", |
|||
"core-js": "3.37.1", |
|||
"echarts": "5.4.0", |
|||
"element-ui": "^2.15.14", |
|||
"file-saver": "2.0.5", |
|||
"fuse.js": "6.4.3", |
|||
"highlight.js": "9.18.5", |
|||
"js-beautify": "1.13.0", |
|||
"js-cookie": "3.0.1", |
|||
"jsencrypt": "3.0.0-rc.1", |
|||
"nprogress": "0.2.0", |
|||
"quill": "2.0.2", |
|||
"screenfull": "5.0.2", |
|||
"sortablejs": "1.10.2", |
|||
"splitpanes": "2.4.1", |
|||
"vue": "2.6.12", |
|||
"vue-count-to": "1.0.13", |
|||
"vue-cropper": "0.5.5", |
|||
"vue-meta": "2.4.0", |
|||
"vue-router": "3.4.9", |
|||
"vuedraggable": "2.24.3", |
|||
"vuex": "3.6.0" |
|||
}, |
|||
"devDependencies": { |
|||
"@vue/cli-plugin-babel": "4.4.6", |
|||
"@vue/cli-plugin-eslint": "4.4.6", |
|||
"@vue/cli-service": "4.4.6", |
|||
"babel-eslint": "10.1.0", |
|||
"babel-plugin-dynamic-import-node": "2.3.3", |
|||
"chalk": "4.1.0", |
|||
"compression-webpack-plugin": "6.1.2", |
|||
"connect": "3.6.6", |
|||
"eslint": "7.15.0", |
|||
"eslint-plugin-vue": "7.2.0", |
|||
"lint-staged": "10.5.3", |
|||
"runjs": "4.4.2", |
|||
"sass": "1.32.13", |
|||
"sass-loader": "10.1.1", |
|||
"script-ext-html-webpack-plugin": "2.1.5", |
|||
"svg-sprite-loader": "5.1.1", |
|||
"vue-template-compiler": "2.6.12" |
|||
}, |
|||
"engines": { |
|||
"node": ">=8.9", |
|||
"npm": ">= 3.0.0" |
|||
}, |
|||
"browserslist": [ |
|||
"> 1%", |
|||
"last 2 versions" |
|||
] |
|||
} |
@ -0,0 +1,70 @@ |
|||
<template> |
|||
<div id="app"> |
|||
<router-view /> |
|||
<theme-picker /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import ThemePicker from "@/components/ThemePicker"; |
|||
|
|||
export default { |
|||
name: "App", |
|||
components: { ThemePicker }, |
|||
metaInfo() { |
|||
return { |
|||
title: |
|||
this.$store.state.settings.dynamicTitle && |
|||
this.$store.state.settings.title, |
|||
titleTemplate: (title) => { |
|||
return title |
|||
? `${title} - ${process.env.VUE_APP_TITLE}` |
|||
: process.env.VUE_APP_TITLE; |
|||
}, |
|||
}; |
|||
}, |
|||
}; |
|||
</script> |
|||
<style scoped> |
|||
#app .theme-picker { |
|||
display: none; |
|||
} |
|||
</style> |
|||
<style> |
|||
.wj-uploader .el-icon-upload:before { |
|||
font-size: 100px; |
|||
} |
|||
.wj-uploader .el-upload { |
|||
display: flex; |
|||
align-content: center; |
|||
justify-content: center; |
|||
flex-wrap: wrap; |
|||
} |
|||
.wj-uploader .el-upload__text { |
|||
line-height: 20px; |
|||
width: 100% !important; |
|||
} |
|||
.avatar-uploader { |
|||
height: 180px; |
|||
} |
|||
.avatar-uploader .el-upload { |
|||
border: 1px dashed #d9d9d9 !important; |
|||
border-radius: 6px; |
|||
cursor: pointer; |
|||
position: relative; |
|||
overflow: hidden; |
|||
width: 100% !important; |
|||
height: 180px !important; |
|||
line-height: 150px; |
|||
} |
|||
.el-upload-dragger, |
|||
.el-upload { |
|||
width: 100% !important; |
|||
} |
|||
.el-upload-list__item { |
|||
transition: none !important; |
|||
} |
|||
.el-upload-list__item:first-child { |
|||
margin-top: 0; |
|||
} |
|||
</style> |
@ -0,0 +1,75 @@ |
|||
import request from "@/utils/request"; |
|||
|
|||
// 随访队列
|
|||
export function followupQuery(data) { |
|||
return request({ |
|||
url: "/followup/query", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 新增随访队列
|
|||
export function followupAdd(data) { |
|||
return request({ |
|||
url: "/followup/add", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 新增随访队列
|
|||
export function followupUpd(data) { |
|||
return request({ |
|||
url: "/followup/upd", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 新增随访队列
|
|||
export function followupDel(data) { |
|||
return request({ |
|||
url: "/followup/del", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// ----随访对象----
|
|||
// 查看随访对象
|
|||
export function queryPatient(data) { |
|||
return request({ |
|||
url: "/followup/queryPatient", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 修改随访对象 队列信息
|
|||
export function updPatient(data) { |
|||
return request({ |
|||
url: "/followup/updPatient", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 随访工单
|
|||
export function queryTask(data) { |
|||
return request({ |
|||
url: "/followup/queryTask", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 失访
|
|||
export function updStatus(data) { |
|||
return request({ |
|||
url: "/followup/updStatus", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 患者随访
|
|||
export function followPatient(data) { |
|||
return request({ |
|||
url: "/followup/followPatient", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
@ -0,0 +1,60 @@ |
|||
import request from "@/utils/request"; |
|||
|
|||
// 登录方法
|
|||
export function login(username, password, code, uuid) { |
|||
const data = { |
|||
username, |
|||
password, |
|||
code, |
|||
uuid, |
|||
}; |
|||
return request({ |
|||
url: "/web/login", |
|||
headers: { |
|||
isToken: false, |
|||
repeatSubmit: false, |
|||
}, |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
|
|||
// 注册方法
|
|||
export function register(data) { |
|||
return request({ |
|||
url: "/register", |
|||
headers: { |
|||
isToken: false, |
|||
}, |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
|
|||
// 获取用户详细信息
|
|||
export function getInfo() { |
|||
return request({ |
|||
url: "/getInfo", |
|||
method: "get", |
|||
}); |
|||
} |
|||
|
|||
// 退出方法
|
|||
export function logout() { |
|||
return request({ |
|||
url: "/logout", |
|||
method: "post", |
|||
}); |
|||
} |
|||
|
|||
// 获取验证码
|
|||
export function getCodeImg() { |
|||
return request({ |
|||
url: "/captchaImage", |
|||
headers: { |
|||
isToken: false, |
|||
}, |
|||
method: "get", |
|||
timeout: 20000, |
|||
}); |
|||
} |
@ -0,0 +1,58 @@ |
|||
import request from "@/utils/request"; |
|||
|
|||
// 获取列表
|
|||
export function treatmentQuery(data) { |
|||
return request({ |
|||
url: "/treatment/list", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 添加患者档案
|
|||
export function treatmentAdd(data) { |
|||
return request({ |
|||
url: "/treatment/add", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 修改患者档案
|
|||
export function treatmentUpd(data) { |
|||
return request({ |
|||
url: "/treatment/upd", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 删除患者档案
|
|||
export function treatmentDel(data) { |
|||
return request({ |
|||
url: "/treatment/del", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 档案详情
|
|||
export function queryRecord(data) { |
|||
return request({ |
|||
url: "/treatment/queryRecord", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
}// 档案详情
|
|||
export function saveAidRecord(data) { |
|||
return request({ |
|||
url: "/treatment/saveAidRecord", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 诊疗档案绑定随访队列
|
|||
|
|||
export function queueAdd(data) { |
|||
return request({ |
|||
url: "/patientQueueRelation/add", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
@ -0,0 +1,34 @@ |
|||
import request from "@/utils/request"; |
|||
|
|||
// 获取列表
|
|||
export function queryPatient(data) { |
|||
return request({ |
|||
url: "/patient/list", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 添加患者档案
|
|||
export function patientAdd(data) { |
|||
return request({ |
|||
url: "/patient/add", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 修改患者档案
|
|||
export function patientUpd(data) { |
|||
return request({ |
|||
url: "/patient/upd", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
|||
// 删除患者档案
|
|||
export function patientDel(data) { |
|||
return request({ |
|||
url: "/patient/del", |
|||
method: "post", |
|||
data: data, |
|||
}); |
|||
} |
@ -0,0 +1,136 @@ |
|||
import request from '@/utils/request' |
|||
import { parseStrEmpty } from "@/utils/ruoyi"; |
|||
|
|||
// 查询用户列表
|
|||
export function listUser(query) { |
|||
return request({ |
|||
url: '/system/user/list', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
|||
|
|||
// 查询用户详细
|
|||
export function getUser(userId) { |
|||
return request({ |
|||
url: '/system/user/' + parseStrEmpty(userId), |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// 新增用户
|
|||
export function addUser(data) { |
|||
return request({ |
|||
url: '/system/user', |
|||
method: 'post', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 修改用户
|
|||
export function updateUser(data) { |
|||
return request({ |
|||
url: '/system/user', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 删除用户
|
|||
export function delUser(userId) { |
|||
return request({ |
|||
url: '/system/user/' + userId, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
|
|||
// 用户密码重置
|
|||
export function resetUserPwd(userId, password) { |
|||
const data = { |
|||
userId, |
|||
password |
|||
} |
|||
return request({ |
|||
url: '/system/user/resetPwd', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 用户状态修改
|
|||
export function changeUserStatus(userId, status) { |
|||
const data = { |
|||
userId, |
|||
status |
|||
} |
|||
return request({ |
|||
url: '/system/user/changeStatus', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 查询用户个人信息
|
|||
export function getUserProfile() { |
|||
return request({ |
|||
url: '/system/user/profile', |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// 修改用户个人信息
|
|||
export function updateUserProfile(data) { |
|||
return request({ |
|||
url: '/system/user/profile', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 用户密码重置
|
|||
export function updateUserPwd(oldPassword, newPassword) { |
|||
const data = { |
|||
oldPassword, |
|||
newPassword |
|||
} |
|||
return request({ |
|||
url: '/system/user/profile/updatePwd', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 用户头像上传
|
|||
export function uploadAvatar(data) { |
|||
return request({ |
|||
url: '/system/user/profile/avatar', |
|||
method: 'post', |
|||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 查询授权角色
|
|||
export function getAuthRole(userId) { |
|||
return request({ |
|||
url: '/system/user/authRole/' + userId, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// 保存授权角色
|
|||
export function updateAuthRole(data) { |
|||
return request({ |
|||
url: '/system/user/authRole', |
|||
method: 'put', |
|||
params: data |
|||
}) |
|||
} |
|||
|
|||
// 查询部门下拉树结构
|
|||
export function deptTreeSelect() { |
|||
return request({ |
|||
url: '/system/user/deptTree', |
|||
method: 'get' |
|||
}) |
|||
} |
@ -0,0 +1,135 @@ |
|||
.popup { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
.popup >>> .el-dialog { |
|||
max-height: 86vh; |
|||
/* margin-top: 0 !important; */ |
|||
overflow: auto; |
|||
border-radius: 6px; |
|||
} |
|||
>>>.el-dialog__header{ |
|||
padding-bottom: 20px; |
|||
} |
|||
.popup >>> .el-dialog::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
.popup >>> .el-dialog::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
.popup >>> .el-dialog:not(.is-fullscreen) { |
|||
margin-top: 0 !important; |
|||
} |
|||
.popup >>> .popupAdd { |
|||
height: 58px; |
|||
font-size: 14px; |
|||
line-height: 58px; |
|||
display: flex; |
|||
margin-bottom: 20px; |
|||
margin-top: 20px; |
|||
align-items: center; |
|||
} |
|||
.popup >>> .popupAdd2 { |
|||
padding-left: 1px; |
|||
|
|||
/* background-image: url(../images/addlog.png); */ |
|||
background-repeat: no-repeat; |
|||
} |
|||
.popup >>> .popupAdd3 { |
|||
margin-top: 20px; |
|||
padding-left: 1px; |
|||
/* background-image: url(../images/tj.png); */ |
|||
} |
|||
.popup .popupbox { |
|||
width: 650px; |
|||
height: 600px; |
|||
/* background-image: url(../images/solid.png); */ |
|||
} |
|||
/* .popup >>> .popupAdd .popupleft { |
|||
font-size: 14px; |
|||
position: relative; |
|||
z-index: 2; |
|||
width: 68px; |
|||
height: 62px; |
|||
line-height: 62px; |
|||
text-align: center; |
|||
background-image: url(../images/addlog.png); |
|||
background-size: 100% 100%; |
|||
} */ |
|||
.formStep >>> .el-icon-delete { |
|||
font-size: 20px; |
|||
} |
|||
.popup >>> .popupAdd .popupRight { |
|||
width: 100%; |
|||
margin-left: -20px; |
|||
height: 50px; |
|||
margin-top: 1px; |
|||
padding-left: 30px; |
|||
line-height: 50px; |
|||
font-size: 18px; |
|||
} |
|||
.inner::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
>>>.el-dialog__body{ |
|||
padding: 0px 20px 0px 20px; |
|||
} |
|||
/* .popup >>> .popupAdd2 .popupleft { |
|||
width: 55px; |
|||
} */ |
|||
.formStep .el-button { |
|||
width: 68px; |
|||
height: 32px; |
|||
line-height: 32px; |
|||
background-size: 100% 100%; |
|||
border: none; |
|||
padding: 0; |
|||
opacity: 0.87; |
|||
} |
|||
.popup .el-button:hover { |
|||
opacity: 1; |
|||
} |
|||
.popup .popuButbox > div { |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
|
|||
/* .formStep >>> .el-checkbox__input.is-checked .el-checkbox__inner { |
|||
background-color: #67defc; |
|||
border-color: #67defc; |
|||
} |
|||
.formStep >>> .is-checked .el-checkbox__label { |
|||
color: #67defc; |
|||
} */ |
|||
.formStep .formStep_StepBox { |
|||
display: flex; |
|||
} |
|||
.formStep .StepBox { |
|||
width: 620px; |
|||
} |
|||
|
|||
.formStep >>> .el-input__inner, |
|||
.formStep >>> .el-textarea__inner { |
|||
/* color: #fff; */ |
|||
background: rgba(0, 0, 0, 0); |
|||
} |
|||
.formStep >>> .el-textarea__inner { |
|||
/* color: rgb(191, 203, 217); */ |
|||
} |
|||
.formStep >>> .vue-treeselect__single-value { |
|||
color: rgb(191, 203, 217); |
|||
} |
|||
.formStep >>> .el-input__inner::input-placeholder { |
|||
color: #a8c9f0; |
|||
} |
|||
.formStep >>> .el-input--medium .el-input__inner, |
|||
.formStep >>> .el-textarea__inner, |
|||
.formStep >>> .el-select, |
|||
.formStep >>> .el-input-number--medium, |
|||
.formStep >>> .el-date-editor.el-input, |
|||
.formStep >>> .el-cascader--medium, |
|||
.formStep >>> .el-autocomplete { |
|||
width: 100% !important; |
|||
text-align: left; |
|||
} |
@ -0,0 +1,296 @@ |
|||
/** |
|||
* 通用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-message-box__status + .el-message-box__message{ |
|||
word-break: break-word; |
|||
} |
|||
|
|||
.el-dialog:not(.is-fullscreen) { |
|||
margin-top: 6vh !important; |
|||
} |
|||
|
|||
.el-dialog__wrapper.scrollbar .el-dialog .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; |
|||
color: #515a6e; |
|||
height: 40px; |
|||
font-size: 13px; |
|||
} |
|||
} |
|||
|
|||
.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: 32px; |
|||
margin-bottom: 10px; |
|||
margin-top: 15px; |
|||
padding: 10px 20px !important; |
|||
} |
|||
|
|||
/* tree border */ |
|||
.tree-border { |
|||
margin-top: 5px; |
|||
border: 1px solid #e5e6e7; |
|||
background: #FFFFFF none; |
|||
border-radius: 4px; |
|||
} |
|||
|
|||
.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--mini { |
|||
padding-left: 0; |
|||
padding-right: 0; |
|||
width: inherit; |
|||
} |
|||
|
|||
/** 表格更多操作下拉样式 */ |
|||
.el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine { |
|||
cursor: pointer; |
|||
margin-left: 5px; |
|||
} |
|||
|
|||
.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; |
|||
min-height: 40px; |
|||
} |
|||
|
|||
.el-card__body { |
|||
padding: 15px 20px 20px 20px; |
|||
} |
|||
|
|||
.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: relative; |
|||
top: 50%; |
|||
left: 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 { |
|||
position: relative; |
|||
float: right; |
|||
} |
|||
|
|||
/* 分割面板样式 */ |
|||
.splitpanes.default-theme .splitpanes__pane { |
|||
background-color: #fff!important; |
|||
} |
@ -0,0 +1,216 @@ |
|||
<template> |
|||
<div :class="{ show: show }" class="header-search"> |
|||
<svg-icon |
|||
class-name="search-icon" |
|||
icon-class="search" |
|||
@click.stop="click" |
|||
/> |
|||
<el-select |
|||
ref="headerSearchSelect" |
|||
v-model="search" |
|||
:remote-method="querySearch" |
|||
filterable |
|||
default-first-option |
|||
remote |
|||
placeholder="Search" |
|||
class="header-search-select" |
|||
@change="change" |
|||
> |
|||
<el-option |
|||
v-for="option in options" |
|||
:key="option.item.path" |
|||
:value="option.item" |
|||
:label="option.item.title.join(' > ')" |
|||
/> |
|||
</el-select> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
// fuse is a lightweight fuzzy-search module |
|||
// make search results more in line with expectations |
|||
import Fuse from "fuse.js/dist/fuse.min.js"; |
|||
import path from "path"; |
|||
import { isHttp } from "@/utils/validate"; |
|||
|
|||
export default { |
|||
name: "HeaderSearch", |
|||
data() { |
|||
return { |
|||
search: "", |
|||
options: [], |
|||
searchPool: [], |
|||
show: false, |
|||
fuse: undefined, |
|||
}; |
|||
}, |
|||
computed: { |
|||
routes() { |
|||
return this.$store.getters.permission_routes; |
|||
}, |
|||
}, |
|||
watch: { |
|||
routes() { |
|||
this.searchPool = this.generateRoutes(this.routes); |
|||
}, |
|||
searchPool(list) { |
|||
this.initFuse(list); |
|||
}, |
|||
show(value) { |
|||
if (value) { |
|||
document.body.addEventListener("click", this.close); |
|||
} else { |
|||
document.body.removeEventListener("click", this.close); |
|||
} |
|||
}, |
|||
}, |
|||
mounted() { |
|||
this.searchPool = this.generateRoutes(this.routes); |
|||
}, |
|||
methods: { |
|||
click() { |
|||
this.show = !this.show; |
|||
if (this.show) { |
|||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus(); |
|||
} |
|||
}, |
|||
close() { |
|||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur(); |
|||
this.options = []; |
|||
this.show = false; |
|||
}, |
|||
change(val) { |
|||
const path = val.path; |
|||
const query = val.query; |
|||
if (isHttp(val.path)) { |
|||
// http(s):// 路径新窗口打开 |
|||
const pindex = path.indexOf("http"); |
|||
window.open(path.substr(pindex, path.length), "_blank"); |
|||
} else { |
|||
if (query) { |
|||
this.$router.push({ path: path, query: JSON.parse(query) }); |
|||
} else { |
|||
this.$router.push(path); |
|||
} |
|||
} |
|||
this.search = ""; |
|||
this.options = []; |
|||
this.$nextTick(() => { |
|||
this.show = false; |
|||
}); |
|||
}, |
|||
initFuse(list) { |
|||
this.fuse = new Fuse(list, { |
|||
shouldSort: true, |
|||
threshold: 0.4, |
|||
location: 0, |
|||
distance: 100, |
|||
minMatchCharLength: 1, |
|||
keys: [ |
|||
{ |
|||
name: "title", |
|||
weight: 0.7, |
|||
}, |
|||
{ |
|||
name: "path", |
|||
weight: 0.3, |
|||
}, |
|||
], |
|||
}); |
|||
}, |
|||
// Filter out the routes that can be displayed in the sidebar |
|||
// And generate the internationalized title |
|||
generateRoutes(routes, basePath = "/", prefixTitle = []) { |
|||
let res = []; |
|||
|
|||
for (const router of routes) { |
|||
// skip hidden router |
|||
if (router.hidden) { |
|||
continue; |
|||
} |
|||
|
|||
const data = { |
|||
path: !isHttp(router.path) |
|||
? path.resolve(basePath, router.path) |
|||
: router.path, |
|||
title: [...prefixTitle], |
|||
}; |
|||
|
|||
if (router.meta && router.meta.title) { |
|||
data.title = [...data.title, router.meta.title]; |
|||
|
|||
if (router.redirect !== "noRedirect") { |
|||
// only push the routes with title |
|||
// special case: need to exclude parent router without redirect |
|||
res.push(data); |
|||
} |
|||
} |
|||
|
|||
if (router.query) { |
|||
data.query = router.query; |
|||
} |
|||
|
|||
// recursive child routes |
|||
if (router.children) { |
|||
const tempRoutes = this.generateRoutes( |
|||
router.children, |
|||
data.path, |
|||
data.title |
|||
); |
|||
if (tempRoutes.length >= 1) { |
|||
res = [...res, ...tempRoutes]; |
|||
} |
|||
} |
|||
} |
|||
return res; |
|||
}, |
|||
querySearch(query) { |
|||
if (query !== "") { |
|||
this.options = this.fuse.search(query); |
|||
} else { |
|||
this.options = []; |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.header-search { |
|||
font-size: 0 !important; |
|||
|
|||
.search-icon { |
|||
cursor: pointer; |
|||
font-size: 18px; |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
.header-search-select { |
|||
font-size: 18px; |
|||
transition: width 0.2s; |
|||
width: 0; |
|||
overflow: hidden; |
|||
background: transparent; |
|||
border-radius: 0; |
|||
display: inline-block; |
|||
vertical-align: middle; |
|||
|
|||
::v-deep .el-input__inner { |
|||
border-radius: 0; |
|||
border: 0; |
|||
padding-left: 0; |
|||
padding-right: 0; |
|||
box-shadow: none !important; |
|||
border-bottom: 1px solid #d9d9d9; |
|||
vertical-align: middle; |
|||
} |
|||
} |
|||
|
|||
&.show { |
|||
.header-search-select { |
|||
width: 210px; |
|||
margin-left: 10px; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,21 @@ |
|||
<template> |
|||
<div> |
|||
<svg-icon icon-class="question" @click="goto" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'RuoYiDoc', |
|||
data() { |
|||
return { |
|||
url: 'http://doc.ruoyi.vip/ruoyi-vue' |
|||
} |
|||
}, |
|||
methods: { |
|||
goto() { |
|||
window.open(this.url) |
|||
} |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,21 @@ |
|||
<template> |
|||
<div> |
|||
<svg-icon icon-class="github" @click="goto" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'RuoYiGit', |
|||
data() { |
|||
return { |
|||
url: 'https://gitee.com/y_project/RuoYi-Vue' |
|||
} |
|||
}, |
|||
methods: { |
|||
goto() { |
|||
window.open(this.url) |
|||
} |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,64 @@ |
|||
/** |
|||
* v-dialogDrag 弹窗拖拽 |
|||
* Copyright (c) 2019 ruoyi |
|||
*/ |
|||
|
|||
export default { |
|||
bind(el, binding, vnode, oldVnode) { |
|||
const value = binding.value |
|||
if (value == false) return |
|||
// 获取拖拽内容头部
|
|||
const dialogHeaderEl = el.querySelector('.el-dialog__header'); |
|||
const dragDom = el.querySelector('.el-dialog'); |
|||
dialogHeaderEl.style.cursor = 'move'; |
|||
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
|||
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null); |
|||
dragDom.style.position = 'absolute'; |
|||
dragDom.style.marginTop = 0; |
|||
let width = dragDom.style.width; |
|||
if (width.includes('%')) { |
|||
width = +document.body.clientWidth * (+width.replace(/\%/g, '') / 100); |
|||
} else { |
|||
width = +width.replace(/\px/g, ''); |
|||
} |
|||
dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`; |
|||
// 鼠标按下事件
|
|||
dialogHeaderEl.onmousedown = (e) => { |
|||
// 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)
|
|||
const disX = e.clientX - dialogHeaderEl.offsetLeft; |
|||
const disY = e.clientY - dialogHeaderEl.offsetTop; |
|||
|
|||
// 获取到的值带px 正则匹配替换
|
|||
let styL, styT; |
|||
|
|||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
|||
if (sty.left.includes('%')) { |
|||
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100); |
|||
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100); |
|||
} else { |
|||
styL = +sty.left.replace(/\px/g, ''); |
|||
styT = +sty.top.replace(/\px/g, ''); |
|||
}; |
|||
|
|||
// 鼠标拖拽事件
|
|||
document.onmousemove = function (e) { |
|||
// 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离)
|
|||
const l = e.clientX - disX; |
|||
const t = e.clientY - disY; |
|||
|
|||
let finallyL = l + styL |
|||
let finallyT = t + styT |
|||
|
|||
// 移动当前元素
|
|||
dragDom.style.left = `${finallyL}px`; |
|||
dragDom.style.top = `${finallyT}px`; |
|||
|
|||
}; |
|||
|
|||
document.onmouseup = function (e) { |
|||
document.onmousemove = null; |
|||
document.onmouseup = null; |
|||
}; |
|||
} |
|||
} |
|||
}; |
@ -0,0 +1,34 @@ |
|||
/** |
|||
* v-dialogDragWidth 可拖动弹窗高度(右下角) |
|||
* Copyright (c) 2019 ruoyi |
|||
*/ |
|||
|
|||
export default { |
|||
bind(el) { |
|||
const dragDom = el.querySelector('.el-dialog'); |
|||
const lineEl = document.createElement('div'); |
|||
lineEl.style = 'width: 6px; background: inherit; height: 10px; position: absolute; right: 0; bottom: 0; margin: auto; z-index: 1; cursor: nwse-resize;'; |
|||
lineEl.addEventListener('mousedown', |
|||
function(e) { |
|||
// 鼠标按下,计算当前元素距离可视区的距离
|
|||
const disX = e.clientX - el.offsetLeft; |
|||
const disY = e.clientY - el.offsetTop; |
|||
// 当前宽度 高度
|
|||
const curWidth = dragDom.offsetWidth; |
|||
const curHeight = dragDom.offsetHeight; |
|||
document.onmousemove = function(e) { |
|||
e.preventDefault(); // 移动时禁用默认事件
|
|||
// 通过事件委托,计算移动的距离
|
|||
const xl = e.clientX - disX; |
|||
const yl = e.clientY - disY |
|||
dragDom.style.width = `${curWidth + xl}px`; |
|||
dragDom.style.height = `${curHeight + yl}px`; |
|||
}; |
|||
document.onmouseup = function(e) { |
|||
document.onmousemove = null; |
|||
document.onmouseup = null; |
|||
}; |
|||
}, false); |
|||
dragDom.appendChild(lineEl); |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
/** |
|||
* v-dialogDragWidth 可拖动弹窗宽度(右侧边) |
|||
* Copyright (c) 2019 ruoyi |
|||
*/ |
|||
|
|||
export default { |
|||
bind(el) { |
|||
const dragDom = el.querySelector('.el-dialog'); |
|||
const lineEl = document.createElement('div'); |
|||
lineEl.style = 'width: 5px; background: inherit; height: 80%; position: absolute; right: 0; top: 0; bottom: 0; margin: auto; z-index: 1; cursor: w-resize;'; |
|||
lineEl.addEventListener('mousedown', |
|||
function (e) { |
|||
// 鼠标按下,计算当前元素距离可视区的距离
|
|||
const disX = e.clientX - el.offsetLeft; |
|||
// 当前宽度
|
|||
const curWidth = dragDom.offsetWidth; |
|||
document.onmousemove = function (e) { |
|||
e.preventDefault(); // 移动时禁用默认事件
|
|||
// 通过事件委托,计算移动的距离
|
|||
const l = e.clientX - disX; |
|||
dragDom.style.width = `${curWidth + l}px`; |
|||
}; |
|||
document.onmouseup = function (e) { |
|||
document.onmousemove = null; |
|||
document.onmouseup = null; |
|||
}; |
|||
}, false); |
|||
dragDom.appendChild(lineEl); |
|||
} |
|||
} |
@ -0,0 +1,54 @@ |
|||
/** |
|||
* v-clipboard 文字复制剪贴 |
|||
* Copyright (c) 2021 ruoyi |
|||
*/ |
|||
|
|||
import Clipboard from 'clipboard' |
|||
export default { |
|||
bind(el, binding, vnode) { |
|||
switch (binding.arg) { |
|||
case 'success': |
|||
el._vClipBoard_success = binding.value; |
|||
break; |
|||
case 'error': |
|||
el._vClipBoard_error = binding.value; |
|||
break; |
|||
default: { |
|||
const clipboard = new Clipboard(el, { |
|||
text: () => binding.value, |
|||
action: () => binding.arg === 'cut' ? 'cut' : 'copy' |
|||
}); |
|||
clipboard.on('success', e => { |
|||
const callback = el._vClipBoard_success; |
|||
callback && callback(e); |
|||
}); |
|||
clipboard.on('error', e => { |
|||
const callback = el._vClipBoard_error; |
|||
callback && callback(e); |
|||
}); |
|||
el._vClipBoard = clipboard; |
|||
} |
|||
} |
|||
}, |
|||
update(el, binding) { |
|||
if (binding.arg === 'success') { |
|||
el._vClipBoard_success = binding.value; |
|||
} else if (binding.arg === 'error') { |
|||
el._vClipBoard_error = binding.value; |
|||
} else { |
|||
el._vClipBoard.text = function () { return binding.value; }; |
|||
el._vClipBoard.action = () => binding.arg === 'cut' ? 'cut' : 'copy'; |
|||
} |
|||
}, |
|||
unbind(el, binding) { |
|||
if (!el._vClipboard) return |
|||
if (binding.arg === 'success') { |
|||
delete el._vClipBoard_success; |
|||
} else if (binding.arg === 'error') { |
|||
delete el._vClipBoard_error; |
|||
} else { |
|||
el._vClipBoard.destroy(); |
|||
delete el._vClipBoard; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
/** |
|||
* v-hasPermi 操作权限处理 |
|||
* Copyright (c) 2019 ruoyi |
|||
*/ |
|||
|
|||
import store from '@/store' |
|||
|
|||
export default { |
|||
inserted(el, binding, vnode) { |
|||
const { value } = binding |
|||
const all_permission = "*:*:*"; |
|||
const permissions = store.getters && store.getters.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(`请设置操作权限标签值`) |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
/** |
|||
* v-hasRole 角色权限处理 |
|||
* Copyright (c) 2019 ruoyi |
|||
*/ |
|||
|
|||
import store from '@/store' |
|||
|
|||
export default { |
|||
inserted(el, binding, vnode) { |
|||
const { value } = binding |
|||
const super_admin = "admin"; |
|||
const roles = store.getters && store.getters.roles |
|||
|
|||
if (value && value instanceof Array && value.length > 0) { |
|||
const roleFlag = value |
|||
|
|||
const hasRole = roles.some(role => { |
|||
return super_admin === role || roleFlag.includes(role) |
|||
}) |
|||
|
|||
if (!hasRole) { |
|||
el.parentNode && el.parentNode.removeChild(el) |
|||
} |
|||
} else { |
|||
throw new Error(`请设置角色权限标签值"`) |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,209 @@ |
|||
<template> |
|||
<div class="navbar"> |
|||
<hamburger |
|||
id="hamburger-container" |
|||
:is-active="sidebar.opened" |
|||
class="hamburger-container" |
|||
@toggleClick="toggleSideBar" |
|||
/> |
|||
|
|||
<breadcrumb |
|||
id="breadcrumb-container" |
|||
class="breadcrumb-container" |
|||
v-if="!topNav" |
|||
/> |
|||
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav" /> |
|||
|
|||
<div class="right-menu"> |
|||
<template v-if="device !== 'mobile'"> |
|||
<search id="header-search" class="right-menu-item" /> |
|||
|
|||
<el-tooltip content="源码地址" effect="dark" placement="bottom"> |
|||
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> |
|||
</el-tooltip> |
|||
|
|||
<el-tooltip content="文档地址" effect="dark" placement="bottom"> |
|||
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" /> |
|||
</el-tooltip> |
|||
|
|||
<screenfull id="screenfull" class="right-menu-item hover-effect" /> |
|||
|
|||
<el-tooltip content="布局大小" effect="dark" placement="bottom"> |
|||
<size-select id="size-select" class="right-menu-item hover-effect" /> |
|||
</el-tooltip> |
|||
</template> |
|||
|
|||
<el-dropdown |
|||
class="avatar-container right-menu-item hover-effect" |
|||
trigger="click" |
|||
> |
|||
<div class="avatar-wrapper"> |
|||
<img :src="avatar" class="user-avatar" /> |
|||
<i class="el-icon-caret-bottom" /> |
|||
</div> |
|||
<el-dropdown-menu slot="dropdown"> |
|||
<router-link to="/user/profile"> |
|||
<el-dropdown-item>个人中心</el-dropdown-item> |
|||
</router-link> |
|||
<el-dropdown-item @click.native="setting = true"> |
|||
<span>布局设置</span> |
|||
</el-dropdown-item> |
|||
<el-dropdown-item divided @click.native="logout"> |
|||
<span>退出登录</span> |
|||
</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</el-dropdown> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapGetters } from "vuex"; |
|||
import Breadcrumb from "@/components/Breadcrumb"; |
|||
import TopNav from "@/components/TopNav"; |
|||
import Hamburger from "@/components/Hamburger"; |
|||
import Screenfull from "@/components/Screenfull"; |
|||
import SizeSelect from "@/components/SizeSelect"; |
|||
import Search from "@/components/HeaderSearch"; |
|||
import RuoYiGit from "@/components/RuoYi/Git"; |
|||
import RuoYiDoc from "@/components/RuoYi/Doc"; |
|||
|
|||
export default { |
|||
components: { |
|||
Breadcrumb, |
|||
TopNav, |
|||
Hamburger, |
|||
Screenfull, |
|||
SizeSelect, |
|||
Search, |
|||
RuoYiGit, |
|||
RuoYiDoc, |
|||
}, |
|||
computed: { |
|||
...mapGetters(["sidebar", "avatar", "device"]), |
|||
setting: { |
|||
get() { |
|||
return this.$store.state.settings.showSettings; |
|||
}, |
|||
set(val) { |
|||
this.$store.dispatch("settings/changeSetting", { |
|||
key: "showSettings", |
|||
value: val, |
|||
}); |
|||
}, |
|||
}, |
|||
topNav: { |
|||
get() { |
|||
return this.$store.state.settings.topNav; |
|||
}, |
|||
}, |
|||
}, |
|||
methods: { |
|||
toggleSideBar() { |
|||
this.$store.dispatch("app/toggleSideBar"); |
|||
}, |
|||
async logout() { |
|||
this.$confirm("确定注销并退出系统吗?", "提示", { |
|||
confirmButtonText: "确定", |
|||
cancelButtonText: "取消", |
|||
type: "warning", |
|||
}) |
|||
.then(() => { |
|||
this.$store.dispatch("LogOut").then(() => { |
|||
location.href = process.env.VUE_APP_PUBLIC_PATH; |
|||
}); |
|||
}) |
|||
.catch(() => {}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.navbar { |
|||
height: 50px; |
|||
overflow: hidden; |
|||
position: relative; |
|||
background: #fff; |
|||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); |
|||
|
|||
.hamburger-container { |
|||
line-height: 46px; |
|||
height: 100%; |
|||
float: left; |
|||
cursor: pointer; |
|||
transition: background 0.3s; |
|||
-webkit-tap-highlight-color: transparent; |
|||
|
|||
&:hover { |
|||
background: rgba(0, 0, 0, 0.025); |
|||
} |
|||
} |
|||
|
|||
.breadcrumb-container { |
|||
float: left; |
|||
} |
|||
|
|||
.topmenu-container { |
|||
position: absolute; |
|||
left: 50px; |
|||
} |
|||
|
|||
.errLog-container { |
|||
display: inline-block; |
|||
vertical-align: top; |
|||
} |
|||
|
|||
.right-menu { |
|||
float: right; |
|||
height: 100%; |
|||
line-height: 50px; |
|||
|
|||
&:focus { |
|||
outline: none; |
|||
} |
|||
|
|||
.right-menu-item { |
|||
display: inline-block; |
|||
padding: 0 8px; |
|||
height: 100%; |
|||
font-size: 18px; |
|||
color: #5a5e66; |
|||
vertical-align: text-bottom; |
|||
|
|||
&.hover-effect { |
|||
cursor: pointer; |
|||
transition: background 0.3s; |
|||
|
|||
&:hover { |
|||
background: rgba(0, 0, 0, 0.025); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.avatar-container { |
|||
margin-right: 30px; |
|||
|
|||
.avatar-wrapper { |
|||
margin-top: 5px; |
|||
position: relative; |
|||
|
|||
.user-avatar { |
|||
cursor: pointer; |
|||
width: 40px; |
|||
height: 40px; |
|||
border-radius: 10px; |
|||
} |
|||
|
|||
.el-icon-caret-bottom { |
|||
cursor: pointer; |
|||
position: absolute; |
|||
right: -20px; |
|||
top: 25px; |
|||
font-size: 12px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,86 @@ |
|||
import Vue from 'vue' |
|||
|
|||
import Cookies from 'js-cookie' |
|||
|
|||
import Element from 'element-ui' |
|||
import './assets/styles/element-variables.scss' |
|||
|
|||
import '@/assets/styles/index.scss' // global css
|
|||
import '@/assets/styles/ruoyi.scss' // ruoyi css
|
|||
import App from './App' |
|||
import store from './store' |
|||
import router from './router' |
|||
import directive from './directive' // directive
|
|||
import plugins from './plugins' // plugins
|
|||
import { download } from '@/utils/request' |
|||
|
|||
import './assets/icons' // icon
|
|||
import './permission' // permission control
|
|||
import { getDicts } from "@/api/system/dict/data"; |
|||
import { getConfigKey } from "@/api/system/config"; |
|||
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"; |
|||
// 分页组件
|
|||
import Pagination from "@/components/Pagination"; |
|||
// 自定义表格工具组件
|
|||
import RightToolbar from "@/components/RightToolbar" |
|||
// 富文本组件
|
|||
import Editor from "@/components/Editor" |
|||
// 文件上传组件
|
|||
import FileUpload from "@/components/FileUpload" |
|||
// 图片上传组件
|
|||
import ImageUpload from "@/components/ImageUpload" |
|||
// 图片预览组件
|
|||
import ImagePreview from "@/components/ImagePreview" |
|||
// 字典标签组件
|
|||
import DictTag from '@/components/DictTag' |
|||
// 头部标签组件
|
|||
import VueMeta from 'vue-meta' |
|||
// 字典数据组件
|
|||
import DictData from '@/components/DictData' |
|||
|
|||
// 全局方法挂载
|
|||
Vue.prototype.getDicts = getDicts |
|||
Vue.prototype.getConfigKey = getConfigKey |
|||
Vue.prototype.parseTime = parseTime |
|||
Vue.prototype.resetForm = resetForm |
|||
Vue.prototype.addDateRange = addDateRange |
|||
Vue.prototype.selectDictLabel = selectDictLabel |
|||
Vue.prototype.selectDictLabels = selectDictLabels |
|||
Vue.prototype.download = download |
|||
Vue.prototype.handleTree = handleTree |
|||
|
|||
// 全局组件挂载
|
|||
Vue.component('DictTag', DictTag) |
|||
Vue.component('Pagination', Pagination) |
|||
Vue.component('RightToolbar', RightToolbar) |
|||
Vue.component('Editor', Editor) |
|||
Vue.component('FileUpload', FileUpload) |
|||
Vue.component('ImageUpload', ImageUpload) |
|||
Vue.component('ImagePreview', ImagePreview) |
|||
|
|||
Vue.use(directive) |
|||
Vue.use(plugins) |
|||
Vue.use(VueMeta) |
|||
DictData.install() |
|||
|
|||
/** |
|||
* If you don't want to use mock-server |
|||
* you want to use MockJs for mock api |
|||
* you can execute: mockXHR() |
|||
* |
|||
* Currently MockJs will be used in the production environment, |
|||
* please remove it before going online! ! ! |
|||
*/ |
|||
|
|||
Vue.use(Element, { |
|||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
|||
}) |
|||
|
|||
Vue.config.productionTip = false |
|||
|
|||
new Vue({ |
|||
el: '#app', |
|||
router, |
|||
store, |
|||
render: h => h(App) |
|||
}) |
@ -0,0 +1,79 @@ |
|||
import axios from 'axios' |
|||
import {Loading, Message} from 'element-ui' |
|||
import { saveAs } from 'file-saver' |
|||
import { getToken } from '@/utils/auth' |
|||
import errorCode from '@/utils/errorCode' |
|||
import { blobValidate } from "@/utils/ruoyi"; |
|||
|
|||
const baseURL = process.env.VUE_APP_BASE_API |
|||
let downloadLoadingInstance; |
|||
|
|||
export default { |
|||
name(name, isDelete = true) { |
|||
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete |
|||
axios({ |
|||
method: 'get', |
|||
url: url, |
|||
responseType: 'blob', |
|||
headers: { 'Authorization': 'Bearer ' + getToken() } |
|||
}).then((res) => { |
|||
const isBlob = blobValidate(res.data); |
|||
if (isBlob) { |
|||
const blob = new Blob([res.data]) |
|||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) |
|||
} else { |
|||
this.printErrMsg(res.data); |
|||
} |
|||
}) |
|||
}, |
|||
resource(resource) { |
|||
var url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource); |
|||
axios({ |
|||
method: 'get', |
|||
url: url, |
|||
responseType: 'blob', |
|||
headers: { 'Authorization': 'Bearer ' + getToken() } |
|||
}).then((res) => { |
|||
const isBlob = blobValidate(res.data); |
|||
if (isBlob) { |
|||
const blob = new Blob([res.data]) |
|||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) |
|||
} else { |
|||
this.printErrMsg(res.data); |
|||
} |
|||
}) |
|||
}, |
|||
zip(url, name) { |
|||
var url = baseURL + url |
|||
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) |
|||
axios({ |
|||
method: 'get', |
|||
url: url, |
|||
responseType: 'blob', |
|||
headers: { 'Authorization': 'Bearer ' + getToken() } |
|||
}).then((res) => { |
|||
const isBlob = blobValidate(res.data); |
|||
if (isBlob) { |
|||
const blob = new Blob([res.data], { type: 'application/zip' }) |
|||
this.saveAs(blob, name) |
|||
} else { |
|||
this.printErrMsg(res.data); |
|||
} |
|||
downloadLoadingInstance.close(); |
|||
}).catch((r) => { |
|||
console.error(r) |
|||
Message.error('下载文件出现错误,请联系管理员!') |
|||
downloadLoadingInstance.close(); |
|||
}) |
|||
}, |
|||
saveAs(text, name, opts) { |
|||
saveAs(text, name, opts); |
|||
}, |
|||
async printErrMsg(data) { |
|||
const resText = await data.text(); |
|||
const rspObj = JSON.parse(resText); |
|||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] |
|||
Message.error(errMsg); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,236 @@ |
|||
import Vue from "vue"; |
|||
import Router from "vue-router"; |
|||
|
|||
Vue.use(Router); |
|||
|
|||
/* Layout */ |
|||
import Layout from "@/layout"; |
|||
|
|||
/** |
|||
* Note: 路由配置项 |
|||
* |
|||
* hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
|
|||
* alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
|
|||
* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
|
|||
* // 若你想不管路由下面的 children 声明的个数都显示你的根路由
|
|||
* // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
|
|||
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
|
|||
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
|
|||
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
|
|||
* roles: ['admin', 'common'] // 访问路由的角色权限
|
|||
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
|
|||
* meta : { |
|||
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
|
|||
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
|
|||
icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg
|
|||
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
|
|||
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
|
|||
} |
|||
*/ |
|||
|
|||
// 公共路由
|
|||
export const constantRoutes = [ |
|||
{ |
|||
path: "/redirect", |
|||
component: Layout, |
|||
hidden: true, |
|||
children: [ |
|||
{ |
|||
path: "/redirect/:path(.*)", |
|||
component: () => import("@/views/redirect"), |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/login", |
|||
component: () => import("@/views/login"), |
|||
hidden: true, |
|||
}, |
|||
{ |
|||
path: "/register", |
|||
component: () => import("@/views/register"), |
|||
hidden: true, |
|||
}, |
|||
{ |
|||
path: "/404", |
|||
component: () => import("@/views/error/404"), |
|||
hidden: true, |
|||
}, |
|||
{ |
|||
path: "/401", |
|||
component: () => import("@/views/error/401"), |
|||
hidden: true, |
|||
}, |
|||
{ |
|||
path: "", |
|||
component: Layout, |
|||
redirect: "index", |
|||
children: [ |
|||
{ |
|||
path: "index", |
|||
component: () => import("@/views/index"), |
|||
name: "Index", |
|||
meta: { title: "首页", icon: "dashboard", affix: true }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/patientFile", |
|||
component: Layout, |
|||
redirect: "index", |
|||
children: [ |
|||
{ |
|||
path: "/patientIndex", |
|||
component: () => import("@/views/patientFile/index"), |
|||
name: "Index", |
|||
meta: { title: "患者档案", icon: "dashboard", }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/medicalFile", |
|||
component: Layout, |
|||
redirect: "medicalFile", |
|||
children: [ |
|||
{ |
|||
path: "/medicalIndex", |
|||
component: () => import("@/views/medicalFile/index"), |
|||
name: "medicalIndex", |
|||
meta: { title: "诊疗档案", icon: "dashboard", }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/followFile", |
|||
meta: { title: "随访档案", icon: "dashboard", }, |
|||
component: Layout, |
|||
redirect: "followFile", |
|||
children: [ |
|||
{ |
|||
path: "/followIndex", |
|||
component: () => import("@/views/followFile/index"), |
|||
name: "followIndex", |
|||
meta: { title: "随访队列", icon: "dashboard", }, |
|||
}, |
|||
{ |
|||
path: "/followSubjects", |
|||
component: () => import("@/views/followFile/subjects"), |
|||
name: "followSubjects", |
|||
meta: { title: "随访对象", icon: "dashboard", }, |
|||
}, |
|||
{ |
|||
path: "/followWork", |
|||
component: () => import("@/views/followFile/work"), |
|||
name: "followWork", |
|||
meta: { title: "随访工单", icon: "dashboard", }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/user", |
|||
component: Layout, |
|||
hidden: true, |
|||
redirect: "noredirect", |
|||
children: [ |
|||
{ |
|||
path: "profile", |
|||
component: () => import("@/views/system/user/profile/index"), |
|||
name: "Profile", |
|||
meta: { title: "个人中心", icon: "user" }, |
|||
}, |
|||
], |
|||
}, |
|||
]; |
|||
|
|||
// 动态路由,基于用户权限动态去加载
|
|||
export const dynamicRoutes = [ |
|||
{ |
|||
path: "/system/user-auth", |
|||
component: Layout, |
|||
hidden: true, |
|||
permissions: ["system:user:edit"], |
|||
children: [ |
|||
{ |
|||
path: "role/:userId(\\d+)", |
|||
component: () => import("@/views/system/user/authRole"), |
|||
name: "AuthRole", |
|||
meta: { title: "分配角色", activeMenu: "/system/user" }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/system/role-auth", |
|||
component: Layout, |
|||
hidden: true, |
|||
permissions: ["system:role:edit"], |
|||
children: [ |
|||
{ |
|||
path: "user/:roleId(\\d+)", |
|||
component: () => import("@/views/system/role/authUser"), |
|||
name: "AuthUser", |
|||
meta: { title: "分配用户", activeMenu: "/system/role" }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/system/dict-data", |
|||
component: Layout, |
|||
hidden: true, |
|||
permissions: ["system:dict:list"], |
|||
children: [ |
|||
{ |
|||
path: "index/:dictId(\\d+)", |
|||
component: () => import("@/views/system/dict/data"), |
|||
name: "Data", |
|||
meta: { title: "字典数据", activeMenu: "/system/dict" }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/monitor/job-log", |
|||
component: Layout, |
|||
hidden: true, |
|||
permissions: ["monitor:job:list"], |
|||
children: [ |
|||
{ |
|||
path: "index/:jobId(\\d+)", |
|||
component: () => import("@/views/monitor/job/log"), |
|||
name: "JobLog", |
|||
meta: { title: "调度日志", activeMenu: "/monitor/job" }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: "/tool/gen-edit", |
|||
component: Layout, |
|||
hidden: true, |
|||
permissions: ["tool:gen:edit"], |
|||
children: [ |
|||
{ |
|||
path: "index/:tableId(\\d+)", |
|||
component: () => import("@/views/tool/gen/editTable"), |
|||
name: "GenEdit", |
|||
meta: { title: "修改生成配置", activeMenu: "/tool/gen" }, |
|||
}, |
|||
], |
|||
}, |
|||
]; |
|||
|
|||
// 防止连续点击多次路由报错
|
|||
let routerPush = Router.prototype.push; |
|||
let routerReplace = Router.prototype.replace; |
|||
// push
|
|||
Router.prototype.push = function push(location) { |
|||
return routerPush.call(this, location).catch((err) => err); |
|||
}; |
|||
// replace
|
|||
Router.prototype.replace = function push(location) { |
|||
return routerReplace.call(this, location).catch((err) => err); |
|||
}; |
|||
|
|||
export default new Router({ |
|||
mode: "history", // 去掉url中的#
|
|||
base: process.env.VUE_APP_PUBLIC_PATH, |
|||
scrollBehavior: () => ({ y: 0 }), |
|||
routes: constantRoutes, |
|||
}); |
@ -0,0 +1,137 @@ |
|||
import auth from "@/plugins/auth"; |
|||
import router, { constantRoutes, dynamicRoutes } from "@/router"; |
|||
import { getRouters } from "@/api/menu"; |
|||
import Layout from "@/layout/index"; |
|||
import ParentView from "@/components/ParentView"; |
|||
import InnerLink from "@/layout/components/InnerLink"; |
|||
|
|||
const permission = { |
|||
state: { |
|||
routes: [], |
|||
addRoutes: [], |
|||
defaultRoutes: [], |
|||
topbarRouters: [], |
|||
sidebarRouters: [], |
|||
}, |
|||
mutations: { |
|||
SET_ROUTES: (state, routes) => { |
|||
state.addRoutes = routes; |
|||
state.routes = constantRoutes.concat(routes); |
|||
}, |
|||
SET_DEFAULT_ROUTES: (state, routes) => { |
|||
state.defaultRoutes = constantRoutes.concat(routes); |
|||
}, |
|||
SET_TOPBAR_ROUTES: (state, routes) => { |
|||
state.topbarRouters = routes; |
|||
}, |
|||
SET_SIDEBAR_ROUTERS: (state, routes) => { |
|||
state.sidebarRouters = routes; |
|||
}, |
|||
}, |
|||
actions: { |
|||
// 生成路由
|
|||
GenerateRoutes({ commit }) { |
|||
return new Promise((resolve) => { |
|||
// 向后端请求路由数据
|
|||
getRouters().then((res) => { |
|||
const sdata = JSON.parse(JSON.stringify(res.data)); |
|||
const rdata = JSON.parse(JSON.stringify(res.data)); |
|||
const sidebarRoutes = filterAsyncRouter(sdata); |
|||
const rewriteRoutes = filterAsyncRouter(rdata, false, true); |
|||
const asyncRoutes = filterDynamicRoutes(dynamicRoutes); |
|||
rewriteRoutes.push({ path: "*", redirect: "/404", hidden: true }); |
|||
router.addRoutes(asyncRoutes); |
|||
commit("SET_ROUTES", rewriteRoutes); |
|||
commit("SET_SIDEBAR_ROUTERS", constantRoutes.concat(sidebarRoutes)); |
|||
commit("SET_DEFAULT_ROUTES", sidebarRoutes); |
|||
commit("SET_TOPBAR_ROUTES", sidebarRoutes); |
|||
resolve(rewriteRoutes); |
|||
}); |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
|
|||
// 遍历后台传来的路由字符串,转换为组件对象
|
|||
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { |
|||
return asyncRouterMap.filter((route) => { |
|||
if (type && route.children) { |
|||
route.children = filterChildren(route.children); |
|||
} |
|||
if (route.component) { |
|||
// Layout ParentView 组件特殊处理
|
|||
if (route.component === "Layout") { |
|||
route.component = Layout; |
|||
} else if (route.component === "ParentView") { |
|||
route.component = ParentView; |
|||
} else if (route.component === "InnerLink") { |
|||
route.component = InnerLink; |
|||
} else { |
|||
route.component = loadView(route.component); |
|||
} |
|||
} |
|||
if (route.children != null && route.children && route.children.length) { |
|||
route.children = filterAsyncRouter(route.children, route, type); |
|||
} else { |
|||
delete route["children"]; |
|||
delete route["redirect"]; |
|||
} |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
function filterChildren(childrenMap, lastRouter = false) { |
|||
var children = []; |
|||
childrenMap.forEach((el, index) => { |
|||
if (el.children && el.children.length) { |
|||
if (el.component === "ParentView" && !lastRouter) { |
|||
el.children.forEach((c) => { |
|||
c.path = el.path + "/" + c.path; |
|||
if (c.children && c.children.length) { |
|||
children = children.concat(filterChildren(c.children, c)); |
|||
return; |
|||
} |
|||
children.push(c); |
|||
}); |
|||
return; |
|||
} |
|||
} |
|||
if (lastRouter) { |
|||
el.path = lastRouter.path + "/" + el.path; |
|||
if (el.children && el.children.length) { |
|||
children = children.concat(filterChildren(el.children, el)); |
|||
return; |
|||
} |
|||
} |
|||
children = children.concat(el); |
|||
}); |
|||
return children; |
|||
} |
|||
|
|||
// 动态路由遍历,验证是否具备权限
|
|||
export function filterDynamicRoutes(routes) { |
|||
const res = []; |
|||
routes.forEach((route) => { |
|||
if (route.permissions) { |
|||
if (auth.hasPermiOr(route.permissions)) { |
|||
res.push(route); |
|||
} |
|||
} else if (route.roles) { |
|||
if (auth.hasRoleOr(route.roles)) { |
|||
res.push(route); |
|||
} |
|||
} |
|||
}); |
|||
return res; |
|||
} |
|||
|
|||
export const loadView = (view) => { |
|||
if (process.env.NODE_ENV === "development") { |
|||
return (resolve) => require([`@/views/${view}`], resolve); |
|||
} else { |
|||
// 使用 import 实现生产环境的路由懒加载
|
|||
return () => import(`@/views/${view}`); |
|||
} |
|||
}; |
|||
|
|||
export default permission; |
@ -0,0 +1,82 @@ |
|||
import Vue from 'vue' |
|||
import { mergeRecursive } from "@/utils/ruoyi"; |
|||
import DictMeta from './DictMeta' |
|||
import DictData from './DictData' |
|||
|
|||
const DEFAULT_DICT_OPTIONS = { |
|||
types: [], |
|||
} |
|||
|
|||
/** |
|||
* @classdesc 字典 |
|||
* @property {Object} label 标签对象,内部属性名为字典类型名称 |
|||
* @property {Object} dict 字段数组,内部属性名为字典类型名称 |
|||
* @property {Array.<DictMeta>} _dictMetas 字典元数据数组 |
|||
*/ |
|||
export default class Dict { |
|||
constructor() { |
|||
this.owner = null |
|||
this.label = {} |
|||
this.type = {} |
|||
} |
|||
|
|||
init(options) { |
|||
if (options instanceof Array) { |
|||
options = { types: options } |
|||
} |
|||
const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options) |
|||
if (opts.types === undefined) { |
|||
throw new Error('need dict types') |
|||
} |
|||
const ps = [] |
|||
this._dictMetas = opts.types.map(t => DictMeta.parse(t)) |
|||
this._dictMetas.forEach(dictMeta => { |
|||
const type = dictMeta.type |
|||
Vue.set(this.label, type, {}) |
|||
Vue.set(this.type, type, []) |
|||
if (dictMeta.lazy) { |
|||
return |
|||
} |
|||
ps.push(loadDict(this, dictMeta)) |
|||
}) |
|||
return Promise.all(ps) |
|||
} |
|||
|
|||
/** |
|||
* 重新加载字典 |
|||
* @param {String} type 字典类型 |
|||
*/ |
|||
reloadDict(type) { |
|||
const dictMeta = this._dictMetas.find(e => e.type === type) |
|||
if (dictMeta === undefined) { |
|||
return Promise.reject(`the dict meta of ${type} was not found`) |
|||
} |
|||
return loadDict(this, dictMeta) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加载字典 |
|||
* @param {Dict} dict 字典 |
|||
* @param {DictMeta} dictMeta 字典元数据 |
|||
* @returns {Promise} |
|||
*/ |
|||
function loadDict(dict, dictMeta) { |
|||
return dictMeta.request(dictMeta) |
|||
.then(response => { |
|||
const type = dictMeta.type |
|||
let dicts = dictMeta.responseConverter(response, dictMeta) |
|||
if (!(dicts instanceof Array)) { |
|||
console.error('the return of responseConverter must be Array.<DictData>') |
|||
dicts = [] |
|||
} else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) { |
|||
console.error('the type of elements in dicts must be DictData') |
|||
dicts = [] |
|||
} |
|||
dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts) |
|||
dicts.forEach(d => { |
|||
Vue.set(dict.label[type], d.value, d.label) |
|||
}) |
|||
return dicts |
|||
}) |
|||
} |
@ -0,0 +1,38 @@ |
|||
import { mergeRecursive } from "@/utils/ruoyi"; |
|||
import DictOptions from './DictOptions' |
|||
|
|||
/** |
|||
* @classdesc 字典元数据 |
|||
* @property {String} type 类型 |
|||
* @property {Function} request 请求 |
|||
* @property {String} label 标签字段 |
|||
* @property {String} value 值字段 |
|||
*/ |
|||
export default class DictMeta { |
|||
constructor(options) { |
|||
this.type = options.type |
|||
this.request = options.request |
|||
this.responseConverter = options.responseConverter |
|||
this.labelField = options.labelField |
|||
this.valueField = options.valueField |
|||
this.lazy = options.lazy === true |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 解析字典元数据 |
|||
* @param {Object} options |
|||
* @returns {DictMeta} |
|||
*/ |
|||
DictMeta.parse= function(options) { |
|||
let opts = null |
|||
if (typeof options === 'string') { |
|||
opts = DictOptions.metas[options] || {} |
|||
opts.type = options |
|||
} else if (typeof options === 'object') { |
|||
opts = options |
|||
} |
|||
opts = mergeRecursive(DictOptions.metas['*'], opts) |
|||
return new DictMeta(opts) |
|||
} |
@ -0,0 +1,51 @@ |
|||
import { mergeRecursive } from "@/utils/ruoyi"; |
|||
import dictConverter from './DictConverter' |
|||
|
|||
export const options = { |
|||
metas: { |
|||
'*': { |
|||
/** |
|||
* 字典请求,方法签名为function(dictMeta: DictMeta): Promise |
|||
*/ |
|||
request: (dictMeta) => { |
|||
console.log(`load dict ${dictMeta.type}`) |
|||
return Promise.resolve([]) |
|||
}, |
|||
/** |
|||
* 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData |
|||
*/ |
|||
responseConverter, |
|||
labelField: 'label', |
|||
valueField: 'value', |
|||
}, |
|||
}, |
|||
/** |
|||
* 默认标签字段 |
|||
*/ |
|||
DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'], |
|||
/** |
|||
* 默认值字段 |
|||
*/ |
|||
DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'], |
|||
} |
|||
|
|||
/** |
|||
* 映射字典 |
|||
* @param {Object} response 字典数据 |
|||
* @param {DictMeta} dictMeta 字典元数据 |
|||
* @returns {DictData} |
|||
*/ |
|||
function responseConverter(response, dictMeta) { |
|||
const dicts = response.content instanceof Array ? response.content : response |
|||
if (dicts === undefined) { |
|||
console.warn(`no dict data of "${dictMeta.type}" found in the response`) |
|||
return [] |
|||
} |
|||
return dicts.map(d => dictConverter(d, dictMeta)) |
|||
} |
|||
|
|||
export function mergeOptions(src) { |
|||
mergeRecursive(options, src) |
|||
} |
|||
|
|||
export default options |
@ -0,0 +1,390 @@ |
|||
import { parseTime } from './ruoyi' |
|||
|
|||
/** |
|||
* 表格时间格式化 |
|||
*/ |
|||
export function formatDate(cellValue) { |
|||
if (cellValue == null || cellValue == "") return ""; |
|||
var date = new Date(cellValue) |
|||
var year = date.getFullYear() |
|||
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 |
|||
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() |
|||
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() |
|||
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() |
|||
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() |
|||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds |
|||
} |
|||
|
|||
/** |
|||
* @param {number} time |
|||
* @param {string} option |
|||
* @returns {string} |
|||
*/ |
|||
export function formatTime(time, option) { |
|||
if (('' + time).length === 10) { |
|||
time = parseInt(time) * 1000 |
|||
} else { |
|||
time = +time |
|||
} |
|||
const d = new Date(time) |
|||
const now = Date.now() |
|||
|
|||
const diff = (now - d) / 1000 |
|||
|
|||
if (diff < 30) { |
|||
return '刚刚' |
|||
} else if (diff < 3600) { |
|||
// less 1 hour
|
|||
return Math.ceil(diff / 60) + '分钟前' |
|||
} else if (diff < 3600 * 24) { |
|||
return Math.ceil(diff / 3600) + '小时前' |
|||
} else if (diff < 3600 * 24 * 2) { |
|||
return '1天前' |
|||
} |
|||
if (option) { |
|||
return parseTime(time, option) |
|||
} else { |
|||
return ( |
|||
d.getMonth() + |
|||
1 + |
|||
'月' + |
|||
d.getDate() + |
|||
'日' + |
|||
d.getHours() + |
|||
'时' + |
|||
d.getMinutes() + |
|||
'分' |
|||
) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @param {string} url |
|||
* @returns {Object} |
|||
*/ |
|||
export function getQueryObject(url) { |
|||
url = url == null ? window.location.href : url |
|||
const search = url.substring(url.lastIndexOf('?') + 1) |
|||
const obj = {} |
|||
const reg = /([^?&=]+)=([^?&=]*)/g |
|||
search.replace(reg, (rs, $1, $2) => { |
|||
const name = decodeURIComponent($1) |
|||
let val = decodeURIComponent($2) |
|||
val = String(val) |
|||
obj[name] = val |
|||
return rs |
|||
}) |
|||
return obj |
|||
} |
|||
|
|||
/** |
|||
* @param {string} input value |
|||
* @returns {number} output value |
|||
*/ |
|||
export function byteLength(str) { |
|||
// returns the byte length of an utf8 string
|
|||
let s = str.length |
|||
for (var i = str.length - 1; i >= 0; i--) { |
|||
const code = str.charCodeAt(i) |
|||
if (code > 0x7f && code <= 0x7ff) s++ |
|||
else if (code > 0x7ff && code <= 0xffff) s += 2 |
|||
if (code >= 0xDC00 && code <= 0xDFFF) i-- |
|||
} |
|||
return s |
|||
} |
|||
|
|||
/** |
|||
* @param {Array} actual |
|||
* @returns {Array} |
|||
*/ |
|||
export function cleanArray(actual) { |
|||
const newArray = [] |
|||
for (let i = 0; i < actual.length; i++) { |
|||
if (actual[i]) { |
|||
newArray.push(actual[i]) |
|||
} |
|||
} |
|||
return newArray |
|||
} |
|||
|
|||
/** |
|||
* @param {Object} json |
|||
* @returns {Array} |
|||
*/ |
|||
export function param(json) { |
|||
if (!json) return '' |
|||
return cleanArray( |
|||
Object.keys(json).map(key => { |
|||
if (json[key] === undefined) return '' |
|||
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) |
|||
}) |
|||
).join('&') |
|||
} |
|||
|
|||
/** |
|||
* @param {string} url |
|||
* @returns {Object} |
|||
*/ |
|||
export function param2Obj(url) { |
|||
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') |
|||
if (!search) { |
|||
return {} |
|||
} |
|||
const obj = {} |
|||
const searchArr = search.split('&') |
|||
searchArr.forEach(v => { |
|||
const index = v.indexOf('=') |
|||
if (index !== -1) { |
|||
const name = v.substring(0, index) |
|||
const val = v.substring(index + 1, v.length) |
|||
obj[name] = val |
|||
} |
|||
}) |
|||
return obj |
|||
} |
|||
|
|||
/** |
|||
* @param {string} val |
|||
* @returns {string} |
|||
*/ |
|||
export function html2Text(val) { |
|||
const div = document.createElement('div') |
|||
div.innerHTML = val |
|||
return div.textContent || div.innerText |
|||
} |
|||
|
|||
/** |
|||
* Merges two objects, giving the last one precedence |
|||
* @param {Object} target |
|||
* @param {(Object|Array)} source |
|||
* @returns {Object} |
|||
*/ |
|||
export function objectMerge(target, source) { |
|||
if (typeof target !== 'object') { |
|||
target = {} |
|||
} |
|||
if (Array.isArray(source)) { |
|||
return source.slice() |
|||
} |
|||
Object.keys(source).forEach(property => { |
|||
const sourceProperty = source[property] |
|||
if (typeof sourceProperty === 'object') { |
|||
target[property] = objectMerge(target[property], sourceProperty) |
|||
} else { |
|||
target[property] = sourceProperty |
|||
} |
|||
}) |
|||
return target |
|||
} |
|||
|
|||
/** |
|||
* @param {HTMLElement} element |
|||
* @param {string} className |
|||
*/ |
|||
export function toggleClass(element, className) { |
|||
if (!element || !className) { |
|||
return |
|||
} |
|||
let classString = element.className |
|||
const nameIndex = classString.indexOf(className) |
|||
if (nameIndex === -1) { |
|||
classString += '' + className |
|||
} else { |
|||
classString = |
|||
classString.substr(0, nameIndex) + |
|||
classString.substr(nameIndex + className.length) |
|||
} |
|||
element.className = classString |
|||
} |
|||
|
|||
/** |
|||
* @param {string} type |
|||
* @returns {Date} |
|||
*/ |
|||
export function getTime(type) { |
|||
if (type === 'start') { |
|||
return new Date().getTime() - 3600 * 1000 * 24 * 90 |
|||
} else { |
|||
return new Date(new Date().toDateString()) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @param {Function} func |
|||
* @param {number} wait |
|||
* @param {boolean} immediate |
|||
* @return {*} |
|||
*/ |
|||
export function debounce(func, wait, immediate) { |
|||
let timeout, args, context, timestamp, result |
|||
|
|||
const later = function() { |
|||
// 据上一次触发时间间隔
|
|||
const last = +new Date() - timestamp |
|||
|
|||
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
|
|||
if (last < wait && last > 0) { |
|||
timeout = setTimeout(later, wait - last) |
|||
} else { |
|||
timeout = null |
|||
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
|
|||
if (!immediate) { |
|||
result = func.apply(context, args) |
|||
if (!timeout) context = args = null |
|||
} |
|||
} |
|||
} |
|||
|
|||
return function(...args) { |
|||
context = this |
|||
timestamp = +new Date() |
|||
const callNow = immediate && !timeout |
|||
// 如果延时不存在,重新设定延时
|
|||
if (!timeout) timeout = setTimeout(later, wait) |
|||
if (callNow) { |
|||
result = func.apply(context, args) |
|||
context = args = null |
|||
} |
|||
|
|||
return result |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* This is just a simple version of deep copy |
|||
* Has a lot of edge cases bug |
|||
* If you want to use a perfect deep copy, use lodash's _.cloneDeep |
|||
* @param {Object} source |
|||
* @returns {Object} |
|||
*/ |
|||
export function deepClone(source) { |
|||
if (!source && typeof source !== 'object') { |
|||
throw new Error('error arguments', 'deepClone') |
|||
} |
|||
const targetObj = source.constructor === Array ? [] : {} |
|||
Object.keys(source).forEach(keys => { |
|||
if (source[keys] && typeof source[keys] === 'object') { |
|||
targetObj[keys] = deepClone(source[keys]) |
|||
} else { |
|||
targetObj[keys] = source[keys] |
|||
} |
|||
}) |
|||
return targetObj |
|||
} |
|||
|
|||
/** |
|||
* @param {Array} arr |
|||
* @returns {Array} |
|||
*/ |
|||
export function uniqueArr(arr) { |
|||
return Array.from(new Set(arr)) |
|||
} |
|||
|
|||
/** |
|||
* @returns {string} |
|||
*/ |
|||
export function createUniqueString() { |
|||
const timestamp = +new Date() + '' |
|||
const randomNum = parseInt((1 + Math.random()) * 65536) + '' |
|||
return (+(randomNum + timestamp)).toString(32) |
|||
} |
|||
|
|||
/** |
|||
* Check if an element has a class |
|||
* @param {HTMLElement} elm |
|||
* @param {string} cls |
|||
* @returns {boolean} |
|||
*/ |
|||
export function hasClass(ele, cls) { |
|||
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) |
|||
} |
|||
|
|||
/** |
|||
* Add class to element |
|||
* @param {HTMLElement} elm |
|||
* @param {string} cls |
|||
*/ |
|||
export function addClass(ele, cls) { |
|||
if (!hasClass(ele, cls)) ele.className += ' ' + cls |
|||
} |
|||
|
|||
/** |
|||
* Remove class from element |
|||
* @param {HTMLElement} elm |
|||
* @param {string} cls |
|||
*/ |
|||
export function removeClass(ele, cls) { |
|||
if (hasClass(ele, cls)) { |
|||
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') |
|||
ele.className = ele.className.replace(reg, ' ') |
|||
} |
|||
} |
|||
|
|||
export function makeMap(str, expectsLowerCase) { |
|||
const map = Object.create(null) |
|||
const list = str.split(',') |
|||
for (let i = 0; i < list.length; i++) { |
|||
map[list[i]] = true |
|||
} |
|||
return expectsLowerCase |
|||
? val => map[val.toLowerCase()] |
|||
: val => map[val] |
|||
} |
|||
|
|||
export const exportDefault = 'export default ' |
|||
|
|||
export const beautifierConf = { |
|||
html: { |
|||
indent_size: '2', |
|||
indent_char: ' ', |
|||
max_preserve_newlines: '-1', |
|||
preserve_newlines: false, |
|||
keep_array_indentation: false, |
|||
break_chained_methods: false, |
|||
indent_scripts: 'separate', |
|||
brace_style: 'end-expand', |
|||
space_before_conditional: true, |
|||
unescape_strings: false, |
|||
jslint_happy: false, |
|||
end_with_newline: true, |
|||
wrap_line_length: '110', |
|||
indent_inner_html: true, |
|||
comma_first: false, |
|||
e4x: true, |
|||
indent_empty_lines: true |
|||
}, |
|||
js: { |
|||
indent_size: '2', |
|||
indent_char: ' ', |
|||
max_preserve_newlines: '-1', |
|||
preserve_newlines: false, |
|||
keep_array_indentation: false, |
|||
break_chained_methods: false, |
|||
indent_scripts: 'normal', |
|||
brace_style: 'end-expand', |
|||
space_before_conditional: true, |
|||
unescape_strings: false, |
|||
jslint_happy: true, |
|||
end_with_newline: true, |
|||
wrap_line_length: '110', |
|||
indent_inner_html: true, |
|||
comma_first: false, |
|||
e4x: true, |
|||
indent_empty_lines: true |
|||
} |
|||
} |
|||
|
|||
// 首字母大小
|
|||
export function titleCase(str) { |
|||
return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) |
|||
} |
|||
|
|||
// 下划转驼峰
|
|||
export function camelCase(str) { |
|||
return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) |
|||
} |
|||
|
|||
export function isNumberStr(str) { |
|||
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) |
|||
} |
|||
|
@ -0,0 +1,152 @@ |
|||
import axios from 'axios' |
|||
import { Notification, MessageBox, Message, Loading } from 'element-ui' |
|||
import store from '@/store' |
|||
import { getToken } from '@/utils/auth' |
|||
import errorCode from '@/utils/errorCode' |
|||
import { tansParams, blobValidate } from "@/utils/ruoyi"; |
|||
import cache from '@/plugins/cache' |
|||
import { saveAs } from 'file-saver' |
|||
|
|||
let downloadLoadingInstance; |
|||
// 是否显示重新登录
|
|||
export let isRelogin = { show: false }; |
|||
|
|||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' |
|||
// 创建axios实例
|
|||
const service = axios.create({ |
|||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
|||
baseURL: process.env.VUE_APP_BASE_API, |
|||
// 超时
|
|||
timeout: 10000 |
|||
}) |
|||
|
|||
// request拦截器
|
|||
service.interceptors.request.use(config => { |
|||
// 是否需要设置 token
|
|||
const isToken = (config.headers || {}).isToken === false |
|||
// 是否需要防止数据重复提交
|
|||
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false |
|||
if (getToken() && !isToken) { |
|||
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
|||
} |
|||
// 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; |
|||
} |
|||
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { |
|||
const requestObj = { |
|||
url: config.url, |
|||
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, |
|||
time: new Date().getTime() |
|||
} |
|||
const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小
|
|||
const limitSize = 5 * 1024 * 1024; // 限制存放数据5M
|
|||
if (requestSize >= limitSize) { |
|||
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。') |
|||
return config; |
|||
} |
|||
const sessionObj = cache.session.getJSON('sessionObj') |
|||
if (sessionObj === undefined || sessionObj === null || sessionObj === '') { |
|||
cache.session.setJSON('sessionObj', requestObj) |
|||
} else { |
|||
const s_url = sessionObj.url; // 请求地址
|
|||
const s_data = sessionObj.data; // 请求数据
|
|||
const s_time = sessionObj.time; // 请求时间
|
|||
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
|
|||
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { |
|||
const message = '数据正在处理,请勿重复提交'; |
|||
console.warn(`[${s_url}]: ` + message) |
|||
return Promise.reject(new Error(message)) |
|||
} else { |
|||
cache.session.setJSON('sessionObj', requestObj) |
|||
} |
|||
} |
|||
} |
|||
return config |
|||
}, error => { |
|||
console.log(error) |
|||
Promise.reject(error) |
|||
}) |
|||
|
|||
// 响应拦截器
|
|||
service.interceptors.response.use(res => { |
|||
// 未设置状态码则默认成功状态
|
|||
const code = res.data.code || 200; |
|||
// 获取错误信息
|
|||
const msg = errorCode[code] || res.data.msg || errorCode['default'] |
|||
// 二进制数据则直接返回
|
|||
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { |
|||
return res.data |
|||
} |
|||
if (code === 401) { |
|||
if (!isRelogin.show) { |
|||
isRelogin.show = true; |
|||
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { |
|||
isRelogin.show = false; |
|||
store.dispatch('LogOut').then(() => { |
|||
location.href = process.env.VUE_APP_PUBLIC_PATH; |
|||
}) |
|||
}).catch(() => { |
|||
isRelogin.show = false; |
|||
}); |
|||
} |
|||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。') |
|||
} else if (code === 500) { |
|||
Message({ message: msg, type: 'error' }) |
|||
return Promise.reject(new Error(msg)) |
|||
} else if (code === 601) { |
|||
Message({ message: msg, type: 'warning' }) |
|||
return Promise.reject('error') |
|||
} else if (code !== 200) { |
|||
Notification.error({ title: msg }) |
|||
return Promise.reject('error') |
|||
} else { |
|||
return 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) + "异常"; |
|||
} |
|||
Message({ message: message, type: 'error', duration: 5 * 1000 }) |
|||
return Promise.reject(error) |
|||
} |
|||
) |
|||
|
|||
// 通用下载方法
|
|||
export function download(url, params, filename, config) { |
|||
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) |
|||
return service.post(url, params, { |
|||
transformRequest: [(params) => { return tansParams(params) }], |
|||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
|||
responseType: 'blob', |
|||
...config |
|||
}).then(async (data) => { |
|||
const isBlob = blobValidate(data); |
|||
if (isBlob) { |
|||
const blob = new Blob([data]) |
|||
saveAs(blob, filename) |
|||
} else { |
|||
const resText = await data.text(); |
|||
const rspObj = JSON.parse(resText); |
|||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] |
|||
Message.error(errMsg); |
|||
} |
|||
downloadLoadingInstance.close(); |
|||
}).catch((r) => { |
|||
console.error(r) |
|||
Message.error('下载文件出现错误,请联系管理员!') |
|||
downloadLoadingInstance.close(); |
|||
}) |
|||
} |
|||
|
|||
export default service |
@ -0,0 +1,233 @@ |
|||
|
|||
|
|||
/** |
|||
* 通用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 |
|||
} |
|||
|
|||
// 验证是否为blob格式
|
|||
export function blobValidate(data) { |
|||
return data.type !== 'application/json' |
|||
} |
@ -0,0 +1,465 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form |
|||
:model="queryParams" |
|||
ref="queryForm" |
|||
size="small" |
|||
:inline="true" |
|||
v-show="showSearch" |
|||
label-width="68px" |
|||
> |
|||
<el-form-item label="队列名称" prop="name"> |
|||
<el-input |
|||
v-model="queryParams.param.name" |
|||
placeholder="请输入" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button |
|||
type="primary" |
|||
icon="el-icon-search" |
|||
size="mini" |
|||
@click="handleQuery" |
|||
>搜索</el-button |
|||
> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"> |
|||
重置 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="primary" |
|||
plain |
|||
icon="el-icon-plus" |
|||
size="mini" |
|||
@click="handleAdd" |
|||
>新增</el-button |
|||
> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="danger" |
|||
plain |
|||
icon="el-icon-delete" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleDelete" |
|||
>删除</el-button |
|||
> |
|||
</el-col> |
|||
<right-toolbar |
|||
:showSearch.sync="showSearch" |
|||
@queryTable="getList" |
|||
></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table |
|||
v-loading="loading" |
|||
:data="listDat" |
|||
@selection-change="handleSelectionChange" |
|||
max-height="600" |
|||
> |
|||
<el-table-column type="selection" width="55" align="center" /> |
|||
<el-table-column |
|||
label="队列名称" |
|||
align="center" |
|||
prop="name" |
|||
min-width="130" |
|||
/> |
|||
<el-table-column |
|||
label="随访方式" |
|||
align="center" |
|||
prop="followupMethod" |
|||
show-overflow-tooltip |
|||
min-width="100" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.followupMethod == 0">电话随访</span> |
|||
<span v-if="scope.row.followupMethod == 1">入户随访</span> |
|||
<span v-if="scope.row.followupMethod == 2">到院随访</span> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column |
|||
label="随访类型" |
|||
align="center" |
|||
show-overflow-tooltip |
|||
min-width="150" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.followupType == 0">单次</span> |
|||
<span v-if="scope.row.followupType == 1">周期</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="随访频次" |
|||
align="center" |
|||
prop="frequency" |
|||
show-overflow-tooltip |
|||
min-width="100" |
|||
/> |
|||
<el-table-column |
|||
label="总月数" |
|||
align="center" |
|||
prop="followupMonth" |
|||
show-overflow-tooltip |
|||
min-width="100" |
|||
/> |
|||
<el-table-column |
|||
label="状态" |
|||
align="center" |
|||
prop="status" |
|||
show-overflow-tooltip |
|||
min-width="100" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.status == 0" style="color: red">禁用</span> |
|||
<span v-if="scope.row.status == 1" style="color: green">启用</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="创建人/创建时间" |
|||
align="center" |
|||
min-width="130" |
|||
fixed="right" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<div>{{ scope.row.createBy }}</div> |
|||
<span>{{ |
|||
parseTime(scope.row.createTime, "{y}-{m}-{d} {h}:{i}") |
|||
}}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column fixed="right" label="操作" align="center" width="150"> |
|||
<template slot-scope="scope"> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-edit" |
|||
@click="handleUpdate(scope.row)" |
|||
>修改</el-button |
|||
> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-delete" |
|||
@click="handleDelete(scope.row)" |
|||
>删除</el-button |
|||
> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="total > 0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
|
|||
<!-- 添加或修改公告对话框 --> |
|||
<el-dialog |
|||
class="popup" |
|||
:title="title" |
|||
:visible.sync="open" |
|||
width="780px" |
|||
append-to-body |
|||
> |
|||
<el-form |
|||
class="formStep" |
|||
ref="form" |
|||
:model="form" |
|||
:rules="rules" |
|||
label-width="90px" |
|||
> |
|||
<el-form-item label="队列名称" prop="name"> |
|||
<el-input v-model="form.name" placeholder="请输入" /> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="随访方式" prop="followupMethod"> |
|||
<el-select v-model="form.followupMethod" placeholder="请选择"> |
|||
<el-option label="电话随访" :value="0"> </el-option> |
|||
<el-option label="入户随访" :value="1"> </el-option> |
|||
<el-option label="到院随访" :value="2"> </el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="随访类型" prop="followupType"> |
|||
<el-radio-group v-model="form.followupType"> |
|||
<el-radio :label="0">单次</el-radio> |
|||
<el-radio :label="1">周期</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="随访频次" prop="followupType"> |
|||
<el-input v-model="form.frequency" placeholder="请输入cron执行表达式"> |
|||
<template slot="append"> |
|||
<el-button |
|||
style="width: 110px" |
|||
type="primary" |
|||
@click="handleShowCron" |
|||
> |
|||
生成表达式 |
|||
<i class="el-icon-time el-icon--right"></i> |
|||
</el-button> |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item label="总月数" prop="followupMonth"> |
|||
<el-input v-model="form.followupMonth" placeholder="请输入" /> |
|||
</el-form-item> |
|||
<el-form-item label="状态" prop="status"> |
|||
<el-radio-group v-model="form.status"> |
|||
<el-radio :label="1">启用</el-radio> |
|||
<el-radio :label="0">禁用</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="cancel">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
<el-dialog |
|||
title="Cron表达式生成器" |
|||
:visible.sync="openCron" |
|||
append-to-body |
|||
destroy-on-close |
|||
class="scrollbar" |
|||
> |
|||
<crontab |
|||
@hide="openCron = false" |
|||
@fill="crontabFill" |
|||
:expression="expression" |
|||
></crontab> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Crontab from "@/components/Crontab"; |
|||
import { |
|||
followupQuery, |
|||
followupAdd, |
|||
followupUpd, |
|||
followupDel, |
|||
} from "@/api/followupFile"; |
|||
export default { |
|||
name: "Notice", |
|||
components: { Crontab }, |
|||
data() { |
|||
return { |
|||
// 是否显示Cron表达式弹出层 |
|||
openCron: false, |
|||
// 传入的表达式 |
|||
expression: "", |
|||
idCardType: [ |
|||
{ |
|||
label: "身份证", |
|||
value: 0, |
|||
}, |
|||
{ |
|||
label: "护照或外国人永居证", |
|||
value: 1, |
|||
}, |
|||
{ |
|||
label: "港澳居民来往内地通行", |
|||
value: 2, |
|||
}, |
|||
{ |
|||
label: "台湾居民来往大陆通行证", |
|||
value: 3, |
|||
}, |
|||
], |
|||
idCardTypeValue: { |
|||
0: "身份证", |
|||
1: "护照或外国人永居证", |
|||
2: "港澳居民来往内地通行", |
|||
3: "台湾居民来往大陆通行证", |
|||
}, |
|||
loading: false, // 遮罩层 |
|||
ids: [], // 选中数组 |
|||
single: true, // 非单个禁用 |
|||
multiple: true, // 非多个禁用 |
|||
showSearch: true, // 显示搜索条件 |
|||
total: 0, // 总条数 |
|||
listDat: [{}], // 公告表格数据 |
|||
title: "", // 弹出层标题 |
|||
open: false, // 是否显示弹出层 |
|||
importOpen: false, // 导入弹窗 |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
param: { |
|||
name: "", //关键字 |
|||
}, |
|||
}, |
|||
formDisabled: false, |
|||
importform: {}, |
|||
// 表单参数 |
|||
form: {}, |
|||
// 表单校验 |
|||
rules: { |
|||
name: [ |
|||
{ required: true, message: "队列名称不能为空", trigger: "blur" }, |
|||
], |
|||
followupMethod: [ |
|||
{ required: true, message: "随访方式不能为空", trigger: "change" }, |
|||
], |
|||
followupType: [ |
|||
{ required: true, message: "随访类型不能为空", trigger: "blur" }, |
|||
], |
|||
frequency: [ |
|||
{ required: true, message: "随访频次不能为空", trigger: "blur" }, |
|||
], |
|||
status: [{ required: true, message: "状态不能为空", trigger: "blur" }], |
|||
}, |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
/** cron表达式按钮操作 */ |
|||
handleShowCron() { |
|||
this.expression = this.form.cronExpression; |
|||
this.openCron = true; |
|||
}, |
|||
/** 确定后回传值 */ |
|||
crontabFill(value) { |
|||
this.form.frequency = value; |
|||
}, |
|||
/** 查询公告列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
followupQuery(this.queryParams).then((res) => { |
|||
this.listDat = res.data.list; |
|||
this.total = res.data.total; |
|||
this.loading = false; |
|||
}); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = { |
|||
name: "", // 姓名 |
|||
followupMethod: 0, // 随访方式 |
|||
followupType: 0, // 随访类型 |
|||
frequency: "", // cron执行表达式 |
|||
followupMonth: "", |
|||
status: 1, // 状态 |
|||
}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.queryParams = { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
param: { |
|||
name: "", |
|||
}, |
|||
}; |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map((item) => item.id); |
|||
this.single = selection.length != 1; |
|||
this.multiple = !selection.length; |
|||
}, |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
this.reset(); |
|||
this.open = true; |
|||
this.title = "新增随访队列"; |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleUpdate(row) { |
|||
this.open = true; |
|||
this.title = "修改随访队列"; |
|||
this.form = JSON.parse(JSON.stringify(row)); |
|||
}, |
|||
/** 提交按钮 */ |
|||
submitForm: function () { |
|||
this.$refs["form"].validate((valid) => { |
|||
if (valid) { |
|||
if (this.form.id != undefined) { |
|||
followupUpd(this.form).then((response) => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} else { |
|||
followupAdd(this.form).then((response) => { |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const idList = row.id ? [row.id] : this.ids; |
|||
this.$modal |
|||
.confirm("是否确认删除当前选择的数据?") |
|||
.then(function () { |
|||
return followupDel({ idList: idList }); |
|||
}) |
|||
.then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}) |
|||
.catch(() => {}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download( |
|||
"system/user/export", |
|||
{ |
|||
...this.queryParams.params, |
|||
}, |
|||
`患者档案.xlsx` |
|||
); |
|||
}, |
|||
/** 导入按钮操作 */ |
|||
handleImport() { |
|||
this.upload.title = "用户导入"; |
|||
this.upload.open = true; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
<style scoped src="@/assets/styles/common.css"></style> |
|||
<style scoped> |
|||
.form-item-age { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.form-item-age span { |
|||
margin: 0 10px; |
|||
} |
|||
.form-item-age >>> .el-input { |
|||
width: 100px; |
|||
} |
|||
</style> |
|||
<!-- >>> .el-input__inner { |
|||
padding: 0 15px !important; |
|||
} --> |
@ -0,0 +1,398 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form |
|||
:model="queryParams" |
|||
ref="queryForm" |
|||
size="small" |
|||
:inline="true" |
|||
v-show="showSearch" |
|||
label-width="68px" |
|||
> |
|||
<el-form-item label="随访队列" prop="queueId"> |
|||
<el-select v-model="queryParams.param.queueId" placeholder="请选择"> |
|||
<el-option |
|||
v-for="item in followupList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
> |
|||
</el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button |
|||
type="primary" |
|||
icon="el-icon-search" |
|||
size="mini" |
|||
@click="handleQuery" |
|||
>搜索</el-button |
|||
> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"> |
|||
重置 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<right-toolbar |
|||
:showSearch.sync="showSearch" |
|||
@queryTable="getList" |
|||
></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table |
|||
v-loading="loading" |
|||
:data="listDat" |
|||
@selection-change="handleSelectionChange" |
|||
max-height="600" |
|||
> |
|||
<el-table-column type="selection" width="55" align="center" /> |
|||
<el-table-column label="姓名" align="center" prop="name" width="100" /> |
|||
<el-table-column |
|||
label="性别" |
|||
align="center" |
|||
prop="gender" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.gender == 0">男</span> |
|||
<span v-if="scope.row.gender == 1">女</span> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column |
|||
label="出生日期" |
|||
align="center" |
|||
prop="birthDate" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
> |
|||
<template slot-scope="scope"> |
|||
{{ parseTime(scope.row.createTime, "{y}-{m}-{d}") }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="民族" |
|||
align="center" |
|||
prop="ethnicity" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="受教育年限" |
|||
align="center" |
|||
prop="educationYears" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="手机号码" |
|||
align="center" |
|||
prop="phone" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column |
|||
label="证件类型" |
|||
align="center" |
|||
prop="idCardType" |
|||
show-overflow-tooltip |
|||
width="200" |
|||
> |
|||
<template slot-scope="scope"> |
|||
{{ idCardTypeValue[scope.row.idCardType] }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="证件号码" |
|||
align="center" |
|||
prop="idCard" |
|||
show-overflow-tooltip |
|||
width="180" |
|||
/> |
|||
<el-table-column |
|||
fixed="right" |
|||
label="随访队列" |
|||
align="center" |
|||
prop="queueList" |
|||
show-overflow-tooltip |
|||
width="180" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<div v-if="scope.row.queueList && scope.row.queueList.length"> |
|||
{{ scope.row.queueList.map((i) => i.queueName).join(",") }} |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="创建人/创建时间" |
|||
align="center" |
|||
width="130" |
|||
fixed="right" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<div>{{ scope.row.createBy }}</div> |
|||
<span>{{ |
|||
parseTime(scope.row.createTime, "{y}-{m}-{d} {h}:{i}") |
|||
}}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column fixed="right" label="操作" align="center" width="200"> |
|||
<template slot-scope="scope"> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-edit" |
|||
@click="handleUpdate(scope.row)" |
|||
>队列管理</el-button |
|||
> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-tickets" |
|||
@click="handlePatient(scope.row)" |
|||
>患者档案</el-button |
|||
> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-notebook-2" |
|||
@click="handleMedical(scope.row)" |
|||
>诊疗档案</el-button |
|||
> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="total > 0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
|
|||
<!-- 添加或修改公告对话框 --> |
|||
<el-dialog |
|||
class="popup" |
|||
:title="title" |
|||
:visible.sync="open" |
|||
width="780px" |
|||
append-to-body |
|||
> |
|||
<el-form |
|||
class="formStep" |
|||
ref="form" |
|||
:model="form" |
|||
:rules="rules" |
|||
label-width="90px" |
|||
> |
|||
<el-form-item label="随访队列" prop="queueIdList"> |
|||
<el-select v-model="form.queueIdList" multiple placeholder="请选择"> |
|||
<el-option |
|||
v-for="item in followupList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
> |
|||
</el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="cancel">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { queryPatient, followupQuery, updPatient } from "@/api/followupFile"; |
|||
export default { |
|||
name: "Notice", |
|||
data() { |
|||
return { |
|||
fileList: [], |
|||
idCardType: [ |
|||
{ |
|||
label: "身份证", |
|||
value: 0, |
|||
}, |
|||
{ |
|||
label: "护照或外国人永居证", |
|||
value: 1, |
|||
}, |
|||
{ |
|||
label: "港澳居民来往内地通行", |
|||
value: 2, |
|||
}, |
|||
{ |
|||
label: "台湾居民来往大陆通行证", |
|||
value: 3, |
|||
}, |
|||
], |
|||
idCardTypeValue: { |
|||
0: "身份证", |
|||
1: "护照或外国人永居证", |
|||
2: "港澳居民来往内地通行", |
|||
3: "台湾居民来往大陆通行证", |
|||
}, |
|||
loading: false, // 遮罩层 |
|||
ids: [], // 选中数组 |
|||
single: true, // 非单个禁用 |
|||
multiple: true, // 非多个禁用 |
|||
showSearch: true, // 显示搜索条件 |
|||
total: 0, // 总条数 |
|||
listDat: [{}], // 公告表格数据 |
|||
title: "", // 弹出层标题 |
|||
open: false, // 是否显示弹出层 |
|||
importOpen: false, // 导入弹窗 |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
param: {}, |
|||
}, |
|||
formDisabled: false, |
|||
importform: {}, |
|||
// 表单参数 |
|||
form: {}, |
|||
// 表单校验 |
|||
rules: { |
|||
queueIdList: [ |
|||
{ required: true, message: "随访队列不能为空", trigger: "change" }, |
|||
], |
|||
}, |
|||
followupList: [], // 随访队列 |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
this.getFollowupQuery(); |
|||
}, |
|||
methods: { |
|||
// 获取随访队列信息 |
|||
getFollowupQuery() { |
|||
followupQuery({ |
|||
pageNum: -1, |
|||
param: {}, |
|||
}).then((res) => { |
|||
this.followupList = res.data.list; |
|||
}); |
|||
}, |
|||
|
|||
/** 查询公告列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
queryPatient(this.queryParams).then((res) => { |
|||
this.listDat = res.data.list; |
|||
this.total = res.data.total; |
|||
this.loading = false; |
|||
}); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = {}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map((item) => item.id); |
|||
this.single = selection.length != 1; |
|||
this.multiple = !selection.length; |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleUpdate(row) { |
|||
this.open = true; |
|||
this.title = "队列管理"; |
|||
let queueList = row.queueList.map((item) => item.queueId); |
|||
this.form = JSON.parse( |
|||
JSON.stringify({ |
|||
...row, |
|||
patientId: row.id, |
|||
queueIdList: queueList, |
|||
}) |
|||
); |
|||
}, |
|||
/** 跳转患者档案 */ |
|||
handlePatient(row) {}, |
|||
/** 诊疗档案 */ |
|||
handleMedical(row) {}, |
|||
/** 提交按钮 */ |
|||
submitForm: function () { |
|||
this.$refs["form"].validate((valid) => { |
|||
if (valid) { |
|||
updPatient(this.form).then((response) => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const idList = row.id ? [row.id] : this.ids; |
|||
this.$modal |
|||
.confirm("是否确认删除当前选择的数据?") |
|||
.then(function () { |
|||
return patientDel({ idList: idList }); |
|||
}) |
|||
.then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}) |
|||
.catch(() => {}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download( |
|||
"system/user/export", |
|||
{ |
|||
...this.queryParams.params, |
|||
}, |
|||
`患者档案.xlsx` |
|||
); |
|||
}, |
|||
/** 导入按钮操作 */ |
|||
handleImport() { |
|||
this.upload.title = "用户导入"; |
|||
this.upload.open = true; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
<style scoped src="@/assets/styles/common.css"></style> |
|||
<style scoped> |
|||
.form-item-age { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.form-item-age span { |
|||
margin: 0 10px; |
|||
} |
|||
.form-item-age >>> .el-input { |
|||
width: 100px; |
|||
} |
|||
</style> |
|||
<!-- >>> .el-input__inner { |
|||
padding: 0 15px !important; |
|||
} --> |
@ -0,0 +1,529 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-row :gutter="10" class="mb8"> |
|||
<right-toolbar |
|||
:showSearch.sync="showSearch" |
|||
@queryTable="getList" |
|||
></right-toolbar> |
|||
</el-row> |
|||
<el-tabs v-model="queryParams.param.status" @tab-click="handleClick"> |
|||
<el-tab-pane label="待随访" name="0"></el-tab-pane> |
|||
<el-tab-pane label="已随访" name="1"></el-tab-pane> |
|||
<el-tab-pane label="失访" name="2"></el-tab-pane> |
|||
</el-tabs> |
|||
<el-table |
|||
v-loading="loading" |
|||
:data="listDat" |
|||
@selection-change="handleSelectionChange" |
|||
> |
|||
<el-table-column type="selection" width="55" align="center" /> |
|||
<el-table-column |
|||
label="工单id" |
|||
show-overflow-tooltip |
|||
align="center" |
|||
prop="" |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="随访队列" |
|||
show-overflow-tooltip |
|||
align="" |
|||
prop="name" |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="患者姓名" |
|||
align="center" |
|||
prop="name" |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="性别" |
|||
align="center" |
|||
prop="gender" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.gender == 0">男</span> |
|||
<span v-if="scope.row.gender == 1">女</span> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column |
|||
label="年龄" |
|||
align="center" |
|||
prop="age" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column |
|||
label="证件号码" |
|||
align="center" |
|||
prop="idCard" |
|||
show-overflow-tooltip |
|||
width="180" |
|||
/> |
|||
<el-table-column |
|||
label="手机号码" |
|||
align="center" |
|||
prop="phone" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column |
|||
label="随访序号" |
|||
align="center" |
|||
prop="phone" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column |
|||
label="开始时间" |
|||
align="center" |
|||
prop="startTime" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column |
|||
label="结束时间" |
|||
align="center" |
|||
prop="endTime" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<!--待随访: 随访状态(待随访/即将超期/超期) --> |
|||
<!--已随访: 随访状态(已随访/超随访期) --> |
|||
<!--待随访: 随访状态(待随访) --> |
|||
<el-table-column |
|||
label="随访状态" |
|||
align="center" |
|||
prop="phone" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
> |
|||
<template slot-scope="scope"> |
|||
{{ status[scope.row.status] }} |
|||
</template> |
|||
</el-table-column> |
|||
<!-- <el-table-column |
|||
label="失访状态" |
|||
align="center" |
|||
prop="phone" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> --> |
|||
<el-table-column |
|||
label="失访原因" |
|||
align="center" |
|||
prop="reason" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
|
|||
<el-table-column fixed="right" label="操作" align="center" width="150"> |
|||
<template slot-scope="scope"> |
|||
<!-- :disabled="scope.row.status !== '0'" --> |
|||
<el-button |
|||
:disabled="scope.row.status !== '0'" |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-s-check" |
|||
@click="handleFollow(scope.row)" |
|||
> |
|||
随访 |
|||
</el-button> |
|||
<el-button |
|||
:disabled="scope.row.status !== '0'" |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-s-release" |
|||
@click="handleLossFollow(scope.row)" |
|||
> |
|||
失访 |
|||
</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="total > 0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
|
|||
<!-- 失访 --> |
|||
<el-dialog |
|||
class="popup" |
|||
title="失访" |
|||
:visible.sync="open" |
|||
width="780px" |
|||
append-to-body |
|||
> |
|||
<el-form |
|||
class="formStep" |
|||
ref="form" |
|||
:model="form" |
|||
:rules="rules" |
|||
label-width="90px" |
|||
> |
|||
<el-form-item label="随访次数" prop="times"> |
|||
<el-input v-model="form.times" placeholder="请输入" /> |
|||
</el-form-item> |
|||
<el-form-item label="失访原因" prop="reason"> |
|||
<el-select v-model="form.reason" placeholder="请选择"> |
|||
<el-option-group |
|||
v-for="group in options" |
|||
:key="group.label" |
|||
:label="group.label" |
|||
> |
|||
<el-option |
|||
v-for="item in group.options" |
|||
:key="item.value" |
|||
:label="item.label" |
|||
:value="item.value" |
|||
> |
|||
</el-option> |
|||
</el-option-group> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="open = false">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
<!-- 随访 --> |
|||
<el-dialog |
|||
class="popup" |
|||
title="随访" |
|||
:visible.sync="open1" |
|||
width="780px" |
|||
append-to-body |
|||
> |
|||
<el-form |
|||
class="formStep" |
|||
ref="form1" |
|||
:model="form" |
|||
:rules="rules" |
|||
label-width="90px" |
|||
> |
|||
<el-form-item label="随访人" prop="followuper"> |
|||
<el-input v-model="form.followuper" placeholder="请输入" /> |
|||
</el-form-item> |
|||
<el-form-item label="随访时间" prop="followupTime"> |
|||
<el-date-picker |
|||
format="yyyy-MM-dd HH:mm:ss" |
|||
value-format="yyyy-MM-dd HH:mm:ss" |
|||
v-model="form.followupTime" |
|||
type="datetime" |
|||
placeholder="选择日期" |
|||
> |
|||
</el-date-picker> |
|||
</el-form-item> |
|||
<el-form-item label="随访次数" prop="times"> |
|||
<el-input v-model="form.times" placeholder="请输入" /> |
|||
</el-form-item> |
|||
<el-form-item label="随访内容" prop="followupText"> |
|||
<el-input v-model="form.followupText" placeholder="请输入" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm1">确 定</el-button> |
|||
<el-button @click="open1 = false">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { queryTask, updStatus, followPatient } from "@/api/followupFile"; |
|||
export default { |
|||
name: "Notice", |
|||
data() { |
|||
return { |
|||
options: [ |
|||
{ |
|||
label: "患者原因", |
|||
options: [ |
|||
{ |
|||
value: "患者病情加重或死亡,无法参与随访", |
|||
}, |
|||
{ |
|||
value: "患者依从性差,不配合随访", |
|||
}, |
|||
{ |
|||
value: "患者主动退出或不愿继续随访", |
|||
}, |
|||
{ |
|||
value: "患者搬迁、工作变动、联系方式更换,无法联系到患者", |
|||
}, |
|||
{ |
|||
value: "信息记录不准确,错误记录患者联系方式或地址", |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
label: "医疗机构原因", |
|||
options: [ |
|||
{ |
|||
value: "随访管理不足,未及时安排随访", |
|||
}, |
|||
{ |
|||
value: "随访人员更换,管理混乱导致遗漏", |
|||
}, |
|||
{ |
|||
value: "转诊或转院", |
|||
}, |
|||
{ |
|||
value: "医疗资源受限", |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
label: "不可抗力", |
|||
options: [ |
|||
{ |
|||
value: "社会动荡或自然灾害", |
|||
}, |
|||
{ |
|||
value: "疫情或公共卫生事件", |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
activeName: "", |
|||
status: { |
|||
0: "待随访", |
|||
1: "已随访", |
|||
2: "失访", |
|||
3: "即将超期", |
|||
4: "超期未随访", |
|||
5: "超期已随访", |
|||
}, |
|||
idCardType: [ |
|||
{ |
|||
label: "身份证", |
|||
value: 0, |
|||
}, |
|||
{ |
|||
label: "护照或外国人永居证", |
|||
value: 1, |
|||
}, |
|||
{ |
|||
label: "港澳居民来往内地通行", |
|||
value: 2, |
|||
}, |
|||
{ |
|||
label: "台湾居民来往大陆通行证", |
|||
value: 3, |
|||
}, |
|||
], |
|||
idCardTypeValue: { |
|||
0: "身份证", |
|||
1: "护照或外国人永居证", |
|||
2: "港澳居民来往内地通行", |
|||
3: "台湾居民来往大陆通行证", |
|||
}, |
|||
loading: false, // 遮罩层 |
|||
ids: [], // 选中数组 |
|||
single: true, // 非单个禁用 |
|||
multiple: true, // 非多个禁用 |
|||
showSearch: true, // 显示搜索条件 |
|||
total: 0, // 总条数 |
|||
listDat: [{}], // 公告表格数据 |
|||
title: "", // 弹出层标题 |
|||
open: false, // 是否显示弹出层 |
|||
open1: false, // 是否显示弹出层 |
|||
importOpen: false, // 导入弹窗 |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
param: { |
|||
status: "0", |
|||
}, |
|||
}, |
|||
formDisabled: false, |
|||
importform: {}, |
|||
// 表单参数 |
|||
form: {}, |
|||
// 表单校验 |
|||
rules: { |
|||
followuper: [ |
|||
{ required: true, message: "随访人不能为空", trigger: "blur" }, |
|||
], |
|||
followupTime: [ |
|||
{ required: true, message: "随访时间不能为空", trigger: "change" }, |
|||
], |
|||
followupText: [ |
|||
{ required: true, message: "随访内容不能为空", trigger: "blur" }, |
|||
], |
|||
times: [ |
|||
{ required: true, message: "随访次数不能为空", trigger: "blur" }, |
|||
], |
|||
|
|||
reason: [ |
|||
{ required: true, message: "失访原因不能为空", trigger: "change" }, |
|||
], |
|||
}, |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
handleClick(tab, event) { |
|||
this.getList(); |
|||
}, |
|||
/** 查询公告列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
queryTask(this.queryParams).then((res) => { |
|||
this.listDat = res.data.list; |
|||
this.total = res.data.total; |
|||
this.loading = false; |
|||
}); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = {}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map((item) => item.id); |
|||
this.single = selection.length != 1; |
|||
this.multiple = !selection.length; |
|||
}, |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
this.reset(); |
|||
this.open = true; |
|||
}, |
|||
/** 随访 */ |
|||
handleFollow(row) { |
|||
this.open1 = true; |
|||
this.form = { |
|||
id: row.id, |
|||
followuper: "", |
|||
followupTime: "", |
|||
times: "", |
|||
followupText: "", |
|||
}; |
|||
}, |
|||
/** 失访 */ |
|||
handleLossFollow(row) { |
|||
this.open = true; |
|||
this.form = JSON.parse(JSON.stringify(row)); |
|||
this.form.status = 2; |
|||
}, |
|||
/** 提交按钮 */ |
|||
submitForm: function () { |
|||
this.$refs["form"].validate((valid) => { |
|||
if (valid) { |
|||
if (this.form.id != undefined) { |
|||
patientUpd(this.form).then((response) => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} else { |
|||
patientAdd(this.form).then((response) => { |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
/** 失访 */ |
|||
submitForm: function () { |
|||
this.$refs["form"].validate((valid) => { |
|||
if (valid) { |
|||
updStatus(this.form).then((response) => { |
|||
this.$modal.msgSuccess("操作成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
/** 随访 */ |
|||
submitForm1: function () { |
|||
this.$refs["form1"].validate((valid) => { |
|||
if (valid) { |
|||
followPatient(this.form).then((response) => { |
|||
this.$modal.msgSuccess("操作成功"); |
|||
this.open1 = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const idList = row.id ? [row.id] : this.ids; |
|||
this.$modal |
|||
.confirm("是否确认删除当前选择的数据?") |
|||
.then(function () { |
|||
return patientDel({ idList: idList }); |
|||
}) |
|||
.then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}) |
|||
.catch(() => {}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download( |
|||
"system/user/export", |
|||
{ |
|||
...this.queryParams.params, |
|||
}, |
|||
`患者档案.xlsx` |
|||
); |
|||
}, |
|||
/** 导入按钮操作 */ |
|||
handleImport() { |
|||
this.upload.title = "用户导入"; |
|||
this.upload.open = true; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
<style scoped src="@/assets/styles/common.css"></style> |
|||
<style scoped> |
|||
.form-item-age { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.form-item-age span { |
|||
margin: 0 10px; |
|||
} |
|||
.form-item-age >>> .el-input { |
|||
width: 100px; |
|||
} |
|||
</style> |
|||
<!-- >>> .el-input__inner { |
|||
padding: 0 15px !important; |
|||
} --> |
File diff suppressed because it is too large
@ -0,0 +1,251 @@ |
|||
<template> |
|||
<div class="login"> |
|||
<el-form |
|||
ref="loginForm" |
|||
:model="loginForm" |
|||
:rules="loginRules" |
|||
class="login-form" |
|||
> |
|||
<h3 class="title">针灸后台管理系统</h3> |
|||
<el-form-item prop="username"> |
|||
<el-input |
|||
v-model="loginForm.username" |
|||
type="text" |
|||
auto-complete="off" |
|||
placeholder="账号" |
|||
> |
|||
<svg-icon |
|||
slot="prefix" |
|||
icon-class="user" |
|||
class="el-input__icon input-icon" |
|||
/> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item prop="password"> |
|||
<el-input |
|||
v-model="loginForm.password" |
|||
type="password" |
|||
auto-complete="off" |
|||
placeholder="密码" |
|||
@keyup.enter.native="handleLogin" |
|||
> |
|||
<svg-icon |
|||
slot="prefix" |
|||
icon-class="password" |
|||
class="el-input__icon input-icon" |
|||
/> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item prop="code" v-if="captchaEnabled"> |
|||
<el-input |
|||
v-model="loginForm.code" |
|||
auto-complete="off" |
|||
placeholder="验证码" |
|||
style="width: 63%" |
|||
@keyup.enter.native="handleLogin" |
|||
> |
|||
<svg-icon |
|||
slot="prefix" |
|||
icon-class="validCode" |
|||
class="el-input__icon input-icon" |
|||
/> |
|||
</el-input> |
|||
<div class="login-code"> |
|||
<img :src="codeUrl" @click="getCode" class="login-code-img" /> |
|||
</div> |
|||
</el-form-item> |
|||
<el-checkbox |
|||
v-model="loginForm.rememberMe" |
|||
style="margin: 0px 0px 25px 0px" |
|||
>记住密码</el-checkbox |
|||
> |
|||
<el-form-item style="width: 100%"> |
|||
<el-button |
|||
:loading="loading" |
|||
size="medium" |
|||
type="primary" |
|||
style="width: 100%" |
|||
@click.native.prevent="handleLogin" |
|||
> |
|||
<span v-if="!loading">登 录</span> |
|||
<span v-else>登 录 中...</span> |
|||
</el-button> |
|||
<div style="float: right" v-if="register"> |
|||
<router-link class="link-type" :to="'/register'" |
|||
>立即注册</router-link |
|||
> |
|||
</div> |
|||
</el-form-item> |
|||
</el-form> |
|||
<!-- 底部 --> |
|||
<div class="el-login-footer"> |
|||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { getCodeImg } from "@/api/login"; |
|||
import Cookies from "js-cookie"; |
|||
import { encrypt, decrypt } from "@/utils/jsencrypt"; |
|||
|
|||
export default { |
|||
name: "Login", |
|||
data() { |
|||
return { |
|||
codeUrl: "", |
|||
loginForm: { |
|||
username: "admin", |
|||
password: "admin123", |
|||
rememberMe: false, |
|||
code: "", |
|||
uuid: "", |
|||
}, |
|||
loginRules: { |
|||
username: [ |
|||
{ required: true, trigger: "blur", message: "请输入您的账号" }, |
|||
], |
|||
password: [ |
|||
{ required: true, trigger: "blur", message: "请输入您的密码" }, |
|||
], |
|||
code: [{ required: true, trigger: "change", message: "请输入验证码" }], |
|||
}, |
|||
loading: false, |
|||
// 验证码开关 |
|||
captchaEnabled: true, |
|||
// 注册开关 |
|||
register: false, |
|||
redirect: undefined, |
|||
}; |
|||
}, |
|||
watch: { |
|||
$route: { |
|||
handler: function (route) { |
|||
this.redirect = route.query && route.query.redirect; |
|||
}, |
|||
immediate: true, |
|||
}, |
|||
}, |
|||
created() { |
|||
this.getCode(); |
|||
this.getCookie(); |
|||
}, |
|||
methods: { |
|||
getCode() { |
|||
getCodeImg().then((res) => { |
|||
this.captchaEnabled = |
|||
res.captchaEnabled === undefined ? true : res.captchaEnabled; |
|||
if (this.captchaEnabled) { |
|||
this.codeUrl = "data:image/gif;base64," + res.img; |
|||
this.loginForm.uuid = res.uuid; |
|||
} |
|||
}); |
|||
}, |
|||
getCookie() { |
|||
const username = Cookies.get("username"); |
|||
const password = Cookies.get("password"); |
|||
const rememberMe = Cookies.get("rememberMe"); |
|||
this.loginForm = { |
|||
username: username === undefined ? this.loginForm.username : username, |
|||
password: |
|||
password === undefined ? this.loginForm.password : decrypt(password), |
|||
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe), |
|||
}; |
|||
}, |
|||
handleLogin() { |
|||
this.$refs.loginForm.validate((valid) => { |
|||
if (valid) { |
|||
this.loading = true; |
|||
if (this.loginForm.rememberMe) { |
|||
Cookies.set("username", this.loginForm.username, { expires: 30 }); |
|||
Cookies.set("password", encrypt(this.loginForm.password), { |
|||
expires: 30, |
|||
}); |
|||
Cookies.set("rememberMe", this.loginForm.rememberMe, { |
|||
expires: 30, |
|||
}); |
|||
} else { |
|||
Cookies.remove("username"); |
|||
Cookies.remove("password"); |
|||
Cookies.remove("rememberMe"); |
|||
} |
|||
this.$store |
|||
.dispatch("Login", this.loginForm) |
|||
.then(() => { |
|||
this.$router.push({ path: this.redirect || "/" }).catch(() => {}); |
|||
}) |
|||
.catch(() => { |
|||
this.loading = false; |
|||
if (this.captchaEnabled) { |
|||
this.getCode(); |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss"> |
|||
.login { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 100%; |
|||
background-image: url("../assets/images/login-background.jpg"); |
|||
background-size: cover; |
|||
} |
|||
.title { |
|||
margin: 0px auto 30px auto; |
|||
text-align: center; |
|||
color: #707070; |
|||
} |
|||
|
|||
.login-form { |
|||
border-radius: 6px; |
|||
background: #ffffff; |
|||
width: 400px; |
|||
padding: 25px 25px 5px 25px; |
|||
.el-input { |
|||
height: 38px; |
|||
input { |
|||
height: 38px; |
|||
} |
|||
} |
|||
.input-icon { |
|||
height: 39px; |
|||
width: 14px; |
|||
margin-left: 2px; |
|||
} |
|||
} |
|||
.login-tip { |
|||
font-size: 13px; |
|||
text-align: center; |
|||
color: #bfbfbf; |
|||
} |
|||
.login-code { |
|||
width: 33%; |
|||
height: 38px; |
|||
float: right; |
|||
img { |
|||
cursor: pointer; |
|||
vertical-align: middle; |
|||
} |
|||
} |
|||
.el-login-footer { |
|||
height: 40px; |
|||
line-height: 40px; |
|||
position: fixed; |
|||
bottom: 0; |
|||
width: 100%; |
|||
text-align: center; |
|||
color: #fff; |
|||
font-family: Arial; |
|||
font-size: 12px; |
|||
letter-spacing: 1px; |
|||
} |
|||
.login-code-img { |
|||
height: 38px; |
|||
} |
|||
</style> |
File diff suppressed because it is too large
@ -0,0 +1,513 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> |
|||
<el-form-item label="任务名称" prop="jobName"> |
|||
<el-input |
|||
v-model="queryParams.jobName" |
|||
placeholder="请输入任务名称" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="任务组名" prop="jobGroup"> |
|||
<el-select v-model="queryParams.jobGroup" placeholder="请选择任务组名" clearable> |
|||
<el-option |
|||
v-for="dict in dict.type.sys_job_group" |
|||
:key="dict.value" |
|||
:label="dict.label" |
|||
:value="dict.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="任务状态" prop="status"> |
|||
<el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable> |
|||
<el-option |
|||
v-for="dict in dict.type.sys_job_status" |
|||
:key="dict.value" |
|||
:label="dict.label" |
|||
:value="dict.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="primary" |
|||
plain |
|||
icon="el-icon-plus" |
|||
size="mini" |
|||
@click="handleAdd" |
|||
v-hasPermi="['monitor:job:add']" |
|||
>新增</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="success" |
|||
plain |
|||
icon="el-icon-edit" |
|||
size="mini" |
|||
:disabled="single" |
|||
@click="handleUpdate" |
|||
v-hasPermi="['monitor:job:edit']" |
|||
>修改</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="danger" |
|||
plain |
|||
icon="el-icon-delete" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleDelete" |
|||
v-hasPermi="['monitor:job:remove']" |
|||
>删除</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="warning" |
|||
plain |
|||
icon="el-icon-download" |
|||
size="mini" |
|||
@click="handleExport" |
|||
v-hasPermi="['monitor:job:export']" |
|||
>导出</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="info" |
|||
plain |
|||
icon="el-icon-s-operation" |
|||
size="mini" |
|||
@click="handleJobLog" |
|||
v-hasPermi="['monitor:job:query']" |
|||
>日志</el-button> |
|||
</el-col> |
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange"> |
|||
<el-table-column type="selection" width="55" align="center" /> |
|||
<el-table-column label="任务编号" width="100" align="center" prop="jobId" /> |
|||
<el-table-column label="任务名称" align="center" prop="jobName" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="任务组名" align="center" prop="jobGroup"> |
|||
<template slot-scope="scope"> |
|||
<dict-tag :options="dict.type.sys_job_group" :value="scope.row.jobGroup"/> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="调用目标字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="cron执行表达式" align="center" prop="cronExpression" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="状态" align="center"> |
|||
<template slot-scope="scope"> |
|||
<el-switch |
|||
v-model="scope.row.status" |
|||
active-value="0" |
|||
inactive-value="1" |
|||
@change="handleStatusChange(scope.row)" |
|||
></el-switch> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template slot-scope="scope"> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-edit" |
|||
@click="handleUpdate(scope.row)" |
|||
v-hasPermi="['monitor:job:edit']" |
|||
>修改</el-button> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-delete" |
|||
@click="handleDelete(scope.row)" |
|||
v-hasPermi="['monitor:job:remove']" |
|||
>删除</el-button> |
|||
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['monitor:job:changeStatus', 'monitor:job:query']"> |
|||
<el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button> |
|||
<el-dropdown-menu slot="dropdown"> |
|||
<el-dropdown-item command="handleRun" icon="el-icon-caret-right" |
|||
v-hasPermi="['monitor:job:changeStatus']">执行一次</el-dropdown-item> |
|||
<el-dropdown-item command="handleView" icon="el-icon-view" |
|||
v-hasPermi="['monitor:job:query']">任务详细</el-dropdown-item> |
|||
<el-dropdown-item command="handleJobLog" icon="el-icon-s-operation" |
|||
v-hasPermi="['monitor:job:query']">调度日志</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</el-dropdown> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="total>0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
|
|||
<!-- 添加或修改定时任务对话框 --> |
|||
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body> |
|||
<el-form ref="form" :model="form" :rules="rules" label-width="120px"> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item label="任务名称" prop="jobName"> |
|||
<el-input v-model="form.jobName" placeholder="请输入任务名称" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="任务分组" prop="jobGroup"> |
|||
<el-select v-model="form.jobGroup" placeholder="请选择任务分组"> |
|||
<el-option |
|||
v-for="dict in dict.type.sys_job_group" |
|||
:key="dict.value" |
|||
:label="dict.label" |
|||
:value="dict.value" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="24"> |
|||
<el-form-item prop="invokeTarget"> |
|||
<span slot="label"> |
|||
调用方法 |
|||
<el-tooltip placement="top"> |
|||
<div slot="content"> |
|||
Bean调用示例:ryTask.ryParams('ry') |
|||
<br />Class类调用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry') |
|||
<br />参数说明:支持字符串,布尔类型,长整型,浮点型,整型 |
|||
</div> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="24"> |
|||
<el-form-item label="cron表达式" prop="cronExpression"> |
|||
<el-input v-model="form.cronExpression" placeholder="请输入cron执行表达式"> |
|||
<template slot="append"> |
|||
<el-button type="primary" @click="handleShowCron"> |
|||
生成表达式 |
|||
<i class="el-icon-time el-icon--right"></i> |
|||
</el-button> |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="24" v-if="form.jobId !== undefined"> |
|||
<el-form-item label="状态"> |
|||
<el-radio-group v-model="form.status"> |
|||
<el-radio |
|||
v-for="dict in dict.type.sys_job_status" |
|||
:key="dict.value" |
|||
:label="dict.value" |
|||
>{{dict.label}}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="执行策略" prop="misfirePolicy"> |
|||
<el-radio-group v-model="form.misfirePolicy" size="small"> |
|||
<el-radio-button label="1">立即执行</el-radio-button> |
|||
<el-radio-button label="2">执行一次</el-radio-button> |
|||
<el-radio-button label="3">放弃执行</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="是否并发" prop="concurrent"> |
|||
<el-radio-group v-model="form.concurrent" size="small"> |
|||
<el-radio-button label="0">允许</el-radio-button> |
|||
<el-radio-button label="1">禁止</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="cancel">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
|
|||
<el-dialog title="Cron表达式生成器" :visible.sync="openCron" append-to-body destroy-on-close class="scrollbar"> |
|||
<crontab @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab> |
|||
</el-dialog> |
|||
|
|||
<!-- 任务日志详细 --> |
|||
<el-dialog title="任务详细" :visible.sync="openView" width="700px" append-to-body> |
|||
<el-form ref="form" :model="form" label-width="120px" size="mini"> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item label="任务编号:">{{ form.jobId }}</el-form-item> |
|||
<el-form-item label="任务名称:">{{ form.jobName }}</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="任务分组:">{{ jobGroupFormat(form) }}</el-form-item> |
|||
<el-form-item label="创建时间:">{{ form.createTime }}</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="cron表达式:">{{ form.cronExpression }}</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="下次执行时间:">{{ parseTime(form.nextValidTime) }}</el-form-item> |
|||
</el-col> |
|||
<el-col :span="24"> |
|||
<el-form-item label="调用目标方法:">{{ form.invokeTarget }}</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="任务状态:"> |
|||
<div v-if="form.status == 0">正常</div> |
|||
<div v-else-if="form.status == 1">暂停</div> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="是否并发:"> |
|||
<div v-if="form.concurrent == 0">允许</div> |
|||
<div v-else-if="form.concurrent == 1">禁止</div> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="执行策略:"> |
|||
<div v-if="form.misfirePolicy == 0">默认策略</div> |
|||
<div v-else-if="form.misfirePolicy == 1">立即执行</div> |
|||
<div v-else-if="form.misfirePolicy == 2">执行一次</div> |
|||
<div v-else-if="form.misfirePolicy == 3">放弃执行</div> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button @click="openView = false">关 闭</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job"; |
|||
import Crontab from '@/components/Crontab' |
|||
|
|||
export default { |
|||
components: { Crontab }, |
|||
name: "Job", |
|||
dicts: ['sys_job_group', 'sys_job_status'], |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 选中数组 |
|||
ids: [], |
|||
// 非单个禁用 |
|||
single: true, |
|||
// 非多个禁用 |
|||
multiple: true, |
|||
// 显示搜索条件 |
|||
showSearch: true, |
|||
// 总条数 |
|||
total: 0, |
|||
// 定时任务表格数据 |
|||
jobList: [], |
|||
// 弹出层标题 |
|||
title: "", |
|||
// 是否显示弹出层 |
|||
open: false, |
|||
// 是否显示详细弹出层 |
|||
openView: false, |
|||
// 是否显示Cron表达式弹出层 |
|||
openCron: false, |
|||
// 传入的表达式 |
|||
expression: "", |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
jobName: undefined, |
|||
jobGroup: undefined, |
|||
status: undefined |
|||
}, |
|||
// 表单参数 |
|||
form: {}, |
|||
// 表单校验 |
|||
rules: { |
|||
jobName: [ |
|||
{ required: true, message: "任务名称不能为空", trigger: "blur" } |
|||
], |
|||
invokeTarget: [ |
|||
{ required: true, message: "调用目标字符串不能为空", trigger: "blur" } |
|||
], |
|||
cronExpression: [ |
|||
{ required: true, message: "cron执行表达式不能为空", trigger: "blur" } |
|||
] |
|||
} |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
/** 查询定时任务列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
listJob(this.queryParams).then(response => { |
|||
this.jobList = response.rows; |
|||
this.total = response.total; |
|||
this.loading = false; |
|||
}); |
|||
}, |
|||
// 任务组名字典翻译 |
|||
jobGroupFormat(row, column) { |
|||
return this.selectDictLabel(this.dict.type.sys_job_group, row.jobGroup); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = { |
|||
jobId: undefined, |
|||
jobName: undefined, |
|||
jobGroup: undefined, |
|||
invokeTarget: undefined, |
|||
cronExpression: undefined, |
|||
misfirePolicy: 1, |
|||
concurrent: 1, |
|||
status: "0" |
|||
}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map(item => item.jobId); |
|||
this.single = selection.length != 1; |
|||
this.multiple = !selection.length; |
|||
}, |
|||
// 更多操作触发 |
|||
handleCommand(command, row) { |
|||
switch (command) { |
|||
case "handleRun": |
|||
this.handleRun(row); |
|||
break; |
|||
case "handleView": |
|||
this.handleView(row); |
|||
break; |
|||
case "handleJobLog": |
|||
this.handleJobLog(row); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
}, |
|||
// 任务状态修改 |
|||
handleStatusChange(row) { |
|||
let text = row.status === "0" ? "启用" : "停用"; |
|||
this.$modal.confirm('确认要"' + text + '""' + row.jobName + '"任务吗?').then(function() { |
|||
return changeJobStatus(row.jobId, row.status); |
|||
}).then(() => { |
|||
this.$modal.msgSuccess(text + "成功"); |
|||
}).catch(function() { |
|||
row.status = row.status === "0" ? "1" : "0"; |
|||
}); |
|||
}, |
|||
/* 立即执行一次 */ |
|||
handleRun(row) { |
|||
this.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function() { |
|||
return runJob(row.jobId, row.jobGroup); |
|||
}).then(() => { |
|||
this.$modal.msgSuccess("执行成功"); |
|||
}).catch(() => {}); |
|||
}, |
|||
/** 任务详细信息 */ |
|||
handleView(row) { |
|||
getJob(row.jobId).then(response => { |
|||
this.form = response.data; |
|||
this.openView = true; |
|||
}); |
|||
}, |
|||
/** cron表达式按钮操作 */ |
|||
handleShowCron() { |
|||
this.expression = this.form.cronExpression; |
|||
this.openCron = true; |
|||
}, |
|||
/** 确定后回传值 */ |
|||
crontabFill(value) { |
|||
this.form.cronExpression = value; |
|||
}, |
|||
/** 任务日志列表查询 */ |
|||
handleJobLog(row) { |
|||
const jobId = row.jobId || 0; |
|||
this.$router.push('/monitor/job-log/index/' + jobId) |
|||
}, |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
this.reset(); |
|||
this.open = true; |
|||
this.title = "添加任务"; |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleUpdate(row) { |
|||
this.reset(); |
|||
const jobId = row.jobId || this.ids; |
|||
getJob(jobId).then(response => { |
|||
this.form = response.data; |
|||
this.open = true; |
|||
this.title = "修改任务"; |
|||
}); |
|||
}, |
|||
/** 提交按钮 */ |
|||
submitForm: function() { |
|||
this.$refs["form"].validate(valid => { |
|||
if (valid) { |
|||
if (this.form.jobId != undefined) { |
|||
updateJob(this.form).then(response => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} else { |
|||
addJob(this.form).then(response => { |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const jobIds = row.jobId || this.ids; |
|||
this.$modal.confirm('是否确认删除定时任务编号为"' + jobIds + '"的数据项?').then(function() { |
|||
return delJob(jobIds); |
|||
}).then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}).catch(() => {}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download('monitor/job/export', { |
|||
...this.queryParams |
|||
}, `job_${new Date().getTime()}.xlsx`) |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,699 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form |
|||
:model="queryParams" |
|||
ref="queryForm" |
|||
size="small" |
|||
:inline="true" |
|||
v-show="showSearch" |
|||
label-width="68px" |
|||
> |
|||
<el-form-item label="" prop="noticeTitle"> |
|||
<el-input |
|||
v-model="queryParams.param.keywords" |
|||
placeholder="支持姓名、全拼、简拼、手机号吗、证件号码" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="年龄范围" prop="createBy"> |
|||
<div class="form-item-age"> |
|||
<el-input |
|||
v-model="queryParams.param.startAge" |
|||
placeholder="最小年龄" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
<span>—</span> |
|||
<el-input |
|||
v-model="queryParams.param.endAge" |
|||
placeholder="最大年龄" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</div> |
|||
</el-form-item> |
|||
<el-form-item label="建档组织" prop="tenantId"> |
|||
<el-select |
|||
v-model="queryParams.param.tenantId" |
|||
placeholder="请选择" |
|||
clearable |
|||
> |
|||
<el-option label="dict.label" value="dict.value" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="数据来源" prop="sourceId"> |
|||
<el-select |
|||
v-model="queryParams.param.sourceId" |
|||
placeholder="请选择" |
|||
clearable |
|||
> |
|||
<el-option label="筛查" :value="0" /> |
|||
<el-option label="录入" :value="1" /> |
|||
<el-option label="HIS" :value="2" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button |
|||
type="primary" |
|||
icon="el-icon-search" |
|||
size="mini" |
|||
@click="handleQuery" |
|||
>搜索</el-button |
|||
> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"> |
|||
重置 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="primary" |
|||
plain |
|||
icon="el-icon-plus" |
|||
size="mini" |
|||
@click="handleAdd" |
|||
>新增</el-button |
|||
> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="danger" |
|||
plain |
|||
icon="el-icon-delete" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleDelete" |
|||
>删除</el-button |
|||
> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="info" |
|||
plain |
|||
icon="el-icon-bottom" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleDelete" |
|||
>下载模版</el-button |
|||
> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="warning" |
|||
plain |
|||
icon="el-icon-upload2" |
|||
size="mini" |
|||
@click="handleImport" |
|||
>导入</el-button |
|||
> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="warning" |
|||
plain |
|||
icon="el-icon-download" |
|||
size="mini" |
|||
@click="handleExport" |
|||
>导出</el-button |
|||
> |
|||
</el-col> |
|||
<right-toolbar |
|||
:showSearch.sync="showSearch" |
|||
@queryTable="getList" |
|||
></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table |
|||
v-loading="loading" |
|||
:data="listDat" |
|||
@selection-change="handleSelectionChange" |
|||
max-height="600" |
|||
> |
|||
<el-table-column type="selection" width="55" align="center" /> |
|||
<el-table-column label="姓名" align="center" prop="name" width="100" /> |
|||
<el-table-column |
|||
label="性别" |
|||
align="center" |
|||
prop="gender" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.gender == 0">男</span> |
|||
<span v-if="scope.row.gender == 1">女</span> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column |
|||
label="出生日期" |
|||
align="center" |
|||
prop="birthDate" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column |
|||
label="民族" |
|||
align="center" |
|||
prop="ethnicity" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="受教育年限" |
|||
align="center" |
|||
prop="educationYears" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="手机号码" |
|||
align="center" |
|||
prop="phone" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column |
|||
label="证件类型" |
|||
align="center" |
|||
prop="idCardType" |
|||
show-overflow-tooltip |
|||
width="200" |
|||
> |
|||
<template slot-scope="scope"> |
|||
{{ idCardTypeValue[scope.row.idCardType] }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="证件号码" |
|||
align="center" |
|||
prop="idCard" |
|||
show-overflow-tooltip |
|||
width="180" |
|||
/> |
|||
|
|||
<el-table-column |
|||
label="建档人" |
|||
align="center" |
|||
prop="createBy" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
/> |
|||
<el-table-column |
|||
label="建档组织(医院名称)" |
|||
align="center" |
|||
prop="organization" |
|||
show-overflow-tooltip |
|||
width="150" |
|||
/> |
|||
<el-table-column label="建档日期" align="center" width="130"> |
|||
<template slot-scope="scope"> |
|||
<span>{{ |
|||
parseTime(scope.row.createTime, "{y}-{m}-{d} {h}:{i}") |
|||
}}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
fixed="right" |
|||
label="来源" |
|||
align="center" |
|||
prop="source" |
|||
show-overflow-tooltip |
|||
width="100" |
|||
> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.gender == 0">筛查</span> |
|||
<span v-if="scope.row.gender == 1">录入</span> |
|||
<span v-if="scope.row.gender == 2">HIS</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column fixed="right" label="操作" align="center" width="150"> |
|||
<template slot-scope="scope"> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-edit" |
|||
@click="handleUpdate(scope.row)" |
|||
>修改</el-button |
|||
> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-delete" |
|||
@click="handleDelete(scope.row)" |
|||
>删除</el-button |
|||
> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-tickets" |
|||
@click="handleDetails(scope.row)" |
|||
>详情</el-button |
|||
> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-notebook-2" |
|||
@click="handleDelete(scope.row)" |
|||
>诊疗档案</el-button |
|||
> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="total > 0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
|
|||
<!-- 添加或修改公告对话框 --> |
|||
<el-dialog |
|||
class="popup" |
|||
:title="title" |
|||
:visible.sync="open" |
|||
width="780px" |
|||
append-to-body |
|||
> |
|||
<el-form |
|||
class="formStep" |
|||
ref="form" |
|||
:model="form" |
|||
:rules="rules" |
|||
label-width="90px" |
|||
> |
|||
<el-form-item label="姓名" prop="name"> |
|||
<el-input |
|||
v-model="form.name" |
|||
:disabled="formDisabled" |
|||
placeholder="请输入" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="gender"> |
|||
<el-radio-group v-model="form.gender" :disabled="formDisabled"> |
|||
<el-radio :label="0">男</el-radio> |
|||
<el-radio :label="1">女</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="出生年月" prop="birthDate"> |
|||
<el-date-picker |
|||
:disabled="formDisabled" |
|||
format="yyyy-MM-dd" |
|||
value-format="yyyy-MM-dd" |
|||
v-model="form.birthDate" |
|||
type="date" |
|||
placeholder="选择日期" |
|||
> |
|||
</el-date-picker> |
|||
</el-form-item> |
|||
<el-form-item label="民族" prop="ethnicity"> |
|||
<el-input |
|||
v-model="form.ethnicity" |
|||
placeholder="请输入" |
|||
:disabled="formDisabled" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="受教育年限" prop="educationYears"> |
|||
<el-input |
|||
v-model="form.educationYears" |
|||
placeholder="请输入" |
|||
:disabled="formDisabled" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="手机号码" prop="phone"> |
|||
<el-input |
|||
v-model="form.phone" |
|||
placeholder="请输入" |
|||
:disabled="formDisabled" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="证件类型" prop="idCardType"> |
|||
<el-select |
|||
v-model="form.idCardType" |
|||
placeholder="请选择" |
|||
:disabled="formDisabled" |
|||
> |
|||
<el-option |
|||
v-for="item in idCardType" |
|||
:key="item.value" |
|||
:label="item.label" |
|||
:value="item.value" |
|||
> |
|||
</el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="证件号码" prop="idCard"> |
|||
<el-input |
|||
v-model="form.idCard" |
|||
placeholder="请输入" |
|||
:disabled="formDisabled" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="现病史" prop="currentIllnessHistory"> |
|||
<el-checkbox-group |
|||
v-model="form.currentIllnessHistory" |
|||
:disabled="formDisabled" |
|||
> |
|||
<el-checkbox v-for="(item, index) in medicalHistory" :label="item"> |
|||
</el-checkbox> |
|||
</el-checkbox-group> |
|||
<el-input |
|||
v-model="form.currentIllnessHistoryQT" |
|||
placeholder="其他" |
|||
:disabled="formDisabled" |
|||
/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="cancel">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
<!-- 导入患者信息 --> |
|||
<el-dialog |
|||
title="导入患者档案" |
|||
:visible.sync="importOpen" |
|||
width="640px" |
|||
append-to-body |
|||
> |
|||
<el-form ref="form" :model="importform"> |
|||
<el-form-item prop="accessUrl"> |
|||
<el-upload |
|||
:limit="1" |
|||
class="avatar-uploader wj-uploader" |
|||
:headers="headers" |
|||
:action="uploadFileUrl1" |
|||
accept=".xlsx, .xls" |
|||
:before-upload="handleBeforePdfUpload1" |
|||
:on-success="handleUploadPdfAdd1" |
|||
:file-list="fileList" |
|||
:show-file-list="true" |
|||
> |
|||
<i class="el-icon-upload"></i> |
|||
<div class="el-upload__text"> |
|||
将文件拖到此处,或 |
|||
<em>点击上传</em> |
|||
</div> |
|||
</el-upload> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { getToken } from "@/utils/auth"; |
|||
import { |
|||
queryPatient, |
|||
patientAdd, |
|||
patientUpd, |
|||
patientDel, |
|||
} from "@/api/patientFile"; |
|||
export default { |
|||
name: "Notice", |
|||
dicts: ["sys_notice_status", "sys_notice_type"], |
|||
data() { |
|||
return { |
|||
headers: { |
|||
Authorization: "Bearer " + getToken(), |
|||
deptId: localStorage.getItem("hospitalId"), |
|||
}, |
|||
uploadFileUrl1: process.env.VUE_APP_BASE_API + "/pms/importTjbgZip", // 上传的图片服务器地址 |
|||
fileList: [], |
|||
// 现病史 |
|||
medicalHistory: [ |
|||
"高血压", |
|||
"脑血管病", |
|||
"恶性肿瘤", |
|||
"冠心病", |
|||
"精神疾病", |
|||
"胃和十二指肠溃疡", |
|||
"肥胖症", |
|||
"骨质疏松症", |
|||
"遗传性、先天性疾病", |
|||
"糖尿病", |
|||
"慢性肺系疾病", |
|||
"高脂血症", |
|||
"肝脏疾病(脂肪肝、乙型肝炎、肝硬化等)", |
|||
"过敏性疾病", |
|||
"关节炎", |
|||
"痛风", |
|||
"肾炎、肾病", |
|||
"高脂血症", |
|||
], |
|||
idCardType: [ |
|||
{ |
|||
label: "身份证", |
|||
value: 0, |
|||
}, |
|||
{ |
|||
label: "护照或外国人永居证", |
|||
value: 1, |
|||
}, |
|||
{ |
|||
label: "港澳居民来往内地通行", |
|||
value: 2, |
|||
}, |
|||
{ |
|||
label: "台湾居民来往大陆通行证", |
|||
value: 3, |
|||
}, |
|||
], |
|||
idCardTypeValue: { |
|||
0: "身份证", |
|||
1: "护照或外国人永居证", |
|||
2: "港澳居民来往内地通行", |
|||
3: "台湾居民来往大陆通行证", |
|||
}, |
|||
loading: false, // 遮罩层 |
|||
ids: [], // 选中数组 |
|||
single: true, // 非单个禁用 |
|||
multiple: true, // 非多个禁用 |
|||
showSearch: true, // 显示搜索条件 |
|||
total: 0, // 总条数 |
|||
listDat: [{}], // 公告表格数据 |
|||
title: "", // 弹出层标题 |
|||
open: false, // 是否显示弹出层 |
|||
importOpen: false, // 导入弹窗 |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
param: { |
|||
keywords: "", //关键字 |
|||
startAge: "", //开始年龄 |
|||
endAge: "", //结束年龄 |
|||
tenantId: "", //建档组织 |
|||
createBy: "", //建档人 |
|||
sourceId: "", //来源 |
|||
}, |
|||
}, |
|||
formDisabled: false, |
|||
importform: {}, |
|||
// 表单参数 |
|||
form: {}, |
|||
// 表单校验 |
|||
rules: { |
|||
name: [ |
|||
{ required: true, message: "患者姓名不能为空", trigger: "blur" }, |
|||
], |
|||
gender: [ |
|||
{ required: true, message: "性别不能为空", trigger: "change" }, |
|||
], |
|||
phone: [ |
|||
{ required: true, message: "手机号码不能为空", trigger: "blur" }, |
|||
], |
|||
ethnicity: [ |
|||
{ required: true, message: "民族不能为空", trigger: "blur" }, |
|||
], |
|||
idCardType: [ |
|||
{ required: true, message: "证件类型不能为空", trigger: "blur" }, |
|||
], |
|||
idCard: [ |
|||
{ required: true, message: "证件号码不能为空", trigger: "blur" }, |
|||
], |
|||
currentIllnessHistory: [ |
|||
{ |
|||
required: true, |
|||
message: "现病史不能为空", |
|||
trigger: "blur", |
|||
}, |
|||
], |
|||
}, |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
// 上传成功回 - pdg |
|||
handleUploadPdfAdd1(res) { |
|||
if (res.code == 200) { |
|||
this.physicaOpen = false; |
|||
this.getList(); |
|||
this.$modal.msgSuccess("导入成功"); |
|||
} else { |
|||
this.$message.error(res.msg || "导入失败"); |
|||
this.fileList = []; |
|||
} |
|||
}, |
|||
// 上传前校检格式和大小 - 图片 |
|||
handleBeforeUpload1(file) { |
|||
const isLt2M = file.size / 1024 / 1024 < 100; |
|||
// 校检文件大小 |
|||
if (!isLt2M) { |
|||
this.$message.error("上传文件大小不能超过 100MB!"); |
|||
} |
|||
return isLt2M; |
|||
}, |
|||
// 上传前校检格式和大小 - 文件 |
|||
handleBeforePdfUpload1(file) { |
|||
const fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1); |
|||
const whiteList = ["xlsx", "xls"]; |
|||
if (whiteList.indexOf(fileSuffix) === -1) { |
|||
this.$message.error("上传文件只能是.xlsx, .xls"); |
|||
return false; |
|||
} |
|||
}, |
|||
/** 查询公告列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
queryPatient(this.queryParams).then((res) => { |
|||
this.listDat = res.data.list; |
|||
this.total = res.data.total; |
|||
this.loading = false; |
|||
}); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = { |
|||
name: "", // 姓名 |
|||
gender: 0, // 性别 |
|||
birthDate: "", // 出生日期 |
|||
ethnicity: "", // 民族 |
|||
educationYears: "", // 教育程度 |
|||
phone: "", // 联系电话 |
|||
idCardType: "", // 证件类型 |
|||
idCard: "", // 证件号码 |
|||
currentIllnessHistory: [], // 现病史 |
|||
currentIllnessHistoryQT: "", // 现病史 |
|||
}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map((item) => item.id); |
|||
this.single = selection.length != 1; |
|||
this.multiple = !selection.length; |
|||
}, |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
this.reset(); |
|||
this.open = true; |
|||
this.title = "新增患者档案"; |
|||
this.formDisabled = false; |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleUpdate(row) { |
|||
this.open = true; |
|||
this.title = "修改患者档案"; |
|||
this.formDisabled = false; |
|||
this.form = JSON.parse(JSON.stringify(row)); |
|||
// 字符串转数组 |
|||
this.form.currentIllnessHistory = |
|||
this.form.currentIllnessHistory.split(","); |
|||
}, |
|||
/** 详情按钮操作 */ |
|||
handleDetails(row) { |
|||
this.open = true; |
|||
this.title = "患者档案详情"; |
|||
this.formDisabled = true; |
|||
this.form = JSON.parse(JSON.stringify(row)); |
|||
}, |
|||
/** 提交按钮 */ |
|||
submitForm: function () { |
|||
this.$refs["form"].validate((valid) => { |
|||
if (valid) { |
|||
if (this.form.id != undefined) { |
|||
patientUpd(this.form).then((response) => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} else { |
|||
patientAdd(this.form).then((response) => { |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const idList = row.id ? [row.id] : this.ids; |
|||
this.$modal |
|||
.confirm("是否确认删除当前选择的数据?") |
|||
.then(function () { |
|||
return patientDel({ idList: idList }); |
|||
}) |
|||
.then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}) |
|||
.catch(() => {}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download( |
|||
"system/user/export", |
|||
{ |
|||
...this.queryParams.params, |
|||
}, |
|||
`患者档案.xlsx` |
|||
); |
|||
}, |
|||
/** 导入按钮操作 */ |
|||
handleImport() { |
|||
this.upload.title = "用户导入"; |
|||
this.upload.open = true; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
<style scoped src="@/assets/styles/common.css"></style> |
|||
<style scoped> |
|||
.form-item-age { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.form-item-age span { |
|||
margin: 0 10px; |
|||
} |
|||
.form-item-age >>> .el-input { |
|||
width: 100px; |
|||
} |
|||
</style> |
|||
<!-- >>> .el-input__inner { |
|||
padding: 0 15px !important; |
|||
} --> |
@ -0,0 +1,263 @@ |
|||
<template> |
|||
<div class="register"> |
|||
<el-form |
|||
ref="registerForm" |
|||
:model="registerForm" |
|||
:rules="registerRules" |
|||
class="register-form" |
|||
> |
|||
<h3 class="title">针灸后台管理系统</h3> |
|||
<el-form-item prop="username"> |
|||
<el-input |
|||
v-model="registerForm.username" |
|||
type="text" |
|||
auto-complete="off" |
|||
placeholder="账号" |
|||
> |
|||
<svg-icon |
|||
slot="prefix" |
|||
icon-class="user" |
|||
class="el-input__icon input-icon" |
|||
/> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item prop="password"> |
|||
<el-input |
|||
v-model="registerForm.password" |
|||
type="password" |
|||
auto-complete="off" |
|||
placeholder="密码" |
|||
@keyup.enter.native="handleRegister" |
|||
> |
|||
<svg-icon |
|||
slot="prefix" |
|||
icon-class="password" |
|||
class="el-input__icon input-icon" |
|||
/> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item prop="confirmPassword"> |
|||
<el-input |
|||
v-model="registerForm.confirmPassword" |
|||
type="password" |
|||
auto-complete="off" |
|||
placeholder="确认密码" |
|||
@keyup.enter.native="handleRegister" |
|||
> |
|||
<svg-icon |
|||
slot="prefix" |
|||
icon-class="password" |
|||
class="el-input__icon input-icon" |
|||
/> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item prop="code" v-if="captchaEnabled"> |
|||
<el-input |
|||
v-model="registerForm.code" |
|||
auto-complete="off" |
|||
placeholder="验证码" |
|||
style="width: 63%" |
|||
@keyup.enter.native="handleRegister" |
|||
> |
|||
<svg-icon |
|||
slot="prefix" |
|||
icon-class="validCode" |
|||
class="el-input__icon input-icon" |
|||
/> |
|||
</el-input> |
|||
<div class="register-code"> |
|||
<img :src="codeUrl" @click="getCode" class="register-code-img" /> |
|||
</div> |
|||
</el-form-item> |
|||
<el-form-item style="width: 100%"> |
|||
<el-button |
|||
:loading="loading" |
|||
size="medium" |
|||
type="primary" |
|||
style="width: 100%" |
|||
@click.native.prevent="handleRegister" |
|||
> |
|||
<span v-if="!loading">注 册</span> |
|||
<span v-else>注 册 中...</span> |
|||
</el-button> |
|||
<div style="float: right"> |
|||
<router-link class="link-type" :to="'/login'" |
|||
>使用已有账户登录</router-link |
|||
> |
|||
</div> |
|||
</el-form-item> |
|||
</el-form> |
|||
<!-- 底部 --> |
|||
<div class="el-register-footer"> |
|||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { getCodeImg, register } from "@/api/login"; |
|||
|
|||
export default { |
|||
name: "Register", |
|||
data() { |
|||
const equalToPassword = (rule, value, callback) => { |
|||
if (this.registerForm.password !== value) { |
|||
callback(new Error("两次输入的密码不一致")); |
|||
} else { |
|||
callback(); |
|||
} |
|||
}; |
|||
return { |
|||
codeUrl: "", |
|||
registerForm: { |
|||
username: "", |
|||
password: "", |
|||
confirmPassword: "", |
|||
code: "", |
|||
uuid: "", |
|||
}, |
|||
registerRules: { |
|||
username: [ |
|||
{ required: true, trigger: "blur", message: "请输入您的账号" }, |
|||
{ |
|||
min: 2, |
|||
max: 20, |
|||
message: "用户账号长度必须介于 2 和 20 之间", |
|||
trigger: "blur", |
|||
}, |
|||
], |
|||
password: [ |
|||
{ required: true, trigger: "blur", message: "请输入您的密码" }, |
|||
{ |
|||
min: 5, |
|||
max: 20, |
|||
message: "用户密码长度必须介于 5 和 20 之间", |
|||
trigger: "blur", |
|||
}, |
|||
{ |
|||
pattern: /^[^<>"'|\\]+$/, |
|||
message: "不能包含非法字符:< > \" ' \\\ |", |
|||
trigger: "blur", |
|||
}, |
|||
], |
|||
confirmPassword: [ |
|||
{ required: true, trigger: "blur", message: "请再次输入您的密码" }, |
|||
{ required: true, validator: equalToPassword, trigger: "blur" }, |
|||
], |
|||
code: [{ required: true, trigger: "change", message: "请输入验证码" }], |
|||
}, |
|||
loading: false, |
|||
captchaEnabled: true, |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getCode(); |
|||
}, |
|||
methods: { |
|||
getCode() { |
|||
getCodeImg().then((res) => { |
|||
this.captchaEnabled = |
|||
res.captchaEnabled === undefined ? true : res.captchaEnabled; |
|||
if (this.captchaEnabled) { |
|||
this.codeUrl = "data:image/gif;base64," + res.img; |
|||
this.registerForm.uuid = res.uuid; |
|||
} |
|||
}); |
|||
}, |
|||
handleRegister() { |
|||
this.$refs.registerForm.validate((valid) => { |
|||
if (valid) { |
|||
this.loading = true; |
|||
register(this.registerForm) |
|||
.then((res) => { |
|||
const username = this.registerForm.username; |
|||
this.$alert( |
|||
"<font color='red'>恭喜你,您的账号 " + |
|||
username + |
|||
" 注册成功!</font>", |
|||
"系统提示", |
|||
{ |
|||
dangerouslyUseHTMLString: true, |
|||
type: "success", |
|||
} |
|||
) |
|||
.then(() => { |
|||
this.$router.push("/login"); |
|||
}) |
|||
.catch(() => {}); |
|||
}) |
|||
.catch(() => { |
|||
this.loading = false; |
|||
if (this.captchaEnabled) { |
|||
this.getCode(); |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss"> |
|||
.register { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 100%; |
|||
background-image: url("../assets/images/login-background.jpg"); |
|||
background-size: cover; |
|||
} |
|||
.title { |
|||
margin: 0px auto 30px auto; |
|||
text-align: center; |
|||
color: #707070; |
|||
} |
|||
|
|||
.register-form { |
|||
border-radius: 6px; |
|||
background: #ffffff; |
|||
width: 400px; |
|||
padding: 25px 25px 5px 25px; |
|||
.el-input { |
|||
height: 38px; |
|||
input { |
|||
height: 38px; |
|||
} |
|||
} |
|||
.input-icon { |
|||
height: 39px; |
|||
width: 14px; |
|||
margin-left: 2px; |
|||
} |
|||
} |
|||
.register-tip { |
|||
font-size: 13px; |
|||
text-align: center; |
|||
color: #bfbfbf; |
|||
} |
|||
.register-code { |
|||
width: 33%; |
|||
height: 38px; |
|||
float: right; |
|||
img { |
|||
cursor: pointer; |
|||
vertical-align: middle; |
|||
} |
|||
} |
|||
.el-register-footer { |
|||
height: 40px; |
|||
line-height: 40px; |
|||
position: fixed; |
|||
bottom: 0; |
|||
width: 100%; |
|||
text-align: center; |
|||
color: #fff; |
|||
font-family: Arial; |
|||
font-size: 12px; |
|||
letter-spacing: 1px; |
|||
} |
|||
.register-code-img { |
|||
height: 38px; |
|||
} |
|||
</style> |
@ -0,0 +1,553 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-row :gutter="20"> |
|||
<splitpanes :horizontal="this.$store.getters.device === 'mobile'" class="default-theme"> |
|||
<!--部门数据--> |
|||
<pane size="16"> |
|||
<el-col> |
|||
<div class="head-container"> |
|||
<el-input v-model="deptName" placeholder="请输入部门名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px" /> |
|||
</div> |
|||
<div class="head-container"> |
|||
<el-tree :data="deptOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current @node-click="handleNodeClick" /> |
|||
</div> |
|||
</el-col> |
|||
</pane> |
|||
<!--用户数据--> |
|||
<pane size="84"> |
|||
<el-col> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> |
|||
<el-form-item label="用户名称" prop="userName"> |
|||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" /> |
|||
</el-form-item> |
|||
<el-form-item label="手机号码" prop="phonenumber"> |
|||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable style="width: 240px" @keyup.enter.native="handleQuery" /> |
|||
</el-form-item> |
|||
<el-form-item label="状态" prop="status"> |
|||
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px"> |
|||
<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="创建时间"> |
|||
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:user:add']">新增</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['system:user:edit']">修改</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:user:remove']">删除</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['system:user:import']">导入</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['system:user:export']">导出</el-button> |
|||
</el-col> |
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange"> |
|||
<el-table-column type="selection" width="50" align="center" /> |
|||
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" /> |
|||
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" /> |
|||
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" /> |
|||
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible"> |
|||
<template slot-scope="scope"> |
|||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160"> |
|||
<template slot-scope="scope"> |
|||
<span>{{ parseTime(scope.row.createTime) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width"> |
|||
<template slot-scope="scope" v-if="scope.row.userId !== 1"> |
|||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']">修改</el-button> |
|||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']">删除</el-button> |
|||
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:user:resetPwd', 'system:user:edit']"> |
|||
<el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button> |
|||
<el-dropdown-menu slot="dropdown"> |
|||
<el-dropdown-item command="handleResetPwd" icon="el-icon-key" v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item> |
|||
<el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check" v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</el-dropdown> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> |
|||
</el-col> |
|||
</pane> |
|||
</splitpanes> |
|||
</el-row> |
|||
|
|||
<!-- 添加或修改用户配置对话框 --> |
|||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body> |
|||
<el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item label="用户昵称" prop="nickName"> |
|||
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="归属部门" prop="deptId"> |
|||
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" placeholder="请选择归属部门" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item label="手机号码" prop="phonenumber"> |
|||
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="邮箱" prop="email"> |
|||
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName"> |
|||
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password"> |
|||
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password /> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item label="用户性别"> |
|||
<el-select v-model="form.sex" placeholder="请选择性别"> |
|||
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="状态"> |
|||
<el-radio-group v-model="form.status"> |
|||
<el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item label="岗位"> |
|||
<el-select v-model="form.postIds" multiple placeholder="请选择岗位"> |
|||
<el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId" :disabled="item.status == 1" ></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="角色"> |
|||
<el-select v-model="form.roleIds" multiple placeholder="请选择角色"> |
|||
<el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" :disabled="item.status == 1"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="24"> |
|||
<el-form-item label="备注"> |
|||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="cancel">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
|
|||
<!-- 用户导入对话框 --> |
|||
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body> |
|||
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag> |
|||
<i class="el-icon-upload"></i> |
|||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> |
|||
<div class="el-upload__tip text-center" slot="tip"> |
|||
<div class="el-upload__tip" slot="tip"> |
|||
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据 |
|||
</div> |
|||
<span>仅允许导入xls、xlsx格式文件。</span> |
|||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link> |
|||
</div> |
|||
</el-upload> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitFileForm">确 定</el-button> |
|||
<el-button @click="upload.open = false">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, deptTreeSelect } from "@/api/system/user"; |
|||
import { getToken } from "@/utils/auth"; |
|||
import Treeselect from "@riophae/vue-treeselect"; |
|||
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; |
|||
import { Splitpanes, Pane } from "splitpanes"; |
|||
import "splitpanes/dist/splitpanes.css"; |
|||
|
|||
export default { |
|||
name: "User", |
|||
dicts: ['sys_normal_disable', 'sys_user_sex'], |
|||
components: { Treeselect, Splitpanes, Pane }, |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 选中数组 |
|||
ids: [], |
|||
// 非单个禁用 |
|||
single: true, |
|||
// 非多个禁用 |
|||
multiple: true, |
|||
// 显示搜索条件 |
|||
showSearch: true, |
|||
// 总条数 |
|||
total: 0, |
|||
// 用户表格数据 |
|||
userList: null, |
|||
// 弹出层标题 |
|||
title: "", |
|||
// 所有部门树选项 |
|||
deptOptions: undefined, |
|||
// 过滤掉已禁用部门树选项 |
|||
enabledDeptOptions: undefined, |
|||
// 是否显示弹出层 |
|||
open: false, |
|||
// 部门名称 |
|||
deptName: undefined, |
|||
// 默认密码 |
|||
initPassword: undefined, |
|||
// 日期范围 |
|||
dateRange: [], |
|||
// 岗位选项 |
|||
postOptions: [], |
|||
// 角色选项 |
|||
roleOptions: [], |
|||
// 表单参数 |
|||
form: {}, |
|||
defaultProps: { |
|||
children: "children", |
|||
label: "label" |
|||
}, |
|||
// 用户导入参数 |
|||
upload: { |
|||
// 是否显示弹出层(用户导入) |
|||
open: false, |
|||
// 弹出层标题(用户导入) |
|||
title: "", |
|||
// 是否禁用上传 |
|||
isUploading: false, |
|||
// 是否更新已经存在的用户数据 |
|||
updateSupport: 0, |
|||
// 设置上传的请求头部 |
|||
headers: { Authorization: "Bearer " + getToken() }, |
|||
// 上传的地址 |
|||
url: process.env.VUE_APP_BASE_API + "/system/user/importData" |
|||
}, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
userName: undefined, |
|||
phonenumber: undefined, |
|||
status: undefined, |
|||
deptId: undefined |
|||
}, |
|||
// 列信息 |
|||
columns: [ |
|||
{ key: 0, label: `用户编号`, visible: true }, |
|||
{ key: 1, label: `用户名称`, visible: true }, |
|||
{ key: 2, label: `用户昵称`, visible: true }, |
|||
{ key: 3, label: `部门`, visible: true }, |
|||
{ key: 4, label: `手机号码`, visible: true }, |
|||
{ key: 5, label: `状态`, visible: true }, |
|||
{ key: 6, label: `创建时间`, visible: true } |
|||
], |
|||
// 表单校验 |
|||
rules: { |
|||
userName: [ |
|||
{ required: true, message: "用户名称不能为空", trigger: "blur" }, |
|||
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' } |
|||
], |
|||
nickName: [ |
|||
{ required: true, message: "用户昵称不能为空", trigger: "blur" } |
|||
], |
|||
password: [ |
|||
{ required: true, message: "用户密码不能为空", trigger: "blur" }, |
|||
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }, |
|||
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" } |
|||
], |
|||
email: [ |
|||
{ |
|||
type: "email", |
|||
message: "请输入正确的邮箱地址", |
|||
trigger: ["blur", "change"] |
|||
} |
|||
], |
|||
phonenumber: [ |
|||
{ |
|||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, |
|||
message: "请输入正确的手机号码", |
|||
trigger: "blur" |
|||
} |
|||
] |
|||
} |
|||
}; |
|||
}, |
|||
watch: { |
|||
// 根据名称筛选部门树 |
|||
deptName(val) { |
|||
this.$refs.tree.filter(val); |
|||
} |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
this.getDeptTree(); |
|||
this.getConfigKey("sys.user.initPassword").then(response => { |
|||
this.initPassword = response.msg; |
|||
}); |
|||
}, |
|||
methods: { |
|||
/** 查询用户列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => { |
|||
this.userList = response.rows; |
|||
this.total = response.total; |
|||
this.loading = false; |
|||
} |
|||
); |
|||
}, |
|||
/** 查询部门下拉树结构 */ |
|||
getDeptTree() { |
|||
deptTreeSelect().then(response => { |
|||
this.deptOptions = response.data; |
|||
this.enabledDeptOptions = this.filterDisabledDept(JSON.parse(JSON.stringify(response.data))); |
|||
}); |
|||
}, |
|||
// 过滤禁用的部门 |
|||
filterDisabledDept(deptList) { |
|||
return deptList.filter(dept => { |
|||
if (dept.disabled) { |
|||
return false; |
|||
} |
|||
if (dept.children && dept.children.length) { |
|||
dept.children = this.filterDisabledDept(dept.children); |
|||
} |
|||
return true; |
|||
}); |
|||
}, |
|||
// 筛选节点 |
|||
filterNode(value, data) { |
|||
if (!value) return true; |
|||
return data.label.indexOf(value) !== -1; |
|||
}, |
|||
// 节点单击事件 |
|||
handleNodeClick(data) { |
|||
this.queryParams.deptId = data.id; |
|||
this.handleQuery(); |
|||
}, |
|||
// 用户状态修改 |
|||
handleStatusChange(row) { |
|||
let text = row.status === "0" ? "启用" : "停用"; |
|||
this.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function() { |
|||
return changeUserStatus(row.userId, row.status); |
|||
}).then(() => { |
|||
this.$modal.msgSuccess(text + "成功"); |
|||
}).catch(function() { |
|||
row.status = row.status === "0" ? "1" : "0"; |
|||
}); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = { |
|||
userId: undefined, |
|||
deptId: undefined, |
|||
userName: undefined, |
|||
nickName: undefined, |
|||
password: undefined, |
|||
phonenumber: undefined, |
|||
email: undefined, |
|||
sex: undefined, |
|||
status: "0", |
|||
remark: undefined, |
|||
postIds: [], |
|||
roleIds: [] |
|||
}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.dateRange = []; |
|||
this.resetForm("queryForm"); |
|||
this.queryParams.deptId = undefined; |
|||
this.$refs.tree.setCurrentKey(null); |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map(item => item.userId); |
|||
this.single = selection.length != 1; |
|||
this.multiple = !selection.length; |
|||
}, |
|||
// 更多操作触发 |
|||
handleCommand(command, row) { |
|||
switch (command) { |
|||
case "handleResetPwd": |
|||
this.handleResetPwd(row); |
|||
break; |
|||
case "handleAuthRole": |
|||
this.handleAuthRole(row); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
}, |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
this.reset(); |
|||
getUser().then(response => { |
|||
this.postOptions = response.posts; |
|||
this.roleOptions = response.roles; |
|||
this.open = true; |
|||
this.title = "添加用户"; |
|||
this.form.password = this.initPassword; |
|||
}); |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleUpdate(row) { |
|||
this.reset(); |
|||
const userId = row.userId || this.ids; |
|||
getUser(userId).then(response => { |
|||
this.form = response.data; |
|||
this.postOptions = response.posts; |
|||
this.roleOptions = response.roles; |
|||
this.$set(this.form, "postIds", response.postIds); |
|||
this.$set(this.form, "roleIds", response.roleIds); |
|||
this.open = true; |
|||
this.title = "修改用户"; |
|||
this.form.password = ""; |
|||
}); |
|||
}, |
|||
/** 重置密码按钮操作 */ |
|||
handleResetPwd(row) { |
|||
this.$prompt('请输入"' + row.userName + '"的新密码', "提示", { |
|||
confirmButtonText: "确定", |
|||
cancelButtonText: "取消", |
|||
closeOnClickModal: false, |
|||
inputPattern: /^.{5,20}$/, |
|||
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间", |
|||
inputValidator: (value) => { |
|||
if (/<|>|"|'|\||\\/.test(value)) { |
|||
return "不能包含非法字符:< > \" ' \\\ |" |
|||
} |
|||
}, |
|||
}).then(({ value }) => { |
|||
resetUserPwd(row.userId, value).then(response => { |
|||
this.$modal.msgSuccess("修改成功,新密码是:" + value); |
|||
}); |
|||
}).catch(() => {}); |
|||
}, |
|||
/** 分配角色操作 */ |
|||
handleAuthRole: function(row) { |
|||
const userId = row.userId; |
|||
this.$router.push("/system/user-auth/role/" + userId); |
|||
}, |
|||
/** 提交按钮 */ |
|||
submitForm: function() { |
|||
this.$refs["form"].validate(valid => { |
|||
if (valid) { |
|||
if (this.form.userId != undefined) { |
|||
updateUser(this.form).then(response => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} else { |
|||
addUser(this.form).then(response => { |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const userIds = row.userId || this.ids; |
|||
this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function() { |
|||
return delUser(userIds); |
|||
}).then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}).catch(() => {}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download('system/user/export', { |
|||
...this.queryParams |
|||
}, `user_${new Date().getTime()}.xlsx`) |
|||
}, |
|||
/** 导入按钮操作 */ |
|||
handleImport() { |
|||
this.upload.title = "用户导入"; |
|||
this.upload.open = true; |
|||
}, |
|||
/** 下载模板操作 */ |
|||
importTemplate() { |
|||
this.download('system/user/importTemplate', { |
|||
}, `user_template_${new Date().getTime()}.xlsx`) |
|||
}, |
|||
// 文件上传中处理 |
|||
handleFileUploadProgress(event, file, fileList) { |
|||
this.upload.isUploading = true; |
|||
}, |
|||
// 文件上传成功处理 |
|||
handleFileSuccess(response, file, fileList) { |
|||
this.upload.open = false; |
|||
this.upload.isUploading = false; |
|||
this.$refs.upload.clearFiles(); |
|||
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true }); |
|||
this.getList(); |
|||
}, |
|||
// 提交上传文件 |
|||
submitFileForm() { |
|||
this.$refs.upload.submit(); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,312 @@ |
|||
<template> |
|||
<el-form ref="genInfoForm" :model="info" :rules="rules" label-width="150px"> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<el-form-item prop="tplCategory"> |
|||
<span slot="label">生成模板</span> |
|||
<el-select v-model="info.tplCategory" @change="tplSelectChange"> |
|||
<el-option label="单表(增删改查)" value="crud" /> |
|||
<el-option label="树表(增删改查)" value="tree" /> |
|||
<el-option label="主子表(增删改查)" value="sub" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item prop="tplWebType"> |
|||
<span slot="label">前端类型</span> |
|||
<el-select v-model="info.tplWebType"> |
|||
<el-option label="Vue2 Element UI 模版" value="element-ui" /> |
|||
<el-option label="Vue3 Element Plus 模版" value="element-plus" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item prop="packageName"> |
|||
<span slot="label"> |
|||
生成包路径 |
|||
<el-tooltip content="生成在哪个java包下,例如 com.ruoyi.system" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-input v-model="info.packageName" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :span="12"> |
|||
<el-form-item prop="moduleName"> |
|||
<span slot="label"> |
|||
生成模块名 |
|||
<el-tooltip content="可理解为子系统名,例如 system" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-input v-model="info.moduleName" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :span="12"> |
|||
<el-form-item prop="businessName"> |
|||
<span slot="label"> |
|||
生成业务名 |
|||
<el-tooltip content="可理解为功能英文名,例如 user" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-input v-model="info.businessName" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :span="12"> |
|||
<el-form-item prop="functionName"> |
|||
<span slot="label"> |
|||
生成功能名 |
|||
<el-tooltip content="用作类描述,例如 用户" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-input v-model="info.functionName" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :span="12"> |
|||
<el-form-item prop="genType"> |
|||
<span slot="label"> |
|||
生成代码方式 |
|||
<el-tooltip content="默认为zip压缩包下载,也可以自定义生成路径" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-radio v-model="info.genType" label="0">zip压缩包</el-radio> |
|||
<el-radio v-model="info.genType" label="1">自定义路径</el-radio> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :span="12"> |
|||
<el-form-item> |
|||
<span slot="label"> |
|||
上级菜单 |
|||
<el-tooltip content="分配到指定菜单下,例如 系统管理" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<treeselect |
|||
:append-to-body="true" |
|||
v-model="info.parentMenuId" |
|||
:options="menus" |
|||
:normalizer="normalizer" |
|||
:show-count="true" |
|||
placeholder="请选择系统菜单" |
|||
/> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :span="24" v-if="info.genType == '1'"> |
|||
<el-form-item prop="genPath"> |
|||
<span slot="label"> |
|||
自定义路径 |
|||
<el-tooltip content="填写磁盘绝对路径,若不填写,则生成到当前Web项目下" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-input v-model="info.genPath"> |
|||
<el-dropdown slot="append"> |
|||
<el-button type="primary"> |
|||
最近路径快速选择 |
|||
<i class="el-icon-arrow-down el-icon--right"></i> |
|||
</el-button> |
|||
<el-dropdown-menu slot="dropdown"> |
|||
<el-dropdown-item @click.native="info.genPath = '/'">恢复默认的生成基础路径</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</el-dropdown> |
|||
</el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row v-show="info.tplCategory == 'tree'"> |
|||
<h4 class="form-header">其他信息</h4> |
|||
<el-col :span="12"> |
|||
<el-form-item> |
|||
<span slot="label"> |
|||
树编码字段 |
|||
<el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-select v-model="info.treeCode" placeholder="请选择"> |
|||
<el-option |
|||
v-for="(column, index) in info.columns" |
|||
:key="index" |
|||
:label="column.columnName + ':' + column.columnComment" |
|||
:value="column.columnName" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item> |
|||
<span slot="label"> |
|||
树父编码字段 |
|||
<el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-select v-model="info.treeParentCode" placeholder="请选择"> |
|||
<el-option |
|||
v-for="(column, index) in info.columns" |
|||
:key="index" |
|||
:label="column.columnName + ':' + column.columnComment" |
|||
:value="column.columnName" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item> |
|||
<span slot="label"> |
|||
树名称字段 |
|||
<el-tooltip content="树节点的显示名称字段名, 如:dept_name" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-select v-model="info.treeName" placeholder="请选择"> |
|||
<el-option |
|||
v-for="(column, index) in info.columns" |
|||
:key="index" |
|||
:label="column.columnName + ':' + column.columnComment" |
|||
:value="column.columnName" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row v-show="info.tplCategory == 'sub'"> |
|||
<h4 class="form-header">关联信息</h4> |
|||
<el-col :span="12"> |
|||
<el-form-item> |
|||
<span slot="label"> |
|||
关联子表的表名 |
|||
<el-tooltip content="关联子表的表名, 如:sys_user" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange"> |
|||
<el-option |
|||
v-for="(table, index) in tables" |
|||
:key="index" |
|||
:label="table.tableName + ':' + table.tableComment" |
|||
:value="table.tableName" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item> |
|||
<span slot="label"> |
|||
子表关联的外键名 |
|||
<el-tooltip content="子表关联的外键名, 如:user_id" placement="top"> |
|||
<i class="el-icon-question"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
<el-select v-model="info.subTableFkName" placeholder="请选择"> |
|||
<el-option |
|||
v-for="(column, index) in subColumns" |
|||
:key="index" |
|||
:label="column.columnName + ':' + column.columnComment" |
|||
:value="column.columnName" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script> |
|||
import Treeselect from "@riophae/vue-treeselect"; |
|||
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; |
|||
|
|||
export default { |
|||
components: { Treeselect }, |
|||
props: { |
|||
info: { |
|||
type: Object, |
|||
default: null |
|||
}, |
|||
tables: { |
|||
type: Array, |
|||
default: null |
|||
}, |
|||
menus: { |
|||
type: Array, |
|||
default: [] |
|||
}, |
|||
}, |
|||
data() { |
|||
return { |
|||
subColumns: [], |
|||
rules: { |
|||
tplCategory: [ |
|||
{ required: true, message: "请选择生成模板", trigger: "blur" } |
|||
], |
|||
packageName: [ |
|||
{ required: true, message: "请输入生成包路径", trigger: "blur" } |
|||
], |
|||
moduleName: [ |
|||
{ required: true, message: "请输入生成模块名", trigger: "blur" } |
|||
], |
|||
businessName: [ |
|||
{ required: true, message: "请输入生成业务名", trigger: "blur" } |
|||
], |
|||
functionName: [ |
|||
{ required: true, message: "请输入生成功能名", trigger: "blur" } |
|||
], |
|||
} |
|||
}; |
|||
}, |
|||
watch: { |
|||
'info.subTableName': function(val) { |
|||
this.setSubTableColumns(val); |
|||
}, |
|||
'info.tplWebType': function(val) { |
|||
if (val === '') { |
|||
this.info.tplWebType = "element-ui"; |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 转换菜单数据结构 */ |
|||
normalizer(node) { |
|||
if (node.children && !node.children.length) { |
|||
delete node.children; |
|||
} |
|||
return { |
|||
id: node.menuId, |
|||
label: node.menuName, |
|||
children: node.children |
|||
}; |
|||
}, |
|||
/** 选择子表名触发 */ |
|||
subSelectChange(value) { |
|||
this.info.subTableFkName = ''; |
|||
}, |
|||
/** 选择生成模板触发 */ |
|||
tplSelectChange(value) { |
|||
if(value !== 'sub') { |
|||
this.info.subTableName = ''; |
|||
this.info.subTableFkName = ''; |
|||
} |
|||
}, |
|||
/** 设置关联外键 */ |
|||
setSubTableColumns(value) { |
|||
for (var item in this.tables) { |
|||
const name = this.tables[item].tableName; |
|||
if (value === name) { |
|||
this.subColumns = this.tables[item].columns; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,354 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> |
|||
<el-form-item label="表名称" prop="tableName"> |
|||
<el-input |
|||
v-model="queryParams.tableName" |
|||
placeholder="请输入表名称" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="表描述" prop="tableComment"> |
|||
<el-input |
|||
v-model="queryParams.tableComment" |
|||
placeholder="请输入表描述" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="创建时间"> |
|||
<el-date-picker |
|||
v-model="dateRange" |
|||
style="width: 240px" |
|||
value-format="yyyy-MM-dd" |
|||
type="daterange" |
|||
range-separator="-" |
|||
start-placeholder="开始日期" |
|||
end-placeholder="结束日期" |
|||
></el-date-picker> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="primary" |
|||
plain |
|||
icon="el-icon-download" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleGenTable" |
|||
v-hasPermi="['tool:gen:code']" |
|||
>生成</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="primary" |
|||
plain |
|||
icon="el-icon-plus" |
|||
size="mini" |
|||
@click="openCreateTable" |
|||
v-hasRole="['admin']" |
|||
>创建</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="info" |
|||
plain |
|||
icon="el-icon-upload" |
|||
size="mini" |
|||
@click="openImportTable" |
|||
v-hasPermi="['tool:gen:import']" |
|||
>导入</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="success" |
|||
plain |
|||
icon="el-icon-edit" |
|||
size="mini" |
|||
:disabled="single" |
|||
@click="handleEditTable" |
|||
v-hasPermi="['tool:gen:edit']" |
|||
>修改</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="danger" |
|||
plain |
|||
icon="el-icon-delete" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleDelete" |
|||
v-hasPermi="['tool:gen:remove']" |
|||
>删除</el-button> |
|||
</el-col> |
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange"> |
|||
<el-table-column type="selection" align="center" width="55"></el-table-column> |
|||
<el-table-column label="序号" type="index" width="50" align="center"> |
|||
<template slot-scope="scope"> |
|||
<span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="表名称" |
|||
align="center" |
|||
prop="tableName" |
|||
:show-overflow-tooltip="true" |
|||
width="120" |
|||
/> |
|||
<el-table-column |
|||
label="表描述" |
|||
align="center" |
|||
prop="tableComment" |
|||
:show-overflow-tooltip="true" |
|||
width="120" |
|||
/> |
|||
<el-table-column |
|||
label="实体" |
|||
align="center" |
|||
prop="className" |
|||
:show-overflow-tooltip="true" |
|||
width="120" |
|||
/> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" width="160" /> |
|||
<el-table-column label="更新时间" align="center" prop="updateTime" width="160" /> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template slot-scope="scope"> |
|||
<el-button |
|||
type="text" |
|||
size="small" |
|||
icon="el-icon-view" |
|||
@click="handlePreview(scope.row)" |
|||
v-hasPermi="['tool:gen:preview']" |
|||
>预览</el-button> |
|||
<el-button |
|||
type="text" |
|||
size="small" |
|||
icon="el-icon-edit" |
|||
@click="handleEditTable(scope.row)" |
|||
v-hasPermi="['tool:gen:edit']" |
|||
>编辑</el-button> |
|||
<el-button |
|||
type="text" |
|||
size="small" |
|||
icon="el-icon-delete" |
|||
@click="handleDelete(scope.row)" |
|||
v-hasPermi="['tool:gen:remove']" |
|||
>删除</el-button> |
|||
<el-button |
|||
type="text" |
|||
size="small" |
|||
icon="el-icon-refresh" |
|||
@click="handleSynchDb(scope.row)" |
|||
v-hasPermi="['tool:gen:edit']" |
|||
>同步</el-button> |
|||
<el-button |
|||
type="text" |
|||
size="small" |
|||
icon="el-icon-download" |
|||
@click="handleGenTable(scope.row)" |
|||
v-hasPermi="['tool:gen:code']" |
|||
>生成代码</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<pagination |
|||
v-show="total>0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
<!-- 预览界面 --> |
|||
<el-dialog :title="preview.title" :visible.sync="preview.open" width="80%" top="5vh" append-to-body class="scrollbar"> |
|||
<el-tabs v-model="preview.activeName"> |
|||
<el-tab-pane |
|||
v-for="(value, key) in preview.data" |
|||
:label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))" |
|||
:name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))" |
|||
:key="key" |
|||
> |
|||
<el-link :underline="false" icon="el-icon-document-copy" v-clipboard:copy="value" v-clipboard:success="clipboardSuccess" style="float:right">复制</el-link> |
|||
<pre><code class="hljs" v-html="highlightedCode(value, key)"></code></pre> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</el-dialog> |
|||
<import-table ref="import" @ok="handleQuery" /> |
|||
<create-table ref="create" @ok="handleQuery" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen"; |
|||
import importTable from "./importTable"; |
|||
import createTable from "./createTable"; |
|||
import hljs from "highlight.js/lib/highlight"; |
|||
import "highlight.js/styles/github-gist.css"; |
|||
hljs.registerLanguage("java", require("highlight.js/lib/languages/java")); |
|||
hljs.registerLanguage("xml", require("highlight.js/lib/languages/xml")); |
|||
hljs.registerLanguage("html", require("highlight.js/lib/languages/xml")); |
|||
hljs.registerLanguage("vue", require("highlight.js/lib/languages/xml")); |
|||
hljs.registerLanguage("javascript", require("highlight.js/lib/languages/javascript")); |
|||
hljs.registerLanguage("sql", require("highlight.js/lib/languages/sql")); |
|||
|
|||
export default { |
|||
name: "Gen", |
|||
components: { importTable, createTable }, |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 唯一标识符 |
|||
uniqueId: "", |
|||
// 选中数组 |
|||
ids: [], |
|||
// 选中表数组 |
|||
tableNames: [], |
|||
// 非单个禁用 |
|||
single: true, |
|||
// 非多个禁用 |
|||
multiple: true, |
|||
// 显示搜索条件 |
|||
showSearch: true, |
|||
// 总条数 |
|||
total: 0, |
|||
// 表数据 |
|||
tableList: [], |
|||
// 日期范围 |
|||
dateRange: "", |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
tableName: undefined, |
|||
tableComment: undefined |
|||
}, |
|||
// 预览参数 |
|||
preview: { |
|||
open: false, |
|||
title: "代码预览", |
|||
data: {}, |
|||
activeName: "domain.java" |
|||
} |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
activated() { |
|||
const time = this.$route.query.t; |
|||
if (time != null && time != this.uniqueId) { |
|||
this.uniqueId = time; |
|||
this.queryParams.pageNum = Number(this.$route.query.pageNum); |
|||
this.getList(); |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 查询表集合 */ |
|||
getList() { |
|||
this.loading = true; |
|||
listTable(this.addDateRange(this.queryParams, this.dateRange)).then(response => { |
|||
this.tableList = response.rows; |
|||
this.total = response.total; |
|||
this.loading = false; |
|||
} |
|||
); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 生成代码操作 */ |
|||
handleGenTable(row) { |
|||
const tableNames = row.tableName || this.tableNames; |
|||
if (tableNames == "") { |
|||
this.$modal.msgError("请选择要生成的数据"); |
|||
return; |
|||
} |
|||
if(row.genType === "1") { |
|||
genCode(row.tableName).then(response => { |
|||
this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath); |
|||
}); |
|||
} else { |
|||
this.$download.zip("/tool/gen/batchGenCode?tables=" + tableNames, "ruoyi.zip"); |
|||
} |
|||
}, |
|||
/** 同步数据库操作 */ |
|||
handleSynchDb(row) { |
|||
const tableName = row.tableName; |
|||
this.$modal.confirm('确认要强制同步"' + tableName + '"表结构吗?').then(function() { |
|||
return synchDb(tableName); |
|||
}).then(() => { |
|||
this.$modal.msgSuccess("同步成功"); |
|||
}).catch(() => {}); |
|||
}, |
|||
/** 打开导入表弹窗 */ |
|||
openImportTable() { |
|||
this.$refs.import.show(); |
|||
}, |
|||
/** 打开创建表弹窗 */ |
|||
openCreateTable() { |
|||
this.$refs.create.show(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.dateRange = []; |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
/** 预览按钮 */ |
|||
handlePreview(row) { |
|||
previewTable(row.tableId).then(response => { |
|||
this.preview.data = response.data; |
|||
this.preview.open = true; |
|||
this.preview.activeName = "domain.java"; |
|||
}); |
|||
}, |
|||
/** 高亮显示 */ |
|||
highlightedCode(code, key) { |
|||
const vmName = key.substring(key.lastIndexOf("/") + 1, key.indexOf(".vm")); |
|||
var language = vmName.substring(vmName.indexOf(".") + 1, vmName.length); |
|||
const result = hljs.highlight(language, code || "", true); |
|||
return result.value || ' '; |
|||
}, |
|||
/** 复制代码成功 */ |
|||
clipboardSuccess() { |
|||
this.$modal.msgSuccess("复制成功"); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map(item => item.tableId); |
|||
this.tableNames = selection.map(item => item.tableName); |
|||
this.single = selection.length != 1; |
|||
this.multiple = !selection.length; |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleEditTable(row) { |
|||
const tableId = row.tableId || this.ids[0]; |
|||
const tableName = row.tableName || this.tableNames[0]; |
|||
const params = { pageNum: this.queryParams.pageNum }; |
|||
this.$tab.openPage("修改[" + tableName + "]生成配置", '/tool/gen-edit/index/' + tableId, params); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const tableIds = row.tableId || this.ids; |
|||
this.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?').then(function() { |
|||
return delTable(tableIds); |
|||
}).then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}).catch(() => {}); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,130 @@ |
|||
"use strict"; |
|||
const path = require("path"); |
|||
|
|||
function resolve(dir) { |
|||
return path.join(__dirname, dir); |
|||
} |
|||
|
|||
const CompressionPlugin = require("compression-webpack-plugin"); |
|||
|
|||
const name = process.env.VUE_APP_TITLE || "针灸管理系统"; // 网页标题
|
|||
|
|||
const port = process.env.port || process.env.npm_config_port || 80; // 端口
|
|||
|
|||
// vue.config.js 配置说明
|
|||
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
|
|||
// 这里只列一部分,具体配置参考文档
|
|||
module.exports = { |
|||
// 部署生产环境和开发环境下的URL。
|
|||
// 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
|
|||
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
|
|||
publicPath: process.env.NODE_ENV === "production" ? "/acupunctureClient/" : "/", |
|||
// 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
|
|||
outputDir: "dist", |
|||
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
|
|||
assetsDir: "static", |
|||
// 是否开启eslint保存检测,有效值:ture | false | 'error'
|
|||
lintOnSave: process.env.NODE_ENV === "development", |
|||
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
|||
productionSourceMap: false, |
|||
transpileDependencies: ["quill"], |
|||
// webpack-dev-server 相关配置
|
|||
devServer: { |
|||
host: "0.0.0.0", |
|||
port: port, |
|||
open: true, |
|||
proxy: { |
|||
// detail: https://cli.vuejs.org/config/#devserver-proxy
|
|||
[process.env.VUE_APP_BASE_API]: { |
|||
target: `https://test.tall.wiki/acupuncture`, |
|||
changeOrigin: true, |
|||
pathRewrite: { |
|||
["^" + process.env.VUE_APP_BASE_API]: "", |
|||
}, |
|||
}, |
|||
}, |
|||
disableHostCheck: true, |
|||
}, |
|||
css: { |
|||
loaderOptions: { |
|||
sass: { |
|||
sassOptions: { outputStyle: "expanded" }, |
|||
}, |
|||
}, |
|||
}, |
|||
configureWebpack: { |
|||
name: name, |
|||
resolve: { |
|||
alias: { |
|||
"@": resolve("src"), |
|||
}, |
|||
}, |
|||
plugins: [ |
|||
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
|
|||
new CompressionPlugin({ |
|||
cache: false, // 不启用文件缓存
|
|||
test: /\.(js|css|html|jpe?g|png|gif|svg)?$/i, // 压缩文件格式
|
|||
filename: "[path][base].gz[query]", // 压缩后的文件名
|
|||
algorithm: "gzip", // 使用gzip压缩
|
|||
minRatio: 0.8, // 压缩比例,小于 80% 的文件不会被压缩
|
|||
deleteOriginalAssets: false, // 压缩后删除原文件
|
|||
}), |
|||
], |
|||
}, |
|||
chainWebpack(config) { |
|||
config.plugins.delete("preload"); // TODO: need test
|
|||
config.plugins.delete("prefetch"); // TODO: need test
|
|||
|
|||
// set svg-sprite-loader
|
|||
config.module.rule("svg").exclude.add(resolve("src/assets/icons")).end(); |
|||
config.module |
|||
.rule("icons") |
|||
.test(/\.svg$/) |
|||
.include.add(resolve("src/assets/icons")) |
|||
.end() |
|||
.use("svg-sprite-loader") |
|||
.loader("svg-sprite-loader") |
|||
.options({ |
|||
symbolId: "icon-[name]", |
|||
}) |
|||
.end(); |
|||
|
|||
config.when(process.env.NODE_ENV !== "development", (config) => { |
|||
config |
|||
.plugin("ScriptExtHtmlWebpackPlugin") |
|||
.after("html") |
|||
.use("script-ext-html-webpack-plugin", [ |
|||
{ |
|||
// `runtime` must same as runtimeChunk name. default is `runtime`
|
|||
inline: /runtime\..*\.js$/, |
|||
}, |
|||
]) |
|||
.end(); |
|||
|
|||
config.optimization.splitChunks({ |
|||
chunks: "all", |
|||
cacheGroups: { |
|||
libs: { |
|||
name: "chunk-libs", |
|||
test: /[\\/]node_modules[\\/]/, |
|||
priority: 10, |
|||
chunks: "initial", // only package third parties that are initially dependent
|
|||
}, |
|||
elementUI: { |
|||
name: "chunk-elementUI", // split elementUI into a single package
|
|||
test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
|
|||
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
|
|||
}, |
|||
commons: { |
|||
name: "chunk-commons", |
|||
test: resolve("src/components"), // can customize your rules
|
|||
minChunks: 3, // minimum common number
|
|||
priority: 5, |
|||
reuseExistingChunk: true, |
|||
}, |
|||
}, |
|||
}); |
|||
config.optimization.runtimeChunk("single"); |
|||
}); |
|||
}, |
|||
}; |
Loading…
Reference in new issue