325 lines
6.5 KiB
Vue
325 lines
6.5 KiB
Vue
<template>
|
||
<div class="auth-manage-page">
|
||
<!-- 顶部导航 -->
|
||
<header class="page-header">
|
||
<button class="back-btn" @click="goBack">‹</button>
|
||
<h1 class="page-title">授权管理</h1>
|
||
<button class="add-btn" @click="addAuthorization">+</button>
|
||
</header>
|
||
|
||
<!-- 授权列表 -->
|
||
<section class="auth-list" v-if="authList.length > 0">
|
||
<div class="auth-card" v-for="auth in authList" :key="auth.id">
|
||
<div class="auth-icon" :class="auth.targetType">
|
||
{{ getAuthIcon(auth.targetType) }}
|
||
</div>
|
||
<div class="auth-info">
|
||
<h3 class="auth-name">{{ auth.targetName }}</h3>
|
||
<p class="auth-type">{{ getAuthTypeText(auth.targetType) }}</p>
|
||
<div class="auth-permissions">
|
||
<span class="permission-tag" v-for="perm in auth.permissions" :key="perm">
|
||
{{ getPermissionText(perm) }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="auth-actions">
|
||
<span class="auth-status" :class="auth.status">
|
||
{{ auth.status === 'active' ? '已授权' : '已撤销' }}
|
||
</span>
|
||
<button class="auth-toggle-btn" @click="toggleAuth(auth)">
|
||
{{ auth.status === 'active' ? '撤销' : '授权' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 空状态 -->
|
||
<section class="empty-state" v-else>
|
||
<div class="empty-icon">🔐</div>
|
||
<h3 class="empty-title">暂无授权</h3>
|
||
<p class="empty-desc">授权其他用户或应用访问你的数字分身</p>
|
||
<button class="empty-btn" @click="addAuthorization">添加授权</button>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { useAvatarStore } from '@/store/avatar'
|
||
import { getAuthorizationList, updateAuthorization } from '@/api'
|
||
import { pickAvatarId, unwrapListData } from '@/utils/avatar-page-data.js'
|
||
|
||
const router = useRouter()
|
||
const avatarStore = useAvatarStore()
|
||
|
||
// 授权列表
|
||
const authList = ref<Array<{
|
||
id: string
|
||
targetType: 'user' | 'organization' | 'application'
|
||
targetName: string
|
||
permissions: string[]
|
||
status: 'active' | 'inactive'
|
||
}>>([])
|
||
|
||
// 从后端加载授权列表
|
||
const loadAuth = async () => {
|
||
try {
|
||
if (!avatarStore.avatars.length) {
|
||
await avatarStore.loadAvatars()
|
||
}
|
||
const avatarId = pickAvatarId(avatarStore.currentAvatarId, avatarStore.avatars)
|
||
if (!avatarId) {
|
||
authList.value = []
|
||
return
|
||
}
|
||
const res: any = await getAuthorizationList(avatarId)
|
||
authList.value = unwrapListData(res)
|
||
} catch (e) {
|
||
console.error('加载授权失败', e)
|
||
}
|
||
}
|
||
|
||
// 获取授权图标
|
||
const getAuthIcon = (type: string) => {
|
||
const map: Record<string, string> = {
|
||
'user': '👤',
|
||
'organization': '🏢',
|
||
'application': '📱'
|
||
}
|
||
return map[type] || '🔑'
|
||
}
|
||
|
||
// 获取授权类型文本
|
||
const getAuthTypeText = (type: string) => {
|
||
const map: Record<string, string> = {
|
||
'user': '用户',
|
||
'organization': '组织',
|
||
'application': '应用'
|
||
}
|
||
return map[type] || type
|
||
}
|
||
|
||
// 获取权限文本
|
||
const getPermissionText = (perm: string) => {
|
||
const map: Record<string, string> = {
|
||
'read': '读取',
|
||
'write': '写入',
|
||
'reply': '回复',
|
||
'edit': '编辑'
|
||
}
|
||
return map[perm] || perm
|
||
}
|
||
|
||
// 切换授权状态(写入后端)
|
||
const toggleAuth = async (auth: any) => {
|
||
const newStatus = auth.status === 'active' ? 'inactive' : 'active'
|
||
try {
|
||
const avatarId = pickAvatarId(avatarStore.currentAvatarId, avatarStore.avatars)
|
||
if (!avatarId) return
|
||
const res: any = await updateAuthorization(avatarId, {
|
||
id: auth.id,
|
||
status: newStatus
|
||
})
|
||
authList.value = unwrapListData(res)
|
||
} catch (e) {
|
||
alert('操作失败,请重试')
|
||
}
|
||
}
|
||
|
||
// 添加授权
|
||
const addAuthorization = () => {
|
||
alert('添加授权功能开发中...')
|
||
}
|
||
|
||
// 返回
|
||
const goBack = () => {
|
||
router.back()
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadAuth()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.auth-manage-page {
|
||
min-height: 100vh;
|
||
background: #F8F9FA;
|
||
padding-bottom: 80px;
|
||
}
|
||
|
||
/* 顶部导航 */
|
||
.page-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16px 20px;
|
||
background: white;
|
||
border-bottom: 1px solid #EDEEF1;
|
||
}
|
||
|
||
.back-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
padding: 4px 8px;
|
||
color: #18191C;
|
||
}
|
||
|
||
.page-title {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
margin: 0;
|
||
color: #18191C;
|
||
}
|
||
|
||
.add-btn {
|
||
background: #F97316;
|
||
color: white;
|
||
border: none;
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 授权列表 */
|
||
.auth-list {
|
||
padding: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.auth-card {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
padding: 16px;
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.auth-icon {
|
||
font-size: 24px;
|
||
width: 48px;
|
||
height: 48px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 12px;
|
||
background: #FFF0E6;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.auth-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.auth-name {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
margin: 0 0 4px;
|
||
color: #18191C;
|
||
}
|
||
|
||
.auth-type {
|
||
font-size: 12px;
|
||
color: #9398AE;
|
||
margin: 0 0 8px;
|
||
}
|
||
|
||
.auth-permissions {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.permission-tag {
|
||
padding: 4px 8px;
|
||
background: #F3F4F6;
|
||
border-radius: 6px;
|
||
font-size: 11px;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.auth-actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
gap: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.auth-status {
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.auth-status.active {
|
||
color: #22C55E;
|
||
}
|
||
|
||
.auth-status.inactive {
|
||
color: #9398AE;
|
||
}
|
||
|
||
.auth-toggle-btn {
|
||
padding: 6px 12px;
|
||
border-radius: 8px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
border: none;
|
||
background: #F97316;
|
||
color: white;
|
||
}
|
||
|
||
/* 空状态 */
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 80px 20px;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 64px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.empty-title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #18191C;
|
||
margin: 0 0 10px;
|
||
}
|
||
|
||
.empty-desc {
|
||
font-size: 14px;
|
||
color: #9398AE;
|
||
margin: 0 0 24px;
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-btn {
|
||
padding: 12px 32px;
|
||
background: #F97316;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 10px;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
}
|
||
</style>
|