1. 更新切换账号逻辑
2. 诊所管理员功能上线
This commit is contained in:
53
api/doctorAuth.js
Normal file
53
api/doctorAuth.js
Normal file
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -21,12 +21,11 @@ export async function requestConfig(ins, options, successHandler = null, failHan
|
|||||||
}
|
}
|
||||||
options.url = options.url.replace('/oldApi/', '');
|
options.url = options.url.replace('/oldApi/', '');
|
||||||
options.url = options.url.replace('/newApi/', '');
|
options.url = options.url.replace('/newApi/', '');
|
||||||
// base
|
const defaultHeader = { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
ins.header = options.header || ins.header
|
// 每次请求独立合并 header,避免登录 isToken:false 污染单例
|
||||||
// config base
|
|
||||||
let config = {
|
let config = {
|
||||||
url:`${baseUrl}${options.url?options.url:''}`,
|
url:`${baseUrl}${options.url?options.url:''}`,
|
||||||
header:ins.header
|
header: { ...defaultHeader, ...(options.header || {}) }
|
||||||
}
|
}
|
||||||
if(ins.requestInterceptor){
|
if(ins.requestInterceptor){
|
||||||
let _cg = null
|
let _cg = null
|
||||||
|
|||||||
@@ -40,13 +40,26 @@ const reqInterceptor = async (options) => {
|
|||||||
options.header = {
|
options.header = {
|
||||||
...options.header
|
...options.header
|
||||||
}
|
}
|
||||||
const isToken = (options.header || {}).isToken === false
|
const skipToken = options.header.isToken === false
|
||||||
const isClinicAdminReq = (options.header || {})._clinicAdmin === '1'
|
const isClinicAdminReq = options.header._clinicAdmin === '1'
|
||||||
|| (options.url && String(options.url).indexOf('clinic-admin') !== -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 clinicToken = uni.getStorageSync('clinic_admin_token')
|
||||||
const doctorToken = uni.getStorageSync('token')
|
const doctorToken = uni.getStorageSync('token')
|
||||||
if (!isToken) {
|
const loginMode = uni.getStorageSync('loginMode')
|
||||||
if (isClinicAdminReq && clinicToken) {
|
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}`
|
options.header['authorization'] = `Bearer ${clinicToken}`
|
||||||
} else if (doctorToken) {
|
} else if (doctorToken) {
|
||||||
options.header['authorization'] = `Bearer ${doctorToken}`
|
options.header['authorization'] = `Bearer ${doctorToken}`
|
||||||
|
|||||||
84
components/account-switch/account-switch-entry.vue
Normal file
84
components/account-switch/account-switch-entry.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<view v-if="canSwitch">
|
||||||
|
<view
|
||||||
|
class="switch-row"
|
||||||
|
hover-class="switch-row-hover"
|
||||||
|
:hover-stay-time="100"
|
||||||
|
@tap.stop="openSwitch"
|
||||||
|
>
|
||||||
|
<text class="switch-label">切换账号</text>
|
||||||
|
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
||||||
|
</view>
|
||||||
|
<account-switch-panel
|
||||||
|
ref="accountSwitchPanel"
|
||||||
|
mode="switch"
|
||||||
|
@switched="onSwitched"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AccountSwitchPanel from '@/components/account-switch/account-switch-panel.vue';
|
||||||
|
import { getDoctorWxSiblingAccounts } from '@/api/doctorAuth.js';
|
||||||
|
import {
|
||||||
|
saveLoginSession,
|
||||||
|
fetchProfileAfterLogin,
|
||||||
|
navigateAfterLogin,
|
||||||
|
} from '@/utils/loginSession.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccountSwitchEntry',
|
||||||
|
components: { AccountSwitchPanel },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
canSwitch: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.checkAccounts();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkAccounts() {
|
||||||
|
getDoctorWxSiblingAccounts()
|
||||||
|
.then((wrap) => {
|
||||||
|
if (wrap && wrap.ok) {
|
||||||
|
this.canSwitch = (wrap.data || []).length > 1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.canSwitch = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openSwitch() {
|
||||||
|
this.$refs.accountSwitchPanel.open();
|
||||||
|
},
|
||||||
|
async onSwitched(result) {
|
||||||
|
saveLoginSession(result);
|
||||||
|
await fetchProfileAfterLogin(result);
|
||||||
|
this.$toast('切换成功');
|
||||||
|
setTimeout(() => {
|
||||||
|
navigateAfterLogin(result);
|
||||||
|
}, 400);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.switch-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 36rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f2f3f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-row-hover {
|
||||||
|
background: #f7f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-label {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #31353d;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
275
components/account-switch/account-switch-panel.vue
Normal file
275
components/account-switch/account-switch-panel.vue
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<page-container
|
||||||
|
v-if="mode === 'switch'"
|
||||||
|
:show="visible"
|
||||||
|
:overlay="false"
|
||||||
|
@leave="handlePageLeave"
|
||||||
|
/>
|
||||||
|
<!-- #endif -->
|
||||||
|
<u-popup
|
||||||
|
v-model="visible"
|
||||||
|
mode="bottom"
|
||||||
|
length="80%"
|
||||||
|
border-radius="24"
|
||||||
|
z-index="10078"
|
||||||
|
:closeable="true"
|
||||||
|
:mask-close-able="true"
|
||||||
|
:safe-area-inset-bottom="true"
|
||||||
|
@close="handleCancel"
|
||||||
|
>
|
||||||
|
<view class="sheet-inner white">
|
||||||
|
<view class="sheet-title">{{ mode === 'switch' ? '切换账号' : '选择登录账号' }}</view>
|
||||||
|
<scroll-view scroll-y class="account-scroll">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in accounts"
|
||||||
|
:key="item._rowKey"
|
||||||
|
class="account-item"
|
||||||
|
:class="{ active: isSelected(item) }"
|
||||||
|
hover-class="account-item-hover"
|
||||||
|
@tap.stop="selectAccount(item)"
|
||||||
|
>
|
||||||
|
<view class="name">{{ item.nick_name }}</view>
|
||||||
|
<view class="meta">
|
||||||
|
{{ item.role_name }}<text v-if="item.store_name"> · {{ item.store_name }}</text><text v-if="item.login_account"> · {{ item.login_account }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.is_current" class="current-tag">当前</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<view class="actions">
|
||||||
|
<button class="btn cancel" @tap.stop="handleCancel">取消</button>
|
||||||
|
<button class="btn confirm" :loading="loading" @tap.stop="handleConfirm">确认</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { switchDoctorWxAccount, getDoctorWxSiblingAccounts } from '@/api/doctorAuth.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccountSwitchPanel',
|
||||||
|
props: {
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'login',
|
||||||
|
},
|
||||||
|
phone: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
accounts: [],
|
||||||
|
selected: null,
|
||||||
|
loading: false,
|
||||||
|
defaultAccountId: null,
|
||||||
|
defaultAccountType: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
normalizeAccounts(accounts) {
|
||||||
|
return (accounts || []).map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
_rowKey: `${item.account_type}_${item.id}_${index}`,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
resolveDefaultSelection(accounts, options = {}) {
|
||||||
|
const list = accounts || [];
|
||||||
|
if (!list.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mode === 'switch') {
|
||||||
|
return list.find((item) => item.is_current) || list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultId = options.defaultAccountId ?? this.defaultAccountId;
|
||||||
|
const defaultType = options.defaultAccountType ?? this.defaultAccountType;
|
||||||
|
if (defaultId != null && defaultType) {
|
||||||
|
const matched = list.find(
|
||||||
|
(item) => item.account_type === defaultType && Number(item.id) === Number(defaultId)
|
||||||
|
);
|
||||||
|
if (matched) {
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.find((item) => item.password_matched) || list[0];
|
||||||
|
},
|
||||||
|
showPanel() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.visible = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
open(accounts = null, options = {}) {
|
||||||
|
this.defaultAccountId = options.defaultAccountId ?? null;
|
||||||
|
this.defaultAccountType = options.defaultAccountType ?? null;
|
||||||
|
|
||||||
|
if (Array.isArray(accounts)) {
|
||||||
|
this.accounts = this.normalizeAccounts(accounts);
|
||||||
|
this.selected = this.resolveDefaultSelection(this.accounts, options);
|
||||||
|
this.showPanel();
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDoctorWxSiblingAccounts().then((wrap) => {
|
||||||
|
if (!wrap || !wrap.ok) {
|
||||||
|
return Promise.reject(new Error('获取账号列表失败'));
|
||||||
|
}
|
||||||
|
this.accounts = this.normalizeAccounts(wrap.data || []);
|
||||||
|
this.selected = this.resolveDefaultSelection(this.accounts, options);
|
||||||
|
this.showPanel();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
handlePageLeave() {
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
isSelected(item) {
|
||||||
|
return this.selected && this.selected._rowKey === item._rowKey;
|
||||||
|
},
|
||||||
|
selectAccount(item) {
|
||||||
|
this.selected = item;
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.close();
|
||||||
|
this.$emit('cancel');
|
||||||
|
},
|
||||||
|
handleConfirm() {
|
||||||
|
if (!this.selected) {
|
||||||
|
this.$toast('请选择账号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.mode === 'switch') {
|
||||||
|
if (this.selected.is_current) {
|
||||||
|
this.$toast('已是当前账号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.submitSwitch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$emit('confirm', this.selected);
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
resolveSwitchError(wrap, err) {
|
||||||
|
return wrap?.res?.msg
|
||||||
|
|| wrap?.res?.message
|
||||||
|
|| err?.msg
|
||||||
|
|| err?.message
|
||||||
|
|| '切换失败';
|
||||||
|
},
|
||||||
|
submitSwitch() {
|
||||||
|
this.loading = true;
|
||||||
|
switchDoctorWxAccount({
|
||||||
|
account_type: this.selected.account_type,
|
||||||
|
account_id: this.selected.id,
|
||||||
|
}).then((wrap) => {
|
||||||
|
if (!wrap || !wrap.ok || !wrap.data || !wrap.data.token) {
|
||||||
|
return Promise.reject({
|
||||||
|
message: this.resolveSwitchError(wrap),
|
||||||
|
wrap,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.$emit('switched', wrap.data);
|
||||||
|
this.close();
|
||||||
|
}).catch((err) => {
|
||||||
|
const message = this.resolveSwitchError(err?.wrap, err);
|
||||||
|
uni.showToast({
|
||||||
|
title: message,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2500,
|
||||||
|
});
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.sheet-inner {
|
||||||
|
padding: 32rpx 32rpx calc(32rpx + env(safe-area-inset-bottom));
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-title {
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #222b2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-scroll {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-item {
|
||||||
|
position: relative;
|
||||||
|
padding: 24rpx;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-item.active {
|
||||||
|
border-color: #6acdbb;
|
||||||
|
background: #f0fbf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-item-hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
padding-right: 80rpx;
|
||||||
|
color: #222b2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-tag {
|
||||||
|
position: absolute;
|
||||||
|
right: 24rpx;
|
||||||
|
top: 24rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #6acdbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm {
|
||||||
|
background: #6acdbb;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
284
components/doctor-login/doctor-login.vue
Normal file
284
components/doctor-login/doctor-login.vue
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container safe-area-inset-bottom">
|
||||||
|
<view class="title">欢迎登录</view>
|
||||||
|
<view class="box">
|
||||||
|
<view class="info">
|
||||||
|
<view class="pwd">
|
||||||
|
<d-input
|
||||||
|
:maxlength="11"
|
||||||
|
height="96"
|
||||||
|
v-model="form.mobile"
|
||||||
|
:prefixIcon="require('../../static/image/act.png')"
|
||||||
|
borderRadius="96rpx"
|
||||||
|
prefixIconSize="40rpx"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
:placeholder-style="{ fontSize: '32rpx' }"
|
||||||
|
:custom-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="pwd">
|
||||||
|
<d-input
|
||||||
|
height="96"
|
||||||
|
v-model="form.password"
|
||||||
|
type="password"
|
||||||
|
:prefixIcon="require('../../static/image/pwd.png')"
|
||||||
|
password
|
||||||
|
borderRadius="96rpx"
|
||||||
|
prefixIconSize="40rpx"
|
||||||
|
placeholder="请输入登录密码"
|
||||||
|
:placeholder-style="{ fontSize: '32rpx' }"
|
||||||
|
:custom-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="pwd">
|
||||||
|
<u-field v-model="smsCode"
|
||||||
|
placeholder="请填写验证码"
|
||||||
|
:prefixIcon="require('../../static/image/pwd.png')"
|
||||||
|
:placeholder-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
|
||||||
|
:custom-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
|
||||||
|
>
|
||||||
|
<template slot="icon">
|
||||||
|
<!-- <image src="../../../../static/image/pwd.png"></image>-->
|
||||||
|
<u-icon
|
||||||
|
size="40"
|
||||||
|
name="chat"
|
||||||
|
style="margin-left: 15rpx"
|
||||||
|
@click="form.check = !form.check"
|
||||||
|
></u-icon>
|
||||||
|
</template>
|
||||||
|
<u-button size="mini" slot="right" @click="getCode">{{ codeText }}</u-button>
|
||||||
|
</u-field>
|
||||||
|
<u-verification-code ref="uCode" @change="codeChange" class="code"></u-verification-code>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex-row" style="margin-top: 88rpx;">
|
||||||
|
<u-icon
|
||||||
|
size="35"
|
||||||
|
:color="form.check ? '#6ACDBB' : '#e0fdff'"
|
||||||
|
name="checkmark-circle-fill"
|
||||||
|
@click="form.check = !form.check"
|
||||||
|
></u-icon>
|
||||||
|
<view class="agreement">
|
||||||
|
我已阅读并同意
|
||||||
|
<text @click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')">《用户服务协议》</text>
|
||||||
|
和
|
||||||
|
<text @click="$go('/subPackages/sub_agreement/agreement?type=service_privacy_agreement')">《隐私权政策》</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="btn">
|
||||||
|
<u-button :throttle-time="20" shape="circle" @click="submitLogin" :ripple="true" :loading="loading">
|
||||||
|
登录
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { sendDoctorWxCode, doctorWxLogin } from '@/api/doctorAuth.js';
|
||||||
|
import {
|
||||||
|
saveLoginSession,
|
||||||
|
fetchProfileAfterLogin,
|
||||||
|
navigateAfterLogin,
|
||||||
|
} from '@/utils/loginSession.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DoctorLogin',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
mobile: '',
|
||||||
|
password: '',
|
||||||
|
check: false,
|
||||||
|
},
|
||||||
|
smsCode: '',
|
||||||
|
codeText: '',
|
||||||
|
loading: false,
|
||||||
|
pendingLoginPayload: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
codeChange(text) {
|
||||||
|
this.codeText = text;
|
||||||
|
},
|
||||||
|
getCode() {
|
||||||
|
if (!this.$refs.uCode.canGetCode) {
|
||||||
|
this.$u.toast('倒计时结束后再发送');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.form.mobile) {
|
||||||
|
this.$toast('请输入手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.form.password) {
|
||||||
|
this.$toast('请输入密码');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uni.showLoading({ title: '正在获取验证码' });
|
||||||
|
sendDoctorWxCode(this.form.mobile, this.form.password)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
this.$refs.uCode.start();
|
||||||
|
if (res.result && res.result.code) {
|
||||||
|
this.smsCode = res.result.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uni.hideLoading();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitLogin() {
|
||||||
|
if (!this.form.mobile) {
|
||||||
|
this.$toast('请输入手机号');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.smsCode) {
|
||||||
|
this.$toast('请输入验证码');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.form.check) {
|
||||||
|
this.$toast('请勾选协议');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.doLogin({});
|
||||||
|
},
|
||||||
|
doLogin(extra = {}) {
|
||||||
|
this.loading = true;
|
||||||
|
const payload = {
|
||||||
|
phone: this.form.mobile,
|
||||||
|
password: this.form.password,
|
||||||
|
code: this.smsCode,
|
||||||
|
...extra,
|
||||||
|
};
|
||||||
|
doctorWxLogin(payload)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code !== 0 || !res.result) {
|
||||||
|
return Promise.reject(new Error(res.message || res.msg || '登录失败'));
|
||||||
|
}
|
||||||
|
const result = res.result;
|
||||||
|
if (result.need_select && result.accounts && result.accounts.length) {
|
||||||
|
this.pendingLoginPayload = payload;
|
||||||
|
this.$emit('need-select', {
|
||||||
|
accounts: result.accounts,
|
||||||
|
defaultAccountId: result.default_account_id,
|
||||||
|
defaultAccountType: result.default_account_type,
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.finishLogin(result);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.$toast(err.message || '登录失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onAccountPicked(account) {
|
||||||
|
this.doLogin({
|
||||||
|
account_type: account.account_type,
|
||||||
|
account_id: account.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async finishLogin(result) {
|
||||||
|
saveLoginSession(result);
|
||||||
|
await fetchProfileAfterLogin(result);
|
||||||
|
this.$toast('登录成功');
|
||||||
|
if (result.account_type !== 'admin') {
|
||||||
|
uni.$emit('user-login');
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
navigateAfterLogin(result);
|
||||||
|
}, 400);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 750rpx;
|
||||||
|
padding-top: 200rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-family: PingFang SC-Regular, PingFang SC;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #0D111A;
|
||||||
|
background: linear-gradient(180deg, #EAF2FF 0%, #FFFFFF 100%);
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 34rpx;
|
||||||
|
|
||||||
|
.agreement {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-family: PingFang SC-Regular, PingFang SC;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #6C7380;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
|
||||||
|
text {
|
||||||
|
color: #2979FF;
|
||||||
|
size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 52rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-family: PingFang SC-Regular, PingFang SC;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #1777FF;
|
||||||
|
background: #F9FAFB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pwd {
|
||||||
|
width: 646rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
background: #F9FAFB;
|
||||||
|
border-radius: 48rpx 48rpx 48rpx 48rpx;
|
||||||
|
font-family: PingFang SC-Regular, PingFang SC;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #CCCCCC;
|
||||||
|
margin: 0rpx 0 30rpx;
|
||||||
|
|
||||||
|
::v-deep button {
|
||||||
|
color: #1777FF;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
::v-deep button {
|
||||||
|
width: 648rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
line-height: 100rpx;
|
||||||
|
background: #6ACDBB;
|
||||||
|
border: 0;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-family: PingFang SC-Regular, PingFang SC;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,473 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container safe-area-inset-bottom">
|
<view>
|
||||||
|
<doctor-login ref="doctorLogin" @need-select="openAccountPicker" />
|
||||||
<view class="title">
|
<account-switch-panel
|
||||||
欢迎登录
|
ref="accountSwitchPanel"
|
||||||
</view>
|
mode="login"
|
||||||
|
@confirm="onAccountPicked"
|
||||||
<view class="box">
|
/>
|
||||||
<view class="info">
|
|
||||||
<view class="pwd">
|
|
||||||
<d-input :maxlength="11" height="96" v-model="form['mobile']"
|
|
||||||
:prefixIcon="require('../../static/image/act.png')" borderRadius="96rpx" prefixIconSize="40rpx"
|
|
||||||
:placeholder="loginMode === 'clinic_admin' ? '请输入手机号' : '请输入手机号'" @input="is_d=false" :placeholder-style="{fontSize:'32rpx'}"
|
|
||||||
:custom-style="{fontSize:'32rpx',paddingLeft:'60rpx'}" />
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="pwd" v-if="loginMode === 'clinic_admin'">
|
|
||||||
<d-input height="96" v-model="clinicPassword" type="password" password
|
|
||||||
borderRadius="96rpx" placeholder="请输入后台登录密码" :placeholder-style="{fontSize:'32rpx'}"
|
|
||||||
:custom-style="{fontSize:'32rpx',paddingLeft:'32rpx'}" />
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="pwd">
|
|
||||||
<u-field v-model="smsCode" placeholder="请填写验证码" :placeholder-style="{fontSize:'32rpx'}">
|
|
||||||
<u-button size="mini" slot="right" @click="getCode">{{codeText}}</u-button>
|
|
||||||
</u-field>
|
|
||||||
<u-verification-code ref="uCode" @change="codeChange" class="code">
|
|
||||||
</u-verification-code>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="flex-row" style="margin-top: 88rpx;">
|
|
||||||
<u-icon size="35" :color="form['check']?'#6ACDBB':'#e0fdff'" name="checkmark-circle-fill"
|
|
||||||
@click="form['check']=!form['check']"></u-icon>
|
|
||||||
<view class="agreement">
|
|
||||||
我已阅读并同意
|
|
||||||
<text
|
|
||||||
@click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')">《用户服务协议》</text>
|
|
||||||
和
|
|
||||||
<text
|
|
||||||
@click="$go('/subPackages/sub_agreement/agreement?type=service_privacy_agreement')">《隐私权政策》</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<!-- :disabled="!form['mobile']||!form['pwd']" -->
|
|
||||||
<view class="btn">
|
|
||||||
<u-button :throttle-time="20" shape="circle" @click="register" :ripple="true" :loading="loading">
|
|
||||||
登录</u-button>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="register">
|
|
||||||
<text @click="switchClinicAdmin">{{ loginMode === 'clinic_admin' ? '返回医生/药师登录' : '我是诊所管理员' }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- <view class="register">-->
|
|
||||||
<!-- <text @click="$go('../../subPackages/sub_workbench/workbench_identity')">手机号注册</text>-->
|
|
||||||
<!-- </view>-->
|
|
||||||
</view>
|
|
||||||
|
|
||||||
</view>
|
|
||||||
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
import DoctorLogin from '@/components/doctor-login/doctor-login.vue';
|
||||||
login,
|
import AccountSwitchPanel from '@/components/account-switch/account-switch-panel.vue';
|
||||||
register,
|
|
||||||
agreement,
|
|
||||||
getUserInfo,
|
|
||||||
loginOrRe
|
|
||||||
} from '../../api/all';
|
|
||||||
import {
|
|
||||||
sendClinicAdminCode,
|
|
||||||
clinicAdminLogin,
|
|
||||||
getClinicAdminMyInfo,
|
|
||||||
} from '../../api/clinicAdmin';
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loginMode: 'service_user',
|
|
||||||
loading: false,
|
|
||||||
form: {
|
|
||||||
check: false
|
|
||||||
},
|
|
||||||
switchVal: false,
|
|
||||||
is_d: false,
|
|
||||||
statusBarHeight: this.$statusBarHeight,
|
|
||||||
navbarHeight: this.$navbarHeight,
|
|
||||||
mobile: '',
|
|
||||||
codeText: '',
|
|
||||||
smsCode: '',
|
|
||||||
clinicPassword: '',
|
|
||||||
code: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onLoad(e) {
|
|
||||||
|
|
||||||
// const accountInfo = uni.getAccountInfoSync();
|
export default {
|
||||||
// console.log('appid', accountInfo.miniProgram.appId); // 小程序 appId
|
components: { DoctorLogin, AccountSwitchPanel },
|
||||||
},
|
onShow() {
|
||||||
mounted() {
|
const clinicToken = uni.getStorageSync('clinic_admin_token');
|
||||||
const clinicToken = uni.getStorageSync('clinic_admin_token')
|
const token = uni.getStorageSync('token');
|
||||||
if (clinicToken && uni.getStorageSync('loginMode') === 'clinic_admin') {
|
const loginMode = uni.getStorageSync('loginMode');
|
||||||
setTimeout(() => {
|
if (loginMode === 'clinic_admin' && clinicToken) {
|
||||||
uni.reLaunch({ url: '/subPackages/sub_clinic_admin/home/index' })
|
uni.reLaunch({ url: '/subPackages/sub_clinic_admin/home/index' });
|
||||||
}, 200)
|
return;
|
||||||
return
|
}
|
||||||
}
|
if (token) {
|
||||||
const token = uni.getStorageSync('token')
|
const role = Number(uni.getStorageSync('identity') || uni.getStorageSync('role') || 0);
|
||||||
const info = uni.getStorageSync('loginInfo')
|
if (role === 2) {
|
||||||
if (token&&info) {
|
uni.reLaunch({ url: '/pages/pharmacist/index' });
|
||||||
// 药师
|
} else {
|
||||||
if (info.role == 2 && info.status > 0 && info!= 3) {
|
uni.reLaunch({ url: '/pages/workbench/index' });
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/pharmacist/index', 3)
|
|
||||||
}, 200)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 医生
|
|
||||||
if (info.role == 1 && info.status > 0 && info.status != 3) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/workbench/index', 2)
|
|
||||||
}, 200)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
switchClinicAdmin() {
|
|
||||||
this.loginMode = this.loginMode === 'clinic_admin' ? 'service_user' : 'clinic_admin'
|
|
||||||
this.clinicPassword = ''
|
|
||||||
this.smsCode = ''
|
|
||||||
},
|
|
||||||
toLogin() {
|
|
||||||
uni.login({
|
|
||||||
provider: 'weixin',
|
|
||||||
success: (loginRes) => {
|
|
||||||
this.code = loginRes.code
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
//登录或者注册接口
|
|
||||||
register() {
|
|
||||||
if (!uni.$u.test.mobile(this.form['mobile'])) {
|
|
||||||
this.$toast('请输入正确的手机号')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.smsCode) {
|
|
||||||
this.$toast('请输入验证码')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.form['check']) {
|
|
||||||
this.$toast('请勾选协议')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.loginMode === 'clinic_admin') {
|
|
||||||
if (!this.clinicPassword) {
|
|
||||||
this.$toast('请输入后台登录密码')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.clinicAdminLoginFlow()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
store_id: '11001',
|
|
||||||
mobile: this.form['mobile'],
|
|
||||||
status: 3,
|
|
||||||
smsCode: this.smsCode,
|
|
||||||
code: this.code
|
|
||||||
}
|
|
||||||
|
|
||||||
loginOrRe(data).then(res => {
|
|
||||||
if (res.errcode != -1) {
|
|
||||||
this.$toast('登录成功')
|
|
||||||
// this.$toast(res.data[0])
|
|
||||||
uni.setStorageSync('token', res.data['token'])
|
|
||||||
// console.log(res);
|
|
||||||
uni.setStorageSync('identity', res.data['ServiceUser']['role'])
|
|
||||||
uni.setStorageSync('logInfo', res.data['ServiceUser'])
|
|
||||||
// 记录店铺id
|
|
||||||
uni.setStorageSync('store_id', res.data['StoreDoctor']['store_id'])
|
|
||||||
|
|
||||||
// 存储doctor_id或user_id,用于WebSocket绑定
|
|
||||||
if (res.data['ServiceUser'] && res.data['ServiceUser']['id']) {
|
|
||||||
uni.setStorageSync('doctor_id', res.data['ServiceUser']['id']);
|
|
||||||
uni.setStorageSync('user_id', res.data['ServiceUser']['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触发WebSocket初始化(登录成功后)
|
|
||||||
uni.$emit('user-login');
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
store_id: uni.getStorageSync('store_id') || res.data['StoreDoctor']['store_id'] || '11001',
|
|
||||||
}
|
|
||||||
getUserInfo(data).then(res => {
|
|
||||||
if (res.errcode != -1) {
|
|
||||||
uni.setStorageSync('loginInfo', res.data)
|
|
||||||
uni.setStorageSync('role', res.data.role)
|
|
||||||
uni.setStorageSync('login_id', res.data['id'])
|
|
||||||
uni.setStorageSync('login_status', res.data['status'])
|
|
||||||
}
|
|
||||||
// 未完善信息
|
|
||||||
if (res.data.status == 0 ) {
|
|
||||||
this.$toast('您还没有完善信息,请先完善信息')
|
|
||||||
// this.$go('../../subPackages/sub_workbench/workbench_upInfo/index')
|
|
||||||
uni.navigateTo({
|
|
||||||
url:'../../subPackages/sub_workbench/workbench_upInfo/index'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}).catch(() => {
|
|
||||||
this.loading = false
|
|
||||||
this.$toast(res.msg)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// 药师
|
|
||||||
if (res.data.ServiceUser.role == 2 && res.data.ServiceUser.status ==2 ) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/pharmacist/index', 3)
|
|
||||||
}, 600)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 医生 res.data.ServiceUser.status > 0 && res.data .ServiceUser.status != 3
|
|
||||||
if (res.data.ServiceUser.role == 1 && res.data.ServiceUser.status==2 ) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/workbench/index', 2)
|
|
||||||
}, 600)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 医生未通过审核
|
|
||||||
if (res.data.ServiceUser.role == 1 && res.data.ServiceUser.status == 3) {
|
|
||||||
// this.$toast(res.data.ServiceUser.reason)
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/workbench/examine')
|
|
||||||
}, 2000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 医生审核中
|
|
||||||
if (res.data.ServiceUser.role == 1 && res.data.ServiceUser.status == 1) {
|
|
||||||
this.$toast('您的账号还在审核中')
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/workbench/examine')
|
|
||||||
}, 2000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 药师未通过审核
|
|
||||||
if (res.data.ServiceUser.role == 2 && res.data.ServiceUser.status == 3) {
|
|
||||||
// this.$toast(res.data.ServiceUser.reason)
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/workbench/examine')
|
|
||||||
}, 2000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 药师审核中
|
|
||||||
if (res.data.ServiceUser.role == 2 && res.data.ServiceUser.status == 1) {
|
|
||||||
this.$toast('您的账号还在审核中')
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$go('/pages/workbench/examine')
|
|
||||||
}, 2000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.errcode == -1) {
|
|
||||||
// this.$toast('您还没有账号,请先注册')
|
|
||||||
// setTimeout(() => {
|
|
||||||
// this.$go('../../subPackages/sub_workbench/workbench_identity')
|
|
||||||
// }, 600)
|
|
||||||
|
|
||||||
this.$toast(res.msg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
checkboxChange(val) {
|
|
||||||
// console.log(val);
|
|
||||||
},
|
|
||||||
codeChange(text) {
|
|
||||||
this.codeText = text;
|
|
||||||
},
|
|
||||||
clinicAdminLoginFlow() {
|
|
||||||
this.loading = true
|
|
||||||
clinicAdminLogin(this.form['mobile'], this.clinicPassword, this.smsCode).then(res => {
|
|
||||||
if (res.code !== 0 || !res.result || !res.result.token) {
|
|
||||||
this.$toast(res.message || res.msg || '登录失败')
|
|
||||||
return Promise.reject()
|
|
||||||
}
|
|
||||||
uni.setStorageSync('loginMode', 'clinic_admin')
|
|
||||||
uni.setStorageSync('clinic_admin_token', res.result.token)
|
|
||||||
return getClinicAdminMyInfo()
|
|
||||||
}).then((wrap) => {
|
|
||||||
if (!wrap || !wrap.ok) return
|
|
||||||
uni.setStorageSync('clinic_admin_user', wrap.data)
|
|
||||||
this.$toast('登录成功')
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.reLaunch({ url: '/subPackages/sub_clinic_admin/home/index' })
|
|
||||||
}, 400)
|
|
||||||
}).catch(() => {}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getCode() {
|
|
||||||
if (this.$refs.uCode.canGetCode) {
|
|
||||||
uni.showLoading({ title: '正在获取验证码' })
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.hideLoading();
|
|
||||||
if (this.loginMode === 'clinic_admin') {
|
|
||||||
if (!this.clinicPassword) {
|
|
||||||
this.$toast('请先输入后台登录密码')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sendClinicAdminCode(this.form['mobile']).then(res => {
|
|
||||||
if (res.code === 0 || res.errcode != -1) {
|
|
||||||
this.$refs.uCode.start();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const data = {
|
|
||||||
store_id: '11001',
|
|
||||||
mobile: this.form['mobile']
|
|
||||||
}
|
|
||||||
register(data).then(res => {
|
|
||||||
// console.log(res);
|
|
||||||
if (res.errcode != -1) {
|
|
||||||
// this.code = res.data.code
|
|
||||||
uni.setStorageSync('smsCode', res.data['smsCode'])
|
|
||||||
// if(this.code==res.data.smsCode){
|
|
||||||
// this.$go('../../subPackages/sub_workbench/workbench_upInfo/index')
|
|
||||||
// }else{
|
|
||||||
// this.$u.toast('验证码错误')
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.$refs.uCode.start();
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
this.$u.toast('倒计时结束后再发送');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
methods: {
|
||||||
|
openAccountPicker({ accounts, defaultAccountId, defaultAccountType }) {
|
||||||
|
this.$refs.accountSwitchPanel.open(accounts, {
|
||||||
|
defaultAccountId,
|
||||||
|
defaultAccountType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onAccountPicked(account) {
|
||||||
|
this.$refs.doctorLogin.onAccountPicked(account);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
padding-bottom: env(safe-area-inset-bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
width: 750rpx;
|
|
||||||
padding-top: 200rpx;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 48rpx;
|
|
||||||
font-family: PingFang SC-Regular, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #0D111A;
|
|
||||||
background: linear-gradient(180deg, #EAF2FF 0%, #FFFFFF 100%);
|
|
||||||
margin-bottom: 60rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-bottom: 34rpx;
|
|
||||||
|
|
||||||
.agreement {
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-family: PingFang SC-Regular, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #6C7380;
|
|
||||||
margin-left: 16rpx;
|
|
||||||
|
|
||||||
text {
|
|
||||||
color: #2979FF;
|
|
||||||
size: 24rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 52rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-family: PingFang SC-Regular, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #1777FF;
|
|
||||||
background: #F9FAFB;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agreement {
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-family: PingFang SC-Regular, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #6C7380;
|
|
||||||
margin-left: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pwd {
|
|
||||||
width: 646rpx;
|
|
||||||
height: 96rpx;
|
|
||||||
background: #F9FAFB;
|
|
||||||
border-radius: 48rpx 48rpx 48rpx 48rpx;
|
|
||||||
font-family: PingFang SC-Regular, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #CCCCCC;
|
|
||||||
margin: 0rpx 0 30rpx;
|
|
||||||
|
|
||||||
::v-deep button {
|
|
||||||
color: #1777FF;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
|
||||||
margin-top: 80rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-family: PingFang SC-Regular, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #6C7380;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
::v-deep button {
|
|
||||||
width: 648rpx;
|
|
||||||
height: 100rpx;
|
|
||||||
line-height: 100rpx;
|
|
||||||
background: #6ACDBB;
|
|
||||||
border: 0;
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-family: PingFang SC-Regular, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #FFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.register {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
text {
|
|
||||||
color: #999;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
word-spacing: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
myService,
|
myService,
|
||||||
myPharmacist
|
myPharmacist,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="box" :style="{marginTop: statusBarHeight+navbarHeight + 'px'}">
|
<view class="box" :style="{marginTop: statusBarHeight+navbarHeight + 'px'}">
|
||||||
<view class="store_" @click="openShow=true">
|
<view class="store_" @click="openShow=true">
|
||||||
{{doctorInfo.store.store.name || '默认门店'}}
|
{{doctorInfo.store.store.name || '默认门店'}}
|
||||||
<u-icon name="map-fill" color="#fff" size="28" margin-left="12"></u-icon>
|
<u-icon name="map-fill" color="#fff" size="28" margin-left="12"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
<view class="box_info">
|
<view class="box_info">
|
||||||
<view class="box_info_left">
|
<view class="box_info_left">
|
||||||
|
|
||||||
@@ -885,6 +885,7 @@ page {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
|
padding: 0 24rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
.u-icon {
|
.u-icon {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
<!-- 操作面板(通栏) -->
|
<!-- 操作面板(通栏) -->
|
||||||
<view class="setting-panel">
|
<view class="setting-panel">
|
||||||
|
<account-switch-entry />
|
||||||
<view
|
<view
|
||||||
class="setting-item action-item"
|
class="setting-item action-item"
|
||||||
hover-class="item-hover"
|
hover-class="item-hover"
|
||||||
@@ -46,9 +47,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||||
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ClinicPageLayout },
|
components: { ClinicPageLayout, AccountSwitchEntry },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
displayName: '',
|
displayName: '',
|
||||||
|
|||||||
@@ -1,135 +1,316 @@
|
|||||||
<template>
|
<template>
|
||||||
<clinic-page-layout title="药品详情" :show-back="true">
|
<clinic-page-layout title="药品详情" :show-back="true">
|
||||||
<view v-if="info.id" class="card">
|
<view v-if="info.id" class="page-container">
|
||||||
<image class="hero" :src="display.imageUrl" mode="aspectFit" />
|
<!-- 头部信息卡片 -->
|
||||||
<view class="row"><text>药品名称</text><text>{{ display.drugName }}</text></view>
|
<view class="card hero-card">
|
||||||
<view class="row"><text>拼音</text><text>{{ display.pinyin }}</text></view>
|
<view class="image-wrapper">
|
||||||
<view class="row"><text>供货价</text><text>{{ display.buyPrice }}</text></view>
|
<image class="hero" :src="display.imageUrl" mode="aspectFill" />
|
||||||
<view class="row"><text>建议售价</text><text>{{ display.suggestPrice }}</text></view>
|
</view>
|
||||||
<view class="row"><text>售价</text><text>{{ display.price }}</text></view>
|
<view class="main-info">
|
||||||
<view class="row"><text>库存</text><text>{{ display.stock }}</text></view>
|
<text class="drug-name">{{ display.drugName }}</text>
|
||||||
<view class="row"><text>状态</text><text>{{ display.statusText }}</text></view>
|
<text class="pinyin">拼音: {{ display.pinyin_simple || '--' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view v-if="info.id" class="btn-group">
|
<!-- 核心数据卡片 -->
|
||||||
<button class="btn secondary" @click="openPriceEdit">修改售价</button>
|
<view class="card data-card">
|
||||||
<button class="btn primary" @click="toggleStatus">
|
<view class="row">
|
||||||
{{ display.isOnShelf ? '下架' : '上架' }}
|
<text class="label">状态</text>
|
||||||
</button>
|
<view class="status-tag" :class="display.isOnShelf ? 'tag-on' : 'tag-off'">
|
||||||
</view>
|
{{ display.statusText }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">库存</text>
|
||||||
|
<text class="value stock-value">{{ display.stock }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<price-edit-modal
|
<view class="divider"></view>
|
||||||
:visible="priceModalVisible"
|
|
||||||
:item="info.id ? info : null"
|
<view class="row">
|
||||||
@close="closePriceEdit"
|
<text class="label">供货价</text>
|
||||||
@confirm="onPriceConfirm"
|
<text class="value">{{ display.buyPrice }}</text>
|
||||||
/>
|
</view>
|
||||||
</clinic-page-layout>
|
<view class="row">
|
||||||
|
<text class="label">建议售价</text>
|
||||||
|
<text class="value">{{ display.suggestPrice }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row highlight-row">
|
||||||
|
<text class="label">当前售价</text>
|
||||||
|
<text class="value price-large">{{ display.price }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作按钮组 -->
|
||||||
|
<view v-if="info.id" class="action-group">
|
||||||
|
<view class="btn-large btn-outline" hover-class="btn-hover-opacity" @click="openPriceEdit">
|
||||||
|
修改售价
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="btn-large"
|
||||||
|
:class="display.isOnShelf ? 'btn-danger' : 'btn-primary'"
|
||||||
|
hover-class="btn-hover-opacity"
|
||||||
|
@click="toggleStatus"
|
||||||
|
>
|
||||||
|
{{ display.isOnShelf ? '下架该药品' : '上架该药品' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<price-edit-modal
|
||||||
|
:visible="priceModalVisible"
|
||||||
|
:item="info.id ? info : null"
|
||||||
|
@close="closePriceEdit"
|
||||||
|
@confirm="onPriceConfirm"
|
||||||
|
/>
|
||||||
|
</clinic-page-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||||
import PriceEditModal from './components/PriceEditModal.vue'
|
import PriceEditModal from './components/PriceEditModal.vue'
|
||||||
import {
|
import {
|
||||||
getWarehouseDetail,
|
getWarehouseDetail,
|
||||||
updateWarehouseStatus,
|
updateWarehouseStatus,
|
||||||
updateWarehousePrice,
|
updateWarehousePrice,
|
||||||
} from '@/api/clinicAdmin.js'
|
} from '@/api/clinicAdmin.js'
|
||||||
import { mapWarehouseRow } from '../common/display.js'
|
import { mapWarehouseRow } from '../common/display.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ClinicPageLayout, PriceEditModal },
|
components: { ClinicPageLayout, PriceEditModal },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
id: 0,
|
id: 0,
|
||||||
info: {},
|
info: {},
|
||||||
priceModalVisible: false,
|
priceModalVisible: false,
|
||||||
priceSubmitting: false,
|
priceSubmitting: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
display() {
|
display() {
|
||||||
return mapWarehouseRow(this.info)
|
return mapWarehouseRow(this.info)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad(q) {
|
onLoad(q) {
|
||||||
this.id = Number(q.id || 0)
|
this.id = Number(q.id || 0)
|
||||||
this.load()
|
this.load()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
load() {
|
load() {
|
||||||
getWarehouseDetail(this.id).then(w => {
|
getWarehouseDetail(this.id).then(w => {
|
||||||
if (w.ok) this.info = w.data || {}
|
if (w.ok) this.info = w.data || {}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openPriceEdit() {
|
openPriceEdit() {
|
||||||
this.priceModalVisible = true
|
this.priceModalVisible = true
|
||||||
},
|
},
|
||||||
closePriceEdit() {
|
closePriceEdit() {
|
||||||
this.priceModalVisible = false
|
this.priceModalVisible = false
|
||||||
},
|
},
|
||||||
onPriceConfirm(price) {
|
onPriceConfirm(price) {
|
||||||
if (this.priceSubmitting) return
|
if (this.priceSubmitting) return
|
||||||
this.priceSubmitting = true
|
this.priceSubmitting = true
|
||||||
updateWarehousePrice({ id: this.id, price }).then(w => {
|
updateWarehousePrice({ id: this.id, price }).then(w => {
|
||||||
if (w.ok) {
|
if (w.ok) {
|
||||||
uni.showToast({ title: '修改成功' })
|
uni.showToast({ title: '修改成功' })
|
||||||
this.closePriceEdit()
|
this.closePriceEdit()
|
||||||
this.load()
|
this.load()
|
||||||
}
|
}
|
||||||
}).finally(() => { this.priceSubmitting = false })
|
}).finally(() => { this.priceSubmitting = false })
|
||||||
},
|
},
|
||||||
toggleStatus() {
|
toggleStatus() {
|
||||||
updateWarehouseStatus(this.id).then(w => {
|
updateWarehouseStatus(this.id).then(w => {
|
||||||
if (w.ok) {
|
if (w.ok) {
|
||||||
uni.showToast({ title: '操作成功' })
|
uni.showToast({ title: '操作成功' })
|
||||||
this.load()
|
this.load()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
/* 同步了列表页的核心色板 */
|
||||||
|
$theme-color: #6acdbb;
|
||||||
|
$theme-color-light: rgba(106, 205, 187, 0.12);
|
||||||
|
$text-primary: #2c3e50;
|
||||||
|
$text-secondary: #8a97a3;
|
||||||
|
$text-light: #b0b8c1;
|
||||||
|
$bg-color: #f7f9fa;
|
||||||
|
$border-color: #ebedf0;
|
||||||
|
$danger-color: #ff6b6b;
|
||||||
|
$danger-color-light: rgba(255, 107, 107, 0.1);
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
padding: 24rpx;
|
||||||
|
padding-bottom: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background: #fff;
|
background: #ffffff;
|
||||||
border-radius: 16rpx;
|
border-radius: 24rpx;
|
||||||
padding: 24rpx;
|
padding: 32rpx;
|
||||||
margin-bottom: 32rpx;
|
margin-bottom: 24rpx;
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 头部卡片特异样式 */
|
||||||
|
.hero-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
background: $bg-color;
|
||||||
|
border: 1rpx solid $border-color;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.hero {
|
.hero {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 360rpx;
|
height: 400rpx;
|
||||||
margin-bottom: 24rpx;
|
display: block;
|
||||||
border-radius: 12rpx;
|
|
||||||
background: #f8f8f8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-info {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drug-name {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pinyin {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 核心数据卡片样式 */
|
||||||
|
.data-card {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 16rpx 0;
|
align-items: center;
|
||||||
font-size: 28rpx;
|
padding: 24rpx 0;
|
||||||
border-bottom: 1rpx solid #f0f0f0;
|
|
||||||
}
|
}
|
||||||
.btn-group {
|
|
||||||
display: flex;
|
.divider {
|
||||||
flex-direction: column;
|
height: 1rpx;
|
||||||
gap: 24rpx;
|
background-color: $border-color;
|
||||||
|
margin: 8rpx 0;
|
||||||
}
|
}
|
||||||
.btn {
|
|
||||||
border-radius: 44rpx;
|
.label {
|
||||||
font-size: 30rpx;
|
font-size: 28rpx;
|
||||||
margin: 0;
|
color: $text-secondary;
|
||||||
}
|
}
|
||||||
.btn.primary {
|
|
||||||
background: #6acdbb;
|
.value {
|
||||||
color: #fff;
|
font-size: 28rpx;
|
||||||
|
color: $text-primary;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.btn.secondary {
|
|
||||||
background: #fff;
|
.stock-value {
|
||||||
color: #6acdbb;
|
font-family: monospace; /* 数字更清晰 */
|
||||||
border: 1rpx solid #6acdbb;
|
font-size: 30rpx;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,231 +1,383 @@
|
|||||||
<template>
|
<template>
|
||||||
<clinic-page-layout :title="pageTitle" :show-back="true">
|
<clinic-page-layout :title="pageTitle" :show-back="true">
|
||||||
<view class="search">
|
<!-- 搜索栏优化:胶囊式设计,增加视觉呼吸感 -->
|
||||||
<input v-model="keyword" placeholder="搜索药品名称" @confirm="reload" />
|
<view class="search-wrapper">
|
||||||
<text @click="reload">搜索</text>
|
<view class="search-bar">
|
||||||
</view>
|
<input class="search-input" v-model="keyword" placeholder="搜索药品名称" placeholder-class="search-placeholder" @confirm="reload" />
|
||||||
|
<view class="search-btn" hover-class="btn-hover" @click="reload">搜索</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view
|
<view class="list-container">
|
||||||
v-for="item in list"
|
<view
|
||||||
:key="item._wxKey"
|
v-for="item in list"
|
||||||
class="item"
|
:key="item._wxKey"
|
||||||
@click="goDetail(item)"
|
class="item-card"
|
||||||
>
|
hover-class="card-hover"
|
||||||
<image class="thumb" :src="rowDisplay(item).imageUrl" mode="aspectFill" />
|
@click="goDetail(item)"
|
||||||
<view class="body">
|
>
|
||||||
<view class="title">{{ rowDisplay(item).drugName }}</view>
|
<!-- 左侧缩略图 -->
|
||||||
<view class="sub">供货 {{ rowDisplay(item).buyPrice }} · 售价 {{ rowDisplay(item).price }}</view>
|
<image class="thumb" :src="rowDisplay(item).imageUrl" mode="aspectFill" />
|
||||||
<view class="sub">库存 {{ rowDisplay(item).stock }}</view>
|
|
||||||
</view>
|
|
||||||
<view class="actions" @click.stop>
|
|
||||||
<view class="tag" :class="rowDisplay(item).isOnShelf ? 'on' : 'off'">{{ rowDisplay(item).statusText }}</view>
|
|
||||||
<view class="btn-row">
|
|
||||||
<text class="act-btn" @click="openPriceEdit(item)">改价</text>
|
|
||||||
<text class="act-btn shelf" @click="toggleStatus(item)">
|
|
||||||
{{ rowDisplay(item).isOnShelf ? '下架' : '上架' }}
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="!loading && !list.length" class="empty">暂无药品</view>
|
<!-- 右侧内容区 -->
|
||||||
|
<view class="content">
|
||||||
|
<!-- 顶部:标题与状态标签对齐 -->
|
||||||
|
<view class="header-row">
|
||||||
|
<text class="title">{{ rowDisplay(item).drugName }}</text>
|
||||||
|
<view class="status-tag" :class="rowDisplay(item).isOnShelf ? 'tag-on' : 'tag-off'">
|
||||||
|
{{ rowDisplay(item).statusText }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<price-edit-modal
|
<!-- 中部:详细信息 -->
|
||||||
:visible="priceModalVisible"
|
<view class="info-group">
|
||||||
:item="priceEditItem"
|
<text class="info-text">供货 {{ rowDisplay(item).buyPrice }} · 售价 {{ rowDisplay(item).price }}</text>
|
||||||
@close="closePriceEdit"
|
<text class="info-text">库存 {{ rowDisplay(item).stock }}</text>
|
||||||
@confirm="onPriceConfirm"
|
</view>
|
||||||
/>
|
|
||||||
</clinic-page-layout>
|
<!-- 底部:操作按钮横向排列,增加点击区域与反馈 -->
|
||||||
|
<view class="action-row" @click.stop>
|
||||||
|
<view class="action-btn outline" hover-class="btn-hover-opacity" @click.stop="openPriceEdit(item)">
|
||||||
|
改价
|
||||||
|
</view>
|
||||||
|
<view class="action-btn" :class="rowDisplay(item).isOnShelf ? 'btn-danger' : 'btn-primary'" hover-class="btn-hover-opacity" @click.stop="toggleStatus(item)">
|
||||||
|
{{ rowDisplay(item).isOnShelf ? '下架' : '上架' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态优化 -->
|
||||||
|
<view v-if="!loading && !list.length" class="empty-state">
|
||||||
|
<view class="empty-icon"></view>
|
||||||
|
<text class="empty-text">暂无药品数据</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<price-edit-modal
|
||||||
|
:visible="priceModalVisible"
|
||||||
|
:item="priceEditItem"
|
||||||
|
@close="closePriceEdit"
|
||||||
|
@confirm="onPriceConfirm"
|
||||||
|
/>
|
||||||
|
</clinic-page-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||||
import PriceEditModal from './components/PriceEditModal.vue'
|
import PriceEditModal from './components/PriceEditModal.vue'
|
||||||
import {
|
import {
|
||||||
getWarehouseList,
|
getWarehouseList,
|
||||||
updateWarehouseStatus,
|
updateWarehouseStatus,
|
||||||
updateWarehousePrice,
|
updateWarehousePrice,
|
||||||
} from '@/api/clinicAdmin.js'
|
} from '@/api/clinicAdmin.js'
|
||||||
import { withWxKey } from '@/utils/wxListKey.js'
|
import { withWxKey } from '@/utils/wxListKey.js'
|
||||||
import { mapWarehouseRow } from '../common/display.js'
|
import { mapWarehouseRow } from '../common/display.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ClinicPageLayout, PriceEditModal },
|
components: { ClinicPageLayout, PriceEditModal },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
pageTitle: '仓库管理',
|
pageTitle: '仓库管理',
|
||||||
warehouseType: '',
|
warehouseType: '',
|
||||||
keyword: '',
|
keyword: '',
|
||||||
list: [],
|
list: [],
|
||||||
page: 1,
|
page: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
priceModalVisible: false,
|
priceModalVisible: false,
|
||||||
priceEditItem: null,
|
priceEditItem: null,
|
||||||
priceSubmitting: false,
|
priceSubmitting: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad(q) {
|
onLoad(q) {
|
||||||
if (q.label) {
|
if (q.label) {
|
||||||
this.pageTitle = decodeURIComponent(q.label)
|
this.pageTitle = decodeURIComponent(q.label)
|
||||||
}
|
}
|
||||||
if (q.type != null && q.type !== '') {
|
if (q.type != null && q.type !== '') {
|
||||||
this.warehouseType = q.type
|
this.warehouseType = q.type
|
||||||
}
|
}
|
||||||
this.reload()
|
this.reload()
|
||||||
},
|
},
|
||||||
onReachBottom() {
|
onReachBottom() {
|
||||||
this.load(this.page + 1, true)
|
this.load(this.page + 1, true)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
rowDisplay(item) {
|
rowDisplay(item) {
|
||||||
return mapWarehouseRow(item)
|
return mapWarehouseRow(item)
|
||||||
},
|
},
|
||||||
reload() {
|
reload() {
|
||||||
this.page = 1
|
this.page = 1
|
||||||
this.load(1, false)
|
this.load(1, false)
|
||||||
},
|
},
|
||||||
load(page, append) {
|
load(page, append) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const params = { page, pageSize: 20 }
|
const params = { page, pageSize: 20 }
|
||||||
if (this.keyword) params.name = this.keyword
|
if (this.keyword) params.name = this.keyword
|
||||||
if (this.warehouseType !== '' && this.warehouseType != null) {
|
if (this.warehouseType !== '' && this.warehouseType != null) {
|
||||||
params.type = this.warehouseType
|
params.type = this.warehouseType
|
||||||
}
|
}
|
||||||
getWarehouseList(params).then(w => {
|
getWarehouseList(params).then(w => {
|
||||||
const items = withWxKey((w.data && w.data.items) || [], 'id', 'wh')
|
const items = withWxKey((w.data && w.data.items) || [], 'id', 'wh')
|
||||||
this.list = append ? this.list.concat(items) : items
|
this.list = append ? this.list.concat(items) : items
|
||||||
this.page = page
|
this.page = page
|
||||||
}).finally(() => { this.loading = false })
|
}).finally(() => { this.loading = false })
|
||||||
},
|
},
|
||||||
goDetail(item) {
|
goDetail(item) {
|
||||||
uni.navigateTo({ url: '/subPackages/sub_clinic_admin/warehouse/detail?id=' + item.id })
|
uni.navigateTo({ url: '/subPackages/sub_clinic_admin/warehouse/detail?id=' + item.id })
|
||||||
},
|
},
|
||||||
openPriceEdit(item) {
|
openPriceEdit(item) {
|
||||||
this.priceEditItem = item
|
this.priceEditItem = item
|
||||||
this.priceModalVisible = true
|
this.priceModalVisible = true
|
||||||
},
|
},
|
||||||
closePriceEdit() {
|
closePriceEdit() {
|
||||||
this.priceModalVisible = false
|
this.priceModalVisible = false
|
||||||
this.priceEditItem = null
|
this.priceEditItem = null
|
||||||
},
|
},
|
||||||
onPriceConfirm(price) {
|
onPriceConfirm(price) {
|
||||||
if (!this.priceEditItem || this.priceSubmitting) return
|
if (!this.priceEditItem || this.priceSubmitting) return
|
||||||
this.priceSubmitting = true
|
this.priceSubmitting = true
|
||||||
updateWarehousePrice({ id: this.priceEditItem.id, price }).then(w => {
|
updateWarehousePrice({ id: this.priceEditItem.id, price }).then(w => {
|
||||||
if (w.ok) {
|
if (w.ok) {
|
||||||
uni.showToast({ title: '修改成功' })
|
uni.showToast({ title: '修改成功' })
|
||||||
this.closePriceEdit()
|
this.closePriceEdit()
|
||||||
this.reload()
|
this.reload()
|
||||||
}
|
}
|
||||||
}).finally(() => { this.priceSubmitting = false })
|
}).finally(() => { this.priceSubmitting = false })
|
||||||
},
|
},
|
||||||
toggleStatus(item) {
|
toggleStatus(item) {
|
||||||
const action = item.status === 2 ? '下架' : '上架'
|
const action = item.status === 2 ? '下架' : '上架'
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确定' + action + '「' + ((item.drug && item.drug.drug_name) || '') + '」?',
|
content: '确定' + action + '「' + ((item.drug && item.drug.drug_name) || '') + '」?',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
updateWarehouseStatus(item.id).then(w => {
|
updateWarehouseStatus(item.id).then(w => {
|
||||||
if (w.ok) {
|
if (w.ok) {
|
||||||
uni.showToast({ title: '操作成功' })
|
uni.showToast({ title: '操作成功' })
|
||||||
item.status = item.status === 2 ? 1 : 2
|
item.status = item.status === 2 ? 1 : 2
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.search {
|
/* 核心主题色变量提取,方便后续统一管理 */
|
||||||
display: flex;
|
$theme-color: #6acdbb;
|
||||||
background: #fff;
|
$theme-color-light: rgba(106, 205, 187, 0.12);
|
||||||
padding: 16rpx 24rpx;
|
$text-primary: #2c3e50;
|
||||||
border-radius: 16rpx;
|
$text-secondary: #8a97a3;
|
||||||
margin-bottom: 24rpx;
|
$text-light: #b0b8c1;
|
||||||
align-items: center;
|
$bg-color: #f7f9fa;
|
||||||
|
$border-color: #ebedf0;
|
||||||
|
$danger-color: #ff6b6b;
|
||||||
|
$danger-color-light: rgba(255, 107, 107, 0.1);
|
||||||
|
|
||||||
|
.search-wrapper {
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
background-color: transparent;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
.search input {
|
|
||||||
flex: 1;
|
.search-bar {
|
||||||
font-size: 28rpx;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 12rpx 16rpx 12rpx 32rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
.search text {
|
|
||||||
color: #6acdbb;
|
.search-input {
|
||||||
font-size: 28rpx;
|
flex: 1;
|
||||||
margin-left: 16rpx;
|
font-size: 28rpx;
|
||||||
|
color: $text-primary;
|
||||||
|
height: 60rpx;
|
||||||
}
|
}
|
||||||
.item {
|
|
||||||
display: flex;
|
.search-placeholder {
|
||||||
align-items: flex-start;
|
color: $text-light;
|
||||||
background: #fff;
|
|
||||||
padding: 24rpx;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
background: $theme-color;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 12rpx 36rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
padding: 0 24rpx 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-card {
|
||||||
|
display: flex;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-hover {
|
||||||
|
transform: scale(0.99);
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
width: 100rpx;
|
width: 140rpx;
|
||||||
height: 100rpx;
|
height: 140rpx;
|
||||||
border-radius: 12rpx;
|
border-radius: 16rpx;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: #f5f5f5;
|
background: $bg-color;
|
||||||
|
border: 1rpx solid $border-color;
|
||||||
}
|
}
|
||||||
.body {
|
|
||||||
flex: 1;
|
.content {
|
||||||
margin-left: 20rpx;
|
flex: 1;
|
||||||
min-width: 0;
|
margin-left: 24rpx;
|
||||||
padding-right: 12rpx;
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 28rpx;
|
font-size: 30rpx;
|
||||||
color: #333;
|
color: $text-primary;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.4;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
/* 单行截断 */
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.sub {
|
|
||||||
font-size: 24rpx;
|
.status-tag {
|
||||||
color: #999;
|
font-size: 20rpx;
|
||||||
margin-top: 8rpx;
|
font-weight: 500;
|
||||||
|
padding: 4rpx 14rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
.actions {
|
|
||||||
flex-shrink: 0;
|
.tag-on {
|
||||||
display: flex;
|
color: $theme-color;
|
||||||
flex-direction: column;
|
background: $theme-color-light;
|
||||||
align-items: flex-end;
|
|
||||||
gap: 12rpx;
|
|
||||||
}
|
}
|
||||||
.tag {
|
|
||||||
font-size: 22rpx;
|
.tag-off {
|
||||||
padding: 4rpx 12rpx;
|
color: $text-secondary;
|
||||||
border-radius: 8rpx;
|
background: $bg-color;
|
||||||
}
|
}
|
||||||
.tag.on {
|
|
||||||
color: #6acdbb;
|
.info-group {
|
||||||
background: rgba(106, 205, 187, 0.12);
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
.tag.off {
|
|
||||||
color: #999;
|
.info-text {
|
||||||
background: #f5f5f5;
|
font-size: 24rpx;
|
||||||
|
color: $text-secondary;
|
||||||
}
|
}
|
||||||
.btn-row {
|
|
||||||
display: flex;
|
.action-row {
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
gap: 8rpx;
|
justify-content: flex-end;
|
||||||
align-items: flex-end;
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
.act-btn {
|
|
||||||
font-size: 24rpx;
|
.action-btn {
|
||||||
color: #6acdbb;
|
font-size: 24rpx;
|
||||||
padding: 4rpx 0;
|
padding: 10rpx 28rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.5;
|
||||||
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
.act-btn.shelf {
|
|
||||||
color: #666;
|
.action-btn.outline {
|
||||||
|
color: $text-primary;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1rpx solid $border-color;
|
||||||
}
|
}
|
||||||
.empty {
|
|
||||||
text-align: center;
|
.action-btn.btn-primary {
|
||||||
color: #999;
|
color: #ffffff;
|
||||||
padding: 60rpx;
|
background: $theme-color;
|
||||||
|
border: 1rpx solid $theme-color;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -36,6 +36,10 @@
|
|||||||
</view> -->
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="p-row-32 m-t-8 white b-r-8">
|
||||||
|
<account-switch-entry />
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="p-row-32 m-t-8" style="height: 100rpx;">
|
<view class="p-row-32 m-t-8" style="height: 100rpx;">
|
||||||
<u-button :throttle-time="0" shape="circle" :custom-style="{backgroundColor:'#6ACDBB',color:'#fff',heigth:'96rpx'}" @click="logout">
|
<u-button :throttle-time="0" shape="circle" :custom-style="{backgroundColor:'#6ACDBB',color:'#fff',heigth:'96rpx'}" @click="logout">
|
||||||
退出登录</u-button>
|
退出登录</u-button>
|
||||||
@@ -49,8 +53,10 @@
|
|||||||
logout,
|
logout,
|
||||||
toUpload,
|
toUpload,
|
||||||
} from '@/api/all.js'
|
} from '@/api/all.js'
|
||||||
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
import { chooseAvatarImage } from '@/common/js/select-avatar-image.js';
|
import { chooseAvatarImage } from '@/common/js/select-avatar-image.js';
|
||||||
export default {
|
export default {
|
||||||
|
components: { AccountSwitchEntry },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
doctorInfo: []
|
doctorInfo: []
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
40056256325
|
40056256325
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="p-row-32 m-t-8 white">
|
||||||
|
<account-switch-entry />
|
||||||
|
</view>
|
||||||
<view class="p-row-32 m-t-8" style="height: 80rpx;">
|
<view class="p-row-32 m-t-8" style="height: 80rpx;">
|
||||||
<u-button :throttle-time="0" shape="circle" :custom-style="{backgroundColor:'#6ACDBB',color:'#fff',heigth:'86rpx'}" @click="logout">
|
<u-button :throttle-time="0" shape="circle" :custom-style="{backgroundColor:'#6ACDBB',color:'#fff',heigth:'86rpx'}" @click="logout">
|
||||||
退出登录</u-button>
|
退出登录</u-button>
|
||||||
@@ -33,7 +36,9 @@
|
|||||||
import {
|
import {
|
||||||
logout
|
logout
|
||||||
} from '@/api/all.js'
|
} from '@/api/all.js'
|
||||||
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
export default {
|
export default {
|
||||||
|
components: { AccountSwitchEntry },
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
|||||||
116
utils/loginSession.js
Normal file
116
utils/loginSession.js
Normal file
@@ -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);
|
||||||
|
|
||||||
Reference in New Issue
Block a user