Files
threeonecheck_web/pages/personalcenter/identity.vue
2026-07-27 09:27:22 +08:00

322 lines
6.8 KiB
Vue

<template>
<view class="page">
<!-- 当前生效身份 -->
<view v-if="currentIdentity" class="current-banner">
<view class="banner-label">当前生效身份</view>
<view class="banner-name">{{ getIdentityTitle(currentIdentity) }}</view>
<view class="banner-sub">{{ getIdentitySubtitle(currentIdentity) }}</view>
</view>
<!-- 加载中 -->
<view v-if="loading" class="state-box">
<u-loading-icon mode="circle" color="#007aff"></u-loading-icon>
<text class="state-text">加载中...</text>
</view>
<!-- 空状态 -->
<view v-else-if="!identityList.length" class="state-box">
<text class="state-text">暂无可用身份</text>
</view>
<!-- 身份列表 -->
<view v-else class="identity-list">
<view
v-for="item in identityList"
:key="item.identityId"
class="identity-card"
:class="{
'is-current': isCurrentIdentity(item),
'is-default': item.isDefault
}"
>
<view class="card-header">
<view class="card-title">{{ getIdentityTitle(item) }}</view>
<view class="card-tags">
<view v-if="isCurrentIdentity(item)" class="tag tag-current">当前使用</view>
<view v-if="item.isDefault" class="tag tag-default">默认</view>
</view>
</view>
<view class="info-list">
<view class="info-row">
<text class="info-label">所属部门</text>
<text class="info-value">{{ item.deptName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">所属角色</text>
<text class="info-value">{{ item.roleName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">角色标识</text>
<text class="info-value">{{ item.roleKey || '-' }}</text>
</view>
<view v-if="item.postName" class="info-row">
<text class="info-label">所属岗位</text>
<text class="info-value">{{ item.postName }}</text>
</view>
</view>
<button
class="switch-btn"
:class="{ 'switch-btn--disabled': isCurrentIdentity(item) }"
:loading="switchingId === item.identityId"
:disabled="isCurrentIdentity(item) || !!switchingId"
@click="handleSwitch(item)"
>
{{ isCurrentIdentity(item) ? '当前使用中' : '切换到此身份' }}
</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import { getMyIdentity } from '@/request/identity.js';
import { performIdentitySwitch, reLaunchAfterSwitch } from '@/utils/identitySwitch.js';
const loading = ref(false);
const switchingId = ref(null);
const identityList = ref([]);
const currentIdentity = ref(null);
function unwrapIdentityData(res) {
if (!res) return { identities: [], currentIdentity: null };
const data = res.data && typeof res.data === 'object' ? res.data : res;
return {
identities: data.identities || [],
currentIdentity: data.currentIdentity || null
};
}
function getIdentityTitle(item) {
return item.identityName || item.roleName || '未命名身份';
}
function getIdentitySubtitle(item) {
const parts = [];
if (item.deptName) parts.push(item.deptName);
if (item.roleName && item.roleName !== item.identityName) parts.push(item.roleName);
return parts.join(' · ') || '-';
}
function isCurrentIdentity(item) {
if (item.current) return true;
return currentIdentity.value && currentIdentity.value.identityId === item.identityId;
}
async function fetchIdentityList() {
loading.value = true;
try {
const res = await getMyIdentity();
if (res.code === 0 || res.code === 200) {
const { identities, currentIdentity: current } = unwrapIdentityData(res);
identityList.value = identities;
currentIdentity.value = current;
} else {
identityList.value = [];
currentIdentity.value = null;
uni.showToast({ title: res.msg || '获取身份列表失败', icon: 'none' });
}
} catch (e) {
console.error('获取身份列表失败:', e);
identityList.value = [];
currentIdentity.value = null;
} finally {
loading.value = false;
}
}
async function handleSwitch(item) {
if (isCurrentIdentity(item) || switchingId.value) return;
const identityName = getIdentityTitle(item);
const confirmMsg = `确认切换到「${identityName}」吗?切换后数据将按新身份刷新。`;
uni.showModal({
title: '切换身份',
content: confirmMsg,
confirmText: '确认切换',
success: async (modalRes) => {
if (!modalRes.confirm) return;
switchingId.value = item.identityId;
try {
await performIdentitySwitch(item.identityId);
reLaunchAfterSwitch();
} catch (e) {
console.error('身份切换失败:', e);
} finally {
switchingId.value = null;
}
}
});
}
onShow(() => {
fetchIdentityList();
});
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: #f4f7fb;
padding: 24rpx;
padding-bottom: 48rpx;
}
.current-banner {
background: linear-gradient(135deg, #3e95f1 0%, #4269f5 100%);
border-radius: 20rpx;
padding: 32rpx;
margin-bottom: 24rpx;
color: #fff;
.banner-label {
font-size: 24rpx;
opacity: 0.85;
}
.banner-name {
font-size: 34rpx;
font-weight: bold;
margin-top: 12rpx;
}
.banner-sub {
font-size: 26rpx;
opacity: 0.9;
margin-top: 8rpx;
}
}
.state-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
.state-text {
margin-top: 24rpx;
font-size: 28rpx;
color: #999;
}
}
.identity-list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.identity-card {
background: #fff;
border-radius: 20rpx;
padding: 32rpx;
border: 2rpx solid transparent;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
&.is-current {
border-color: #67c23a;
}
&.is-default {
border-color: #e6a23c;
}
&.is-current.is-default {
border-color: #67c23a;
}
}
.card-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16rpx;
margin-bottom: 24rpx;
}
.card-title {
flex: 1;
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.card-tags {
display: flex;
flex-wrap: wrap;
gap: 8rpx;
flex-shrink: 0;
}
.tag {
font-size: 22rpx;
padding: 4rpx 12rpx;
border-radius: 8rpx;
}
.tag-current {
background: #e8f8ef;
color: #67c23a;
}
.tag-default {
background: #fdf6ec;
color: #e6a23c;
}
.info-list {
margin-bottom: 28rpx;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 16rpx 0;
border-bottom: 1rpx dashed #eee;
font-size: 28rpx;
&:last-child {
border-bottom: none;
}
}
.info-label {
color: #999;
flex-shrink: 0;
margin-right: 24rpx;
}
.info-value {
color: #333;
text-align: right;
word-break: break-all;
}
.switch-btn {
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: linear-gradient(90deg, #3e95f1 0%, #4269f5 100%);
color: #fff;
font-size: 30rpx;
border-radius: 40rpx;
border: none;
&::after {
border: none;
}
&--disabled {
background: #e8e8e8;
color: #999;
}
}
</style>