Browse Source

Merge pull request '管理员登录相关' (#14) from admin-login-2025_11_11 into master

Reviewed-on: #14
master
超级管理员 4 weeks ago
parent
commit
c4f35a1857
  1. 175
      package-lock.json
  2. 2
      package.json
  3. 5
      src/App.vue
  4. 1
      src/assets/doctor_h5.svg
  5. 1
      src/assets/login_backImg.svg
  6. 117
      src/components/Header.vue
  7. 7
      src/main.js
  8. 49
      src/router/index.js
  9. 46
      src/utils/request.js
  10. 48
      src/views/HomeView.vue
  11. 17
      src/views/HosInformation.vue
  12. 296
      src/views/adminLogin.vue
  13. 382
      src/views/login.vue
  14. 38
      src/views/paymentMethod.vue

175
package-lock.json

@ -15,7 +15,9 @@
"regenerator-runtime": "^0.14.1",
"vue": "^2.6.14",
"vue-clickaway": "^2.2.2",
"vue-qr": "^4.0.9",
"vue-router": "^3.5.1",
"vue-wxlogin": "^1.0.5",
"vuex": "^3.6.2"
},
"devDependencies": {
@ -3874,8 +3876,7 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
@ -4989,6 +4990,21 @@
}
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
@ -6497,8 +6513,7 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
"version": "2.3.3",
@ -7113,7 +7128,6 @@
"resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@ -7122,8 +7136,7 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ipaddr.js": {
"version": "2.2.0",
@ -7377,6 +7390,12 @@
"@sideway/pinpoint": "^2.0.0"
}
},
"node_modules/js-binary-schema-parser": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz",
"integrity": "sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==",
"license": "MIT"
},
"node_modules/js-message": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz",
@ -7954,6 +7973,18 @@
"node": ">=6"
}
},
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mini-css-extract-plugin": {
"version": "2.9.2",
"resolved": "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz",
@ -8375,7 +8406,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
}
@ -8541,6 +8571,12 @@
"node": ">=6"
}
},
"node_modules/parenthesis": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz",
"integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==",
"license": "MIT"
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
@ -10271,6 +10307,51 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/sirv": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/sirv/-/sirv-2.0.4.tgz",
@ -10459,6 +10540,15 @@
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-split-by": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz",
"integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
"license": "MIT",
"dependencies": {
"parenthesis": "^3.1.5"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
@ -11209,6 +11299,59 @@
}
}
},
"node_modules/vue-qr": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/vue-qr/-/vue-qr-4.0.9.tgz",
"integrity": "sha512-pAISV94T0MNEYA3NGjykUpsXRE2QfaNxlu9ZhEL6CERgqNc21hJYuP3hRVzAWfBQlgO18DPmZTbrFerJC3+Ikw==",
"license": "MIT",
"dependencies": {
"glob": "^8.0.1",
"js-binary-schema-parser": "^2.0.2",
"simple-get": "^4.0.1",
"string-split-by": "^1.0.0"
}
},
"node_modules/vue-qr/node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/vue-qr/node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^5.0.1",
"once": "^1.3.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/vue-qr/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/vue-router": {
"version": "3.6.5",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-3.6.5.tgz",
@ -11273,6 +11416,19 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"node_modules/vue-wxlogin": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/vue-wxlogin/-/vue-wxlogin-1.0.5.tgz",
"integrity": "sha512-JJTL9qwROH7c/OWU8mube6Yd6kwUTItCg4tyjZQVby1MKWNuNz/f0eeRd0bhOdFyPSFmVazTF3nw+Jhfo4FzVA==",
"license": "MIT",
"dependencies": {
"vue": "^2.4.2"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/vuex/-/vuex-3.6.2.tgz",
@ -11807,8 +11963,7 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/ws": {
"version": "7.5.10",

2
package.json

@ -15,7 +15,9 @@
"regenerator-runtime": "^0.14.1",
"vue": "^2.6.14",
"vue-clickaway": "^2.2.2",
"vue-qr": "^4.0.9",
"vue-router": "^3.5.1",
"vue-wxlogin": "^1.0.5",
"vuex": "^3.6.2"
},
"devDependencies": {

5
src/App.vue

@ -11,7 +11,7 @@
<SliderMenu v-if="showSidebar" :menuData="slidermenu" :customize="customize"></SliderMenu>
<el-container class="main-right-content">
<!-- 面包屑导航 -->
<Breadcrumb />
<Breadcrumb v-if="user"/>
<el-main class="app-content">
<global-loading>
<router-view />
@ -34,6 +34,7 @@ export default {
data() {
return {
isCollapse: true,
user:null
};
},
components: {
@ -46,6 +47,8 @@ export default {
...mapState(['hosMenuData','slidermenu','customize','showSidebar', 'showFooter', 'showHeader']) // VuexshowSidebar
},
mounted() {
this.user = localStorage.getItem('nick')
window.addEventListener('beforeunload', this.clearStorage);
},
beforeUnmount() {

1
src/assets/doctor_h5.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 357 KiB

1
src/assets/login_backImg.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 MiB

117
src/components/Header.vue

@ -3,37 +3,121 @@
<div class="header-logo" @click="toHome">
<img src="../assets/header-logo.png" alt="">
</div>
<div class="header-right flex">
<!-- <div class="right-item">权限设置</div>
<div class="right-item">岗位设置</div> -->
<!-- <el-dropdown trigger="click">
<div class="header-right flex" v-if="headerShow">
<div
:class="['right-item', currentPath == item.path ? 'right-item-active' :'']"
v-for="(item,index) in routerNavList"
:key="index"
@click="goToPage(item.path)"
>
{{ item.name }}
</div>
<el-dropdown trigger="click">
<div class="right-item flex el-dropdown-link">
<img class="right-item-icon" src="../assets/header-icon.png" alt="">
<span>学术论文出版</span>
<img src="../assets/header-drop.svg" alt="">
<span>{{user}}</span>
<div class="img"></div>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-plus">黄金糕</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus">狮子头</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus-outline">螺蛳粉</el-dropdown-item>
<el-dropdown-item icon="el-icon-check">双皮奶</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-check">蚵仔煎</el-dropdown-item>
<el-dropdown-item><div @click="loginOut">退出</div></el-dropdown-item>
</el-dropdown-menu>
</el-dropdown> -->
</el-dropdown>
</div>
</div>
</template>
<script>
export default {
data(){
return{
user: '',
headerShow:false,
doctor_id:'',
authtoken:false,
currentPath:'',
routerNavList:[
// {
// name:'',
// path:'/doctorInformation'
// },
// {
// name:'',
// path:'/hospitalManage'
// },
// {
// name:'',
// path:'/paymentMethod'
// },
]
}
},
mounted(){
this.user = localStorage.getItem('user_name')
var user_type = localStorage.getItem('user_type')
if(!user_type || user_type == 'undefined') {
this.user = '超级管理员'
}else{
if(user_type && user_type != 'undefined') this.user = (user_type == 1?'医生':'助手') + this.user
}
this.doctor_id = localStorage.getItem('doctor_id')
this.authtoken = localStorage.getItem('authtoken')
if(this.authtoken){
this.headerShow = true
//
this.updateCurrentPath()
//
this.watchRouteChange()
}else{
this.headerShow = false
}
},
methods: {
toHome() {
this.$router.push({
path: '/'
})
},
goToPage(path) {
this.currentPath = path;
this.$router.push({
path: path + '?doctor_id=' + this.doctor_id
})
},
loginOut(){
localStorage.removeItem('token');
localStorage.removeItem('user_name');
localStorage.removeItem('authtoken');
localStorage.removeItem('user_type');
localStorage.removeItem('doctor_id');
this.$router.push('/login');
},
//
updateCurrentPath() {
const currentRoute = this.$route.path;
const matchedItem = this.routerNavList.find(item =>
currentRoute.startsWith(item.path)
);
if (matchedItem) {
this.currentPath = matchedItem.path;
} else {
this.currentPath = '';
}
},
//
watchRouteChange() {
this.$watch('$route', (to, from) => {
console.log(to,from,'====');
this.updateCurrentPath();
});
}
},
beforeDestroy() {
}
}
</script>
<style lang="scss" scoped>
.flex {
display: flex;
@ -56,8 +140,8 @@ export default {
}
.header-right {
width: 362px;
justify-content: flex-end;
gap:20px;
.el-dropdown-link {
cursor: pointer;
}
@ -72,12 +156,17 @@ export default {
transition: all .3s;
}
}
.right-item-active{
border-radius: 6px;
background: rgba(255, 255, 255, 0.4);
color: #006AFF;
transition: all .3s;
}
.el-dropdown .right-item{
padding: 0 10px;
cursor: pointer;
}
.right-item-icon {
width: 36px;
height: 36px;

7
src/main.js

@ -25,6 +25,13 @@ Vue.directive('clickaway', clickaway);
Vue.mixin(HeaderIcon)
Vue.use(PositionMessage)
window.issuper = true;
var user_type = localStorage.getItem('user_type')
if(user_type && user_type != 'undefined') window.issuper = false;
window.doctor_id = localStorage.getItem('doctor_id');
new Vue({
router,
store,

49
src/router/index.js

@ -5,10 +5,19 @@ import HomeView from '../views/HomeView.vue';
Vue.use(VueRouter)
const whiteSlideList = [ '/ui', '/hosInformation']; //侧边导航白名单
const blackHeaderList = ['/ErrorAccess']; //头部导航黑名单
const blackHeaderList = ['/ErrorAccess','/login']; //头部导航黑名单
const whiteFooterList = ['/','/doctorInformation' ,'/hosInformation','/addNewTreatment']; //底部白名单
const routes = [
{
path: '/login',
name: '登录',
component: () => import('../views/adminLogin.vue'),
meta: {
hideBreadcrumb: true, // 首页不显示面包屑
requiresAuth:false
}
},
{
path: '/',
name: '首页',
component: HomeView,
@ -18,6 +27,19 @@ const routes = [
}
},
{
path: '/hospitalManage',
name: '医院管理',
component: HomeView,
props:{
onlyHosFlag:true
},
meta: {
title: '医院管理',
hideBreadcrumb: true, // 首页不显示面包屑
requiresAuth:true
}
},
{
path: '/doctorInformation',
name: '医生信息',
component: () => import('../views/DoctorInformation.vue'),
@ -42,9 +64,6 @@ const routes = [
meta: {
title: '编辑医院',
breadcrumbParent: '医生信息' // 手动指定父级
// r如果想隐藏中间层级
// breadcrumbParent: '首页', // 跳过医生信息
// hideInBreadcrumb: true // 可选:隐藏当前项
}
},
{
@ -54,9 +73,6 @@ const routes = [
meta: {
title: '微信收款',
breadcrumbParent: '编辑医院' // 手动指定父级
// r如果想隐藏中间层级
// breadcrumbParent: '首页', // 跳过医生信息
// hideInBreadcrumb: true // 可选:隐藏当前项
}
},
@ -83,7 +99,8 @@ const routes = [
name: '收款方式',
meta: {
title: '收款管理',
hideBreadcrumb: true // 首页不显示面包屑
hideBreadcrumb: true, // 首页不显示面包屑
requiresAuth:true
}
},
{
@ -102,7 +119,22 @@ const router = new VueRouter({
base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => {
// 检查本地存储是否有nick
const hasNick = localStorage.getItem('nick');
// 如果有nick且访问的是根路径,则重定向到/hospitalManage
if (hasNick && to.path === '/') {
next('/hospitalManage');
return;
}
// 如果没有nick且尝试访问需要认证的页面(如/hospitalManage),则重定向到首页
if (!hasNick && to.meta.requiresAuth) {
next('/');
return;
}
if (whiteSlideList.includes(to.path)) {
store.commit('SET_SIDEBAR', true); // 登录页面不显示侧边栏
} else {
@ -118,6 +150,7 @@ router.beforeEach((to, from, next) => {
} else {
store.commit('SET_HEADER', true); // 其他页面显示顶部
}
next();
});

46
src/utils/request.js

@ -15,10 +15,21 @@ const service = axios.create({
service.interceptors.request.use(
(config) => {
// 在发送请求之前做一些处理,例如添加 token
const token = localStorage.getItem("token");
if (token) {
config.headers["Auth"] = `${token}`;
// const token = localStorage.getItem("token");
const token = localStorage.getItem("authtoken");
const doctor_id = localStorage.getItem("doctor_id");
// 检查是否存在 token,如果不存在则跳转到登录页
if (!token && location.pathname != '/login' && location.href.indexOf('/login') == -1) {
// 可以根据需要决定是否提示用户
console.warn("未检测到登录信息,请重新登录");
window.location.href = "/login";
return Promise.reject(new Error("未登录"));
}
if (token) config.headers["Auth"] = `${token}`;
if (doctor_id) config.headers["DID"] = `${doctor_id}`;
return config;
},
(error) => {
@ -58,10 +69,19 @@ service.interceptors.response.use(
console.error("请求失败", error.message);
}
}
return Promise.reject(error);
}
);
function logout() {
localStorage.removeItem('authtoken')
localStorage.removeItem('doctor_id')
localStorage.removeItem('user_name')
localStorage.removeItem('user_type')
location.href = '/login'
}
/**
* 封装请求方法
* @param {string} method 请求方法 (GET, POST, PUT, DELETE )
@ -81,9 +101,13 @@ const request = (method, url, data = {}, config = {}) => {
...config,
});
res.then(response => {
if(response && response.msg && response.msg.indexOf('非法操作') > -1) {
localStorage.removeItem('authtoken')
location.href = '/ErrorAccess'
if(response && response.msg && response.msg.indexOf('您没有访问权限~~~') > -1) {
location.href = '/'
return;
}
if(response && response.msg && (response.msg.indexOf('非法操作') > -1 || response.code == 40002)) {
logout()
return;
}
}).catch(error => {
@ -101,9 +125,13 @@ const request = (method, url, data = {}, config = {}) => {
});
respost.then(response => {
if(response && response.msg && response.msg.indexOf('非法操作') > -1) {
localStorage.removeItem('authtoken')
location.href = '/ErrorAccess'
if(response && response.msg && response.msg.indexOf('您没有访问权限~~~') > -1) {
location.href = '/'
return;
}
if(response && response.msg && (response.msg.indexOf('非法操作') > -1 || response.code == 40002)) {
logout()
return;
}
}).catch(error => {

48
src/views/HomeView.vue

@ -1,7 +1,7 @@
<template>
<div class="min-width pagePadding " style="background: #F5F7FA;" v-if="pageShow">
<div class="doctor-list-wrap ">
<p class="pageTitle">医生列表</p>
<p class="pageTitle">{{issuper ? '医院管理' :'医生列表'}}</p>
<el-form>
<div class="selectAllTable-wrap flex-between mt32">
<div class="left flex">
@ -16,16 +16,20 @@
@click="batchOperate('1')">
批量启用</GuipButton>
</div>
<div class="right flex">
<div class="right" v-if="!issuper">
<GuipButton type="system" size="form" @click="addHospital(false)">添加医院</GuipButton>
</div>
<div class="right flex" v-else>
<span>搜索医生</span>
<GuipInput ref="GuipInput" style="margin:0 24px 0 12px" width="280px" height="32px"
placeholder="输入姓名" @blur="inputBlur" v-model="doctorName" />
<GuipButton @click="addDoctor" size="form">新增医生</GuipButton>
</div>
</div>
<div class="tableList mt32" v-for="(item, index) in doctorList" :key="index">
<div class="top flex-between mb24">
<div class="top flex-between mb24" v-if="issuper">
<div class="left left1 flex">
<el-checkbox @change="handleCheckAllChange(index)" v-model="item.checked"></el-checkbox>
<el-avatar :src="item.avator" v-if="item.avator"></el-avatar>
@ -88,8 +92,7 @@
</template>
</el-table-column>
</GuipTable>
<PaymentMethod :list="payList[item.id] ? payList[item.id] : []" @updatePayStatus="updatePayStatus" :depart_id="item.id" :doctor_id="item.id"/>
<PaymentMethod :list="payList[item.id] ? payList[item.id] : []" @updatePayStatus="updatePayStatus" :depart_id="item.id" :doctor_id="item.id" v-if="!onlyHosFlag"/>
</div>
</el-form>
</div>
@ -107,6 +110,7 @@ import { mapState } from 'vuex';
import PaymentMethod from '@/views/paymentMethod.vue';
export default {
props:['onlyHosFlag'],
data() {
return {
list: {
@ -143,7 +147,8 @@ export default {
allChecked: false,
pageShow:false,
doctorIds:[],
payList:[]
payList:false,
issuper:false
}
},
components: {
@ -158,18 +163,27 @@ export default {
// CustomDropdown
},
created() {
const { authtoken } = this.$route.query;
const { authtoken, did, type, name} = this.$route.query;
if(authtoken) {
localStorage.setItem('authtoken', authtoken);
localStorage.setItem('authtoken', decodeURIComponent(authtoken));
localStorage.setItem('doctor_id', did);
localStorage.setItem('user_type', type);
localStorage.setItem('user_name', name);
location.href = '/'
return;
}
store.commit('SET_PAGENAME', '医生管理'); //
},
mounted() {
this.issuper = window.issuper
store.commit('SET_CUSTOMIZE', false);
store.commit('SET_SLIDER_MENU', 'menuData');
this.getInitData()
if(this.onlyHosFlag)return
// --
this.getBindpayList()
},
computed: {
...mapState(['menuData']) // VuexshowSidebar
@ -295,7 +309,7 @@ export default {
})
},
onSwitchChange1(row) {
console.log(row, '------flag');
// console.log(row, '------flag');
row.status = row.status == 0 ? 1 : 0;
this.$http('POST', '/api/admin/depart_mutil_option', {
depart_index_ids:row.id,
@ -315,7 +329,7 @@ export default {
this.$set(this.doctorList, row)
},
batchOperate(status) {
console.log(this.tableSelections,'this.tableSelections----');
// console.log(this.tableSelections,'this.tableSelections----');
if(!this.tableSelections || this.tableSelections.length <= 0) {
this.$message.error('未选择要'+(status == '0' ? '禁用':'启用')+'的科室')
return;
@ -388,9 +402,21 @@ export default {
})
},
addHospital(item) {
var docotr_id = window.doctor_id
if(item) {
docotr_id = item.id
}
this.$router.push({
name: '医院信息',
query: { doctor_id: docotr_id }
})
},
addHospitalUser(){
// id token
let id = this.doctor_id;
this.$router.push({
name: '医院信息',
query: { doctor_id: item.id }
query: { doctor_id: id }
})
},
editDoctor(item) {

17
src/views/HosInformation.vue

@ -712,7 +712,6 @@ export default {
this.depart_id = depart_id;
this.doctor_id = doctor_id;
await this.fetchDoctorData()
console.log(this.doctor_id)
await this.getAreaData()
this.fetchPackData()
this.fetchProjectData()
@ -730,10 +729,12 @@ export default {
this.doctor_id = newVal.doctor_id;
this.depart_id = newVal.depart_id;
this.getAreaData();
this.fetchDoctorData();
this.fetchPackData();
this.fetchProjectData();
this.geHosAdress();
if(this.depart_id) {
this.fetchDoctorData();
this.fetchPackData();
this.fetchProjectData();
this.geHosAdress();
}
}
}
},
@ -1027,6 +1028,12 @@ export default {
depart_id: this.depart_id,
doctor_id: this.doctor_id
}).then(response => {
if(response.code != 0) {
this.$Message.error(response.msg);
// location.href = '/'
return;
}
this.form = {
...this.form, ...response.data,
'morning_worktime': response.data.morning_worktime ? response.data.morning_worktime.split('-') : '',

296
src/views/adminLogin.vue

@ -0,0 +1,296 @@
<template>
<div class="index_loginPage__K_bGK login-wrap">
<div class="index_banner__1d8CU">
<div class="index_rootContainer">
<b class="title">扫码登录</b>
<p class="sub_title mt32">打开 <b>微信</b> 扫码登录</p>
<div class="wxlogin-qrcode-box mb32" v-if="status == 'waiting'">
<Wxlogin
v-if="appid && redirect_uri"
:appid="appid"
:redirect_uri="redirect_uri"
scope="snsapi_login"
:state="state"
height="186px"
href="data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDE4OHB4O2JvcmRlcjpub25lO21hcmdpbi10b3A6MHB4O30KLmltcG93ZXJCb3ggLnRpdGxlIHtkaXNwbGF5OiBub25lO30KLmltcG93ZXJCb3ggLmluZm8ge3dpZHRoOiAxODhweDtkaXNwbGF5Om5vbmU7fQouc3RhdHVzX2ljb24ge2Rpc3BsYXk6IG5vbmV9Ci5pbXBvd2VyQm94IC5zdGF0dXMge3RleHQtYWxpZ246IGNlbnRlcjt9Ci53ZWJfcXJjb2RlX3R5cGVfaWZyYW1le2hlaWdodDoxODZweDt9Ci53ZWJfcXJjb2RlX3N3aXRjaF93cnAuanNfc3dpdGNoVG9GYXN0X3dycHttYXJnaW4tdG9wOjBweCAhaW1wb3J0YW50O2NvbG9yOiAjOEE5MDk5ICFpbXBvcnRhbnQ7fQoud2ViX3FyY29kZV9zd2l0Y2hfd3JwLmpzX3N3aXRjaFRvRmFzdF93cnAgYnV0dG9ue2NvbG9yOiAjOEE5MDk5ICFpbXBvcnRhbnQ7Y3Vyc29yOnBvaW50ZXI7fQoud2ViX3FyY29kZV9zd2l0Y2hfd3JwLmpzX3N3aXRjaFRvTm9ybWFsX3dycCBidXR0b257Y29sb3I6ICM4QTkwOTkgIWltcG9ydGFudDtjdXJzb3I6cG9pbnRlcjt9"
></Wxlogin>
</div>
<div class="refreshCode" v-if="status == 'expired'" @click="refreshCode">
<i class="el-icon-refresh"></i>
刷新二维码
</div>
<div class="attention gap10">
<el-checkbox v-model="checked"></el-checkbox>
<b>登录即代表同意 <a @click="jumpDoc">用户协议</a> <a @click="jumpDoc">隐私条款</a></b>
</div>
<p class="tip">若无账号请先联系客服注册</p>
</div>
</div>
<div class="style_footerWrapper">
<div class="left flex">
<img src="@/assets/doctor_h5.svg" alt="">
<div>
<b>手机端</b>
<p>微信扫码体验</p>
</div>
</div>
<div class="center">
<p><b>入驻指南</b>个人入驻 组织医院等入驻</p>
<p><b>人工咨询</b>扫左侧码进入手机端-我的-页面底部联系我们</p>
</div>
<div class="right">
<p><b>网站备案</b>© 2012-2025 something , <a href="">Inc. All rights 测试</a></p>
<p></p>
</div>
</div>
</div>
</template>
<script>
import Wxlogin from 'vue-wxlogin';
export default {
components: {
Wxlogin
},
data() {
return {
checked: true,
qrCodeUrl: 'https://example.com',
token: '',
status: 'expired',
loginTime:'',
appid:false,
redirectUri:false,
state:'zjadmin'
}
},
created(){
this.getLoginConfig()
},
methods: {
async getLoginConfig() {
this.status = 'waiting';
this.redirect_uri = '';
this.appid = '';
try {
await this.$http('POST', '/api/callback/get_admin_login_config').then(response => {
this.appid = response.data.appid
this.redirect_uri = response.data.redirect_uri
}).catch(error => {
console.error('生成二维码失败:', error);
this.status = 'error';
});
} catch (error) {
console.error('生成二维码失败:', error);
this.status = 'error';
}
},
handleLoginSuccess(data) {
this.status = 'confirmed';
this.userInfo = data.user_info;
this.loginTime = new Date().toLocaleString();
//
localStorage.setItem('authToken', data.auth_token);
localStorage.setItem('nick', JSON.stringify(data.nick));
localStorage.setItem('userInfo', JSON.stringify(data.user_info));
setTimeout(() => {
this.isLoggedIn = true;
this.$emit('login-success', data);
}, 1000);
},
}
}
</script>
<style lang="scss" scoped>
.index_banner__1d8CU {
align-items: center;
background-image: url(../assets/login_backImg.svg);
background-size: cover;
display: flex;
flex: 1 1;
justify-content: flex-end;
-webkit-transition: padding-right .5s;
transition: padding-right .5s;
width: 100%;
}
.index_rootContainer {
background: #fff;
border-radius: 16px;
box-shadow: 0 6px 40px 0 rgba(53, 76, 166, .08);
display: inline-block;
flex-shrink: 0;
height: 480px;
overflow: hidden;
width: 432px;
margin-right: 192px;
padding: 40px;
box-sizing: border-box;
display: flex;
flex-direction: column;
.title {
font-family: Microsoft YaHei;
font-size: 22px;
font-weight: bold;
line-height: normal;
text-align: center;
letter-spacing: 0.08em;
color: #1E2226;
}
.sub_title {
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: normal;
line-height: normal;
letter-spacing: 0.08em;
color: #626573;
margin-top: 32px;
margin-bottom: 12px;
b{
font-weight: bold;
color: #1E2226;
}
}
.img {
width: 192px;
height: 192px;
background: #006AFF;
margin: 0 auto;
margin-bottom: 32px;
}
}
.attention,
.tip {
font-size: 12px;
font-weight: normal;
line-height: 13px;
letter-spacing: 0.08em;
color: #626573;
text-align: center;
display: flex;
justify-content: center;
b {
font-size: 12px;
font-weight: normal;
a {
text-decoration: none;
color: #006AFF;
cursor: pointer;
}
}
}
.attention {
margin-bottom: 12px;
}
.refreshCode {
width: 172px;
height: 172px;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 10px;
justify-content: center;
align-items: center;
color: #626573;
font-family: Arial, sans-serif;
font-weight: bold;
cursor: pointer;
margin: 20px auto;
i{
font-size: 28px;
}
}
@media(max-width: 1420px) {
.index_rootContainer {
margin-right: 102px;
}
}
.style_footerWrapper {
background: #f3f4f6;
display: flex;
align-items: center;
justify-content: center;
padding: 46px 49px;
width: 100%;
height: 192px;
box-sizing: border-box;
gap: 72px;
b {
font-size: 12px;
font-weight: bold;
line-height: 13px;
letter-spacing: 0.08em;
color: #1E2226;
}
.left {
gap: 12px;
div {
display: flex;
flex-direction: column;
gap: 8px;
text-align: left;
}
img {
width: 97px;
height: 100px;
}
}
.center,
.right {
display: flex;
flex-direction: column;
gap: 24px;
text-align: left;
p {
display: flex;
gap: 8px;
}
}
}
.style_iconWrapper__3qi7l {
flex-basis: 80px;
}
.index_loginPage__K_bGK {
display: flex;
flex-direction: column;
height: 100vh;
min-width: 1200px;
}
#ecomLoginForm {
height: 280px;
}
.wxlogin-qrcode-box{
div:first-of-type{
height: 214px;
}
}
</style>

382
src/views/login.vue

@ -0,0 +1,382 @@
<template>
<div class="index_loginPage__K_bGK login-wrap">
<div class="index_banner__1d8CU">
<div class="index_rootContainer">
<b class="title">扫码登录</b>
<p class="sub_title mt32">打开 <b>微信</b> 扫码登录</p>
<div class=" mb32" v-if="status == 'waiting'">
<vue-qr :text="qrCodeUrl" :size="192" :dot-scale="1"></vue-qr>
</div>
<div class="refreshCode" v-if="status == 'expired'" @click="refreshCode">
<i class="el-icon-refresh"></i>
刷新二维码
</div>
<!-- :logo-src="logoUrl"
:logo-scale="0.2" -->
<div class="attention gap10">
<el-checkbox v-model="checked"></el-checkbox>
<b>登录即代表同意 <a @click="jumpDoc">用户协议</a> <a @click="jumpDoc">隐私条款</a></b>
</div>
<p class="tip">若无账号请先联系客服注册</p>
</div>
</div>
<div class="style_footerWrapper">
<div class="left flex">
<img src="@/assets/doctor_h5.svg" alt="">
<div>
<b>手机端</b>
<p>微信扫码体验</p>
</div>
</div>
<div class="center">
<p><b>入驻指南</b>个人入驻 组织医院等入驻</p>
<p><b>人工咨询</b>扫左侧码进入手机端-我的-页面底部联系我们</p>
</div>
<div class="right">
<p><b>网站备案</b>© 2012-2025 something , <a href="">Inc. All rights 测试</a></p>
<p></p>
</div>
</div>
</div>
</template>
<script>
import VueQr from 'vue-qr'
export default {
components: {
VueQr
},
data() {
return {
checked: true,
qrCodeUrl: 'https://example.com',
token: '',
status: 'expired', // waiting, scanned, confirmed, expired, error
loginTime:'',
}
},
created(){
// this.generateQRCode()
},
beforeDestroy() {
this.clearTimers();
},
methods: {
jumpDoc() {
},
//
refreshCode() {
},
async generateQRCode() {
this.status = 'waiting';
this.qrCodeUrl = '';
try {
await this.$http('POST', '/api/admin/generate_qrcode', {
device_type: 'web',
timestamp: Date.now()
}).then(response => {
this.token = response.data.token;
this.qrCodeUrl = response.data.qr_code_url;
//
this.startPolling();
// 5
this.setExpireTimer();
}).catch(error => {
console.error('生成二维码失败:', error);
this.status = 'error';
});
} catch (error) {
console.error('生成二维码失败:', error);
this.status = 'error';
}
},
//
startPolling() {
this.clearTimers();
this.pollInterval = setInterval(async () => {
if (this.status === 'confirmed' || this.status === 'expired' || this.status === 'error') {
this.clearTimers();
return;
}
try {
await this.$http('POST', '/api/admin/check_scan_status', {
token: this.token
}).then(response => {
this.status = response.data.status;
if (response.data.status === 'scanned') {
//
setTimeout(() => {
this.handleLoginConfirm();
}, 2000);
} else if (response.data.status === 'confirmed') {
this.handleLoginSuccess(response.data);
} else if (response.data.status === 'expired') {
this.status = 'expired';
this.clearTimers();
}
}).catch(error => {
console.error('检查扫码状态失败:', error);
this.status = 'error';
this.clearTimers();
});
} catch (error) {
console.error('检查扫码状态失败:', error);
this.status = 'error';
this.clearTimers();
}
}, 2000);
},
//
async handleLoginConfirm() {
try {
await this.$http('POST', '/api/admin/confirm_login', {
token: this.token
}).then(response => {
if (response.data.success) {
this.handleLoginSuccess(response.data);
} else {
this.status = 'expired';
}
}).catch(error => {
console.error('登录确认失败:', error);
this.status = 'error';
});
} catch (error) {
console.error('登录确认失败:', error);
this.status = 'error';
}
},
//
handleLoginSuccess(data) {
this.status = 'confirmed';
this.userInfo = data.user_info;
this.loginTime = new Date().toLocaleString();
//
localStorage.setItem('authToken', data.auth_token);
localStorage.setItem('nick', JSON.stringify(data.nick));
localStorage.setItem('userInfo', JSON.stringify(data.user_info));
setTimeout(() => {
this.isLoggedIn = true;
this.$emit('login-success', data);
}, 1000);
},
//
setExpireTimer() {
this.expireTimer = setTimeout(() => {
if (this.status !== 'confirmed') {
this.status = 'expired';
this.clearTimers();
}
}, 5 * 60 * 1000); // 5
},
//
clearTimers() {
if (this.pollInterval) {
clearInterval(this.pollInterval);
this.pollInterval = null;
}
if (this.expireTimer) {
clearTimeout(this.expireTimer);
this.expireTimer = null;
}
},
}
}
</script>
<style lang="scss" scoped>
.index_banner__1d8CU {
align-items: center;
background-image: url(../assets/login_backImg.svg);
background-size: cover;
display: flex;
flex: 1 1;
justify-content: flex-end;
-webkit-transition: padding-right .5s;
transition: padding-right .5s;
width: 100%;
}
.index_rootContainer {
background: #fff;
border-radius: 16px;
box-shadow: 0 6px 40px 0 rgba(53, 76, 166, .08);
display: inline-block;
flex-shrink: 0;
height: 480px;
overflow: hidden;
width: 432px;
margin-right: 192px;
padding: 40px;
box-sizing: border-box;
display: flex;
flex-direction: column;
.title {
font-family: Microsoft YaHei;
font-size: 22px;
font-weight: bold;
line-height: normal;
text-align: center;
letter-spacing: 0.08em;
color: #1E2226;
}
.sub_title {
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: normal;
line-height: normal;
letter-spacing: 0.08em;
color: #626573;
margin-top: 32px;
margin-bottom: 12px;
}
.img {
width: 192px;
height: 192px;
background: #006AFF;
margin: 0 auto;
margin-bottom: 32px;
}
}
.attention,
.tip {
font-size: 12px;
font-weight: normal;
line-height: 13px;
letter-spacing: 0.08em;
color: #626573;
text-align: center;
display: flex;
justify-content: center;
b {
font-size: 12px;
font-weight: normal;
a {
text-decoration: none;
color: #006AFF;
cursor: pointer;
}
}
}
.attention {
margin-bottom: 12px;
}
.refreshCode {
width: 172px;
height: 172px;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 10px;
justify-content: center;
align-items: center;
color: #626573;
font-family: Arial, sans-serif;
font-weight: bold;
cursor: pointer;
margin: 20px auto;
i{
font-size: 28px;
}
}
@media(max-width: 1420px) {
.index_rootContainer {
margin-right: 102px;
}
}
.style_footerWrapper {
background: #f3f4f6;
display: flex;
align-items: center;
justify-content: center;
padding: 46px 49px;
width: 100%;
height: 192px;
box-sizing: border-box;
gap: 72px;
b {
font-size: 12px;
font-weight: bold;
line-height: 13px;
letter-spacing: 0.08em;
color: #1E2226;
}
.left {
gap: 12px;
div {
display: flex;
flex-direction: column;
gap: 8px;
text-align: left;
}
img {
width: 97px;
height: 100px;
}
}
.center,
.right {
display: flex;
flex-direction: column;
gap: 24px;
text-align: left;
p {
display: flex;
gap: 8px;
}
}
}
.style_iconWrapper__3qi7l {
flex-basis: 80px;
}
.index_loginPage__K_bGK {
display: flex;
flex-direction: column;
height: 100vh;
min-width: 1200px;
}
#ecomLoginForm {
height: 280px;
}
</style>

38
src/views/paymentMethod.vue

@ -9,7 +9,7 @@
</div>
</div>
<el-form>
<GuipTable :tableData="payList" :loading="tableLoading">
<GuipTable :tableData="payList" >
<el-table-column prop="type" fixed="left" label="类型" width="140">
<template slot-scope="scope">
<div class="flex gap10">
@ -21,7 +21,13 @@
</el-table-column>
<el-table-column prop="company_short_name" label="公司简称" min-width="160px"></el-table-column>
<el-table-column prop="appid" label="账号" min-width="288px"></el-table-column>
<el-table-column prop="expires_time_desc" label="有效期" min-width="260px"></el-table-column>
<el-table-column prop="expires_time_desc" label="有效期" min-width="260px">
<template slot-scope="scope">
<div :class="scope.row.expires_time_desc=='已过期' || scope.row.expires_time_desc=='未授权'?' error':''">
{{scope.row.expires_time_desc}}
</div>
</template>
</el-table-column>
<el-table-column prop="status" label="上传证书" min-width="258px">
<template slot-scope="scope">
<div :class="'flex'+(scope.row.have_cert?'':' error')" v-if="scope.row.type == 2">
@ -104,16 +110,19 @@ export default {
},
watch:{
list: {
deep: true,
handler(newVal) {
console.log(newVal,'newVal=====');
this.payList = [...newVal];
this.tableLoading = false;
}
}
},
doctor_id(newVal){
if(newVal) this.doctorId = newVal
},
depart_id(newVal){
if(newVal) this.departId = newVal
},
},
created(){
const {doctor_id,depart_id } = this.$route.query;
@ -121,25 +130,8 @@ export default {
this.departId =depart_id;
this.doctorId =doctor_id;
}
console.log(this.doctor_id,'=doctor_id===');
},
mounted() {
if(this.doctor_id){
this.getBindpayList()
}
},
methods: {
async getBindpayList() {
await this.$http('POST', '/api/admin/get_pay_list', {
doctor_id: this.doctor_id,
depart_id: this.depart_id
}).then(response => {
this.payList = response.data;
}).catch(error => {
console.error(error, 'error')
})
},
handleEvent(data) {
this.isShowAddPay = data;
},

Loading…
Cancel
Save