Files
xk-doctor-wx/components/account-switch/account-switch-panel.vue
2026-06-09 17:02:13 +08:00

281 lines
6.0 KiB
Vue

<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="10090"
: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}_${item.login_persona || 'admin'}_${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);
if (!this.accounts.length) {
uni.showToast({ title: '暂无可选账号', icon: 'none' });
return Promise.resolve();
}
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,
login_persona: this.selected.login_persona || undefined,
}).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>