From 31551437c241d2285a5a6950e77703e81757e7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E7=90=A6?= Date: Sat, 30 May 2026 16:36:33 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E6=9B=B4=E6=96=B0=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E9=80=BB=E8=BE=91=202.=20=E8=AF=8A=E6=89=80?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=E5=8A=9F=E8=83=BD=E4=B8=8A=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/doctorAuth.js | 53 ++ common/js/common.js | 7 +- common/js/index.js | 21 +- .../account-switch/account-switch-entry.vue | 84 +++ .../account-switch/account-switch-panel.vue | 275 +++++++++ components/doctor-login/doctor-login.vue | 284 +++++++++ pages/login/index.vue | 499 ++-------------- pages/my/index.vue | 2 +- pages/workbench/index.vue | 7 +- subPackages/sub_clinic_admin/home/index.vue | 282 ++++++++- subPackages/sub_clinic_admin/my/index.vue | 4 +- .../sub_clinic_admin/warehouse/detail.vue | 411 +++++++++---- .../sub_clinic_admin/warehouse/index.vue | 556 +++++++++++------- subPackages/sub_my/my_setAct.vue | 6 + .../sub_pharmacist/pharmacist_setInfo.vue | 5 + utils/loginSession.js | 116 ++++ 16 files changed, 1820 insertions(+), 792 deletions(-) create mode 100644 api/doctorAuth.js create mode 100644 components/account-switch/account-switch-entry.vue create mode 100644 components/account-switch/account-switch-panel.vue create mode 100644 components/doctor-login/doctor-login.vue create mode 100644 utils/loginSession.js diff --git a/api/doctorAuth.js b/api/doctorAuth.js new file mode 100644 index 0000000..d8fdd21 --- /dev/null +++ b/api/doctorAuth.js @@ -0,0 +1,53 @@ +import { req } from '@/common/js/index.js'; + +const AUTH_PREFIX = '/newApi/doctor-wx-auth'; + +function unwrapResult(res) { + if (!res) return null; + if (res.code === 0 && res.result !== undefined) return res.result; + return res.result != null ? res.result : res.data; +} + +function doctorAuthRequest(options) { + return req.request({ + ...options, + url: `${AUTH_PREFIX}${options.url}`, + }).then((res) => { + const data = unwrapResult(res); + const ok = res && res.code === 0; + return { res, data, ok }; + }); +} + +export function sendDoctorWxCode(phone, password) { + return req.request({ + url: `${AUTH_PREFIX}/send-verification-code`, + method: 'POST', + data: { phone, password }, + header: { isToken: false }, + }); +} + +export function doctorWxLogin(payload) { + return req.request({ + url: `${AUTH_PREFIX}/login`, + method: 'POST', + data: payload, + header: { isToken: false }, + }); +} + +export function getDoctorWxSiblingAccounts() { + return doctorAuthRequest({ + url: '/sibling-accounts', + method: 'GET', + }); +} + +export function switchDoctorWxAccount(payload) { + return doctorAuthRequest({ + url: '/switch-account', + method: 'POST', + data: payload, + }); +} diff --git a/common/js/common.js b/common/js/common.js index 9b501de..9145cdc 100644 --- a/common/js/common.js +++ b/common/js/common.js @@ -21,12 +21,11 @@ export async function requestConfig(ins, options, successHandler = null, failHan } options.url = options.url.replace('/oldApi/', ''); options.url = options.url.replace('/newApi/', ''); - // base - ins.header = options.header || ins.header - // config base + const defaultHeader = { 'Content-Type': 'application/x-www-form-urlencoded' } + // 每次请求独立合并 header,避免登录 isToken:false 污染单例 let config = { url:`${baseUrl}${options.url?options.url:''}`, - header:ins.header + header: { ...defaultHeader, ...(options.header || {}) } } if(ins.requestInterceptor){ let _cg = null diff --git a/common/js/index.js b/common/js/index.js index 6dfed37..9c3a726 100644 --- a/common/js/index.js +++ b/common/js/index.js @@ -40,13 +40,26 @@ const reqInterceptor = async (options) => { options.header = { ...options.header } - const isToken = (options.header || {}).isToken === false - const isClinicAdminReq = (options.header || {})._clinicAdmin === '1' + const skipToken = options.header.isToken === false + const isClinicAdminReq = options.header._clinicAdmin === '1' || (options.url && String(options.url).indexOf('clinic-admin') !== -1) + const isDoctorWxAuthReq = options.url && String(options.url).indexOf('doctor-wx-auth') !== -1 + // 内部控制位,不得作为 HTTP 头发出 + delete options.header.isToken + delete options.header._clinicAdmin const clinicToken = uni.getStorageSync('clinic_admin_token') const doctorToken = uni.getStorageSync('token') - if (!isToken) { - if (isClinicAdminReq && clinicToken) { + const loginMode = uni.getStorageSync('loginMode') + if (!skipToken) { + if (isDoctorWxAuthReq) { + if (loginMode === 'clinic_admin' && clinicToken) { + options.header['authorization'] = `Bearer ${clinicToken}` + } else if (doctorToken) { + options.header['authorization'] = `Bearer ${doctorToken}` + } else if (clinicToken) { + options.header['authorization'] = `Bearer ${clinicToken}` + } + } else if (isClinicAdminReq && clinicToken) { options.header['authorization'] = `Bearer ${clinicToken}` } else if (doctorToken) { options.header['authorization'] = `Bearer ${doctorToken}` diff --git a/components/account-switch/account-switch-entry.vue b/components/account-switch/account-switch-entry.vue new file mode 100644 index 0000000..3b57a09 --- /dev/null +++ b/components/account-switch/account-switch-entry.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/components/account-switch/account-switch-panel.vue b/components/account-switch/account-switch-panel.vue new file mode 100644 index 0000000..7095847 --- /dev/null +++ b/components/account-switch/account-switch-panel.vue @@ -0,0 +1,275 @@ + + + + + diff --git a/components/doctor-login/doctor-login.vue b/components/doctor-login/doctor-login.vue new file mode 100644 index 0000000..4e7f662 --- /dev/null +++ b/components/doctor-login/doctor-login.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/pages/login/index.vue b/pages/login/index.vue index 3d5c952..f35a7ed 100644 --- a/pages/login/index.vue +++ b/pages/login/index.vue @@ -1,473 +1,50 @@ diff --git a/pages/my/index.vue b/pages/my/index.vue index d175702..e4345ec 100644 --- a/pages/my/index.vue +++ b/pages/my/index.vue @@ -82,7 +82,7 @@ export default { components: { myService, - myPharmacist + myPharmacist, }, data() { return { diff --git a/pages/workbench/index.vue b/pages/workbench/index.vue index 3513397..e55ee16 100644 --- a/pages/workbench/index.vue +++ b/pages/workbench/index.vue @@ -10,9 +10,9 @@ - {{doctorInfo.store.store.name || '默认门店'}} - - + {{doctorInfo.store.store.name || '默认门店'}} + + @@ -885,6 +885,7 @@ page { align-items: center; font-size: 28rpx; color: #FFFFFF; + padding: 0 24rpx; margin-bottom: 24rpx; .u-icon { diff --git a/subPackages/sub_clinic_admin/home/index.vue b/subPackages/sub_clinic_admin/home/index.vue index c1f2a37..abdc455 100644 --- a/subPackages/sub_clinic_admin/home/index.vue +++ b/subPackages/sub_clinic_admin/home/index.vue @@ -1 +1,281 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/subPackages/sub_clinic_admin/my/index.vue b/subPackages/sub_clinic_admin/my/index.vue index de6c3e6..d5e54d8 100644 --- a/subPackages/sub_clinic_admin/my/index.vue +++ b/subPackages/sub_clinic_admin/my/index.vue @@ -28,6 +28,7 @@ + import ClinicPageLayout from '../components/clinic-page-layout.vue' +import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue' export default { - components: { ClinicPageLayout }, + components: { ClinicPageLayout, AccountSwitchEntry }, data() { return { displayName: '', diff --git a/subPackages/sub_clinic_admin/warehouse/detail.vue b/subPackages/sub_clinic_admin/warehouse/detail.vue index 74804da..63617c2 100644 --- a/subPackages/sub_clinic_admin/warehouse/detail.vue +++ b/subPackages/sub_clinic_admin/warehouse/detail.vue @@ -1,135 +1,316 @@ + +.highlight-row { + margin-top: 8rpx; + background: #fafbfa; /* 极浅的背景强调 */ + margin: 0 -32rpx; + padding: 32rpx; + border-bottom-left-radius: 24rpx; + border-bottom-right-radius: 24rpx; +} + +.highlight-row .label { + color: $text-primary; + font-weight: 500; +} + +.price-large { + font-size: 40rpx; + color: $theme-color; + font-weight: 600; +} + +/* 标签样式 */ +.status-tag { + font-size: 24rpx; + font-weight: 500; + padding: 6rpx 20rpx; + border-radius: 12rpx; +} + +.tag-on { + color: $theme-color; + background: $theme-color-light; +} + +.tag-off { + color: $text-secondary; + background: $bg-color; +} + +/* 底部操作按钮组 */ +.action-group { + display: flex; + flex-direction: column; + gap: 24rpx; + margin-top: 16rpx; +} + +.btn-large { + width: 100%; + height: 96rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 48rpx; + font-size: 32rpx; + font-weight: 500; + transition: opacity 0.2s; + box-sizing: border-box; +} + +.btn-primary { + background: $theme-color; + color: #ffffff; + box-shadow: 0 8rpx 24rpx $theme-color-light; +} + +.btn-danger { + background: $danger-color; + color: #ffffff; + box-shadow: 0 8rpx 24rpx $danger-color-light; +} + +.btn-outline { + background: #ffffff; + color: $theme-color; + border: 2rpx solid $theme-color; +} + +.btn-hover-opacity { + opacity: 0.7; +} + \ No newline at end of file diff --git a/subPackages/sub_clinic_admin/warehouse/index.vue b/subPackages/sub_clinic_admin/warehouse/index.vue index 1ac25f4..eb7fa5e 100644 --- a/subPackages/sub_clinic_admin/warehouse/index.vue +++ b/subPackages/sub_clinic_admin/warehouse/index.vue @@ -1,231 +1,383 @@ + +.action-btn.btn-danger { + color: $danger-color; + background: $danger-color-light; + border: 1rpx solid transparent; +} + +.btn-hover { + opacity: 0.8; +} + +.btn-hover-opacity { + opacity: 0.6; +} + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 120rpx 0; +} + +.empty-icon { + width: 200rpx; + height: 200rpx; + background-color: $bg-color; + border-radius: 50%; + margin-bottom: 30rpx; + position: relative; +} + +/* 用伪元素做一个简单的空状态图形占位,有条件可替换为真实的Image/SVG */ +.empty-icon::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80rpx; + height: 80rpx; + border: 8rpx solid $border-color; + border-radius: 16rpx; +} + +.empty-text { + color: $text-light; + font-size: 28rpx; +} + \ No newline at end of file diff --git a/subPackages/sub_my/my_setAct.vue b/subPackages/sub_my/my_setAct.vue index 18859d0..def8d5b 100644 --- a/subPackages/sub_my/my_setAct.vue +++ b/subPackages/sub_my/my_setAct.vue @@ -36,6 +36,10 @@ --> + + + + 退出登录 @@ -49,8 +53,10 @@ logout, toUpload, } from '@/api/all.js' + import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue' import { chooseAvatarImage } from '@/common/js/select-avatar-image.js'; export default { + components: { AccountSwitchEntry }, data() { return { doctorInfo: [] diff --git a/subPackages/sub_pharmacist/pharmacist_setInfo.vue b/subPackages/sub_pharmacist/pharmacist_setInfo.vue index 4c1bc44..a3418d0 100644 --- a/subPackages/sub_pharmacist/pharmacist_setInfo.vue +++ b/subPackages/sub_pharmacist/pharmacist_setInfo.vue @@ -22,6 +22,9 @@ 40056256325 + + + 退出登录 @@ -33,7 +36,9 @@ import { logout } from '@/api/all.js' + import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue' export default { + components: { AccountSwitchEntry }, data() { return {} }, diff --git a/utils/loginSession.js b/utils/loginSession.js new file mode 100644 index 0000000..7cf7bf4 --- /dev/null +++ b/utils/loginSession.js @@ -0,0 +1,116 @@ +import { getClinicAdminMyInfo } from '@/api/clinicAdmin.js'; +import { getUserInfo } from '@/api/all.js'; + +function applyServiceUserProfile(user) { + if (!user || !user.id) return; + uni.setStorageSync('loginInfo', user); + uni.setStorageSync('role', user.role); + uni.setStorageSync('login_id', user.id); + uni.setStorageSync('login_status', user.status); +} + +export function saveLoginSession(result) { + if (!result) return; + + const token = result.token ? String(result.token).trim() : ''; + + if (result.account_type === 'admin') { + uni.setStorageSync('loginMode', 'clinic_admin'); + uni.setStorageSync('clinic_admin_token', token); + uni.setStorageSync('clinic_admin_user', result.user || {}); + uni.removeStorageSync('token'); + return; + } + + uni.removeStorageSync('loginMode'); + uni.removeStorageSync('clinic_admin_token'); + uni.removeStorageSync('clinic_admin_user'); + uni.setStorageSync('token', token); + + const user = result.user || {}; + const storeDoctor = result.store_doctor || {}; + uni.setStorageSync('identity', user.role); + uni.setStorageSync('logInfo', user); + uni.setStorageSync('store_id', storeDoctor.store_id || '11001'); + if (user.id) { + uni.setStorageSync('doctor_id', user.id); + uni.setStorageSync('user_id', user.id); + } +} + +export async function fetchProfileAfterLogin(result) { + if (result.account_type === 'admin') { + const wrap = await getClinicAdminMyInfo(); + if (wrap && wrap.ok) { + uni.setStorageSync('clinic_admin_user', wrap.data); + } + return; + } + + if (result.user && result.user.id) { + applyServiceUserProfile(result.user); + return; + } + + const data = { + store_id: uni.getStorageSync('store_id') || '11001', + }; + const res = await getUserInfo(data); + if (res && res.errcode != -1) { + applyServiceUserProfile(res.data); + } +} + +export function navigateAfterLogin(result) { + const hint = result.redirect_hint; + const role = Number(result.role || 0); + const status = Number(result.status ?? result.user?.status ?? 0); + + if (result.account_type === 'admin' || hint === 'clinic_admin_home') { + uni.reLaunch({ url: '/subPackages/sub_clinic_admin/home/index' }); + return; + } + + if (hint === 'complete_profile' || status === 0) { + uni.showToast({ title: '您还没有完善信息,请先完善信息', icon: 'none' }); + setTimeout(() => { + uni.navigateTo({ url: '/subPackages/sub_workbench/workbench_upInfo/index' }); + }, 600); + return; + } + + if (hint === 'examine' || status === 1) { + uni.showToast({ title: '您的账号还在审核中', icon: 'none' }); + setTimeout(() => { + uni.reLaunch({ url: '/pages/workbench/examine' }); + }, 600); + return; + } + + if (status === 3) { + setTimeout(() => { + uni.reLaunch({ url: '/pages/workbench/examine' }); + }, 600); + return; + } + + if (hint === 'pharmacist_home' || role === 2) { + setTimeout(() => { + uni.reLaunch({ url: '/pages/pharmacist/index' }); + }, 400); + return; + } + + setTimeout(() => { + uni.reLaunch({ url: '/pages/workbench/index' }); + }, 400); +} + +export function clearLoginStorage() { + uni.removeStorageSync('token'); + uni.removeStorageSync('clinic_admin_token'); + uni.removeStorageSync('clinic_admin_user'); + uni.removeStorageSync('loginMode'); + uni.removeStorageSync('loginInfo'); + uni.removeStorageSync('logInfo'); +}