Files
akmon/pages/admins/users/detail.uvue
2026-01-20 08:04:15 +08:00

1423 lines
44 KiB
Plaintext

<template>
<view class="user-detail-page">
<admin-layout>
<view class="container">
<view class="page-header">
<view class="back-button" @click="goBack">
<text class="iconfont">&#xe6a8;</text>
<text>返回</text>
</view>
<text class="page-title">用户名详情</text>
<view class="header-actions">
<button
class="primary-btn"
@click="showRoleModal = true"
v-if="canManageUserRoles"
>
添加角色
</button>
</view>
</view>
<view class="loading-container" v-if="loading">
<text>加载中...</text>
</view>
<view v-else>
<view class="user-info-card">
<view class="card-header">
<text class="card-title">基本信息</text>
<text
class="edit-link"
@click="showEditModal = true"
v-if="canManageUserInfo"
>
编辑
</text>
</view>
<view class="card-body">
<view class="info-row">
<view class="info-label">濮撳悕:</view>
<view class="info-value">{{ user.username ?? '-' }}</view>
</view>
<view class="info-row">
<view class="info-label">用户名</view>
<view class="info-value">{{ user.username }}</view>
</view>
<view class="info-row">
<view class="info-label">邮:</view>
<view class="info-value">{{ user.email ?? '-' }}</view>
</view>
<view class="info-row">
<view class="info-label">手机:</view>
<view class="info-value">{{ user.phone ?? '-' }}</view>
</view>
<view class="info-row">
<view class="info-label">状态</view>
<view class="info-value">
<text :class="['status-badge', user.is_active ? 'active' : 'inactive']">
{{ user.is_active ? '已启用' : '已禁用' }}
</text>
</view>
</view>
<view class="info-row">
<view class="info-label">注册时间:</view>
<view class="info-value">{{ formatDate(user.created_at) }}</view>
</view>
<view class="info-row">
<view class="info-label">最后登录</view>
<view class="info-value">{{ (user.last_login != null && user.last_login !== '') ? formatDate(user.last_login as string) : '从未登录' }}</view>
</view>
</view>
</view>
<view class="roles-card">
<view class="card-header">
<text class="card-title">角色与权限</text>
</view>
<view class="card-body" v-if="userRoles.length === 0">
<text class="empty-text">该用户未分配任何角色</text>
</view>
<view class="card-body" v-else>
<view
class="role-item"
v-for="(role, index) in userRoles"
:key="index"
>
<view class="role-info">
<text class="role-name">{{ role.role_name }}</text>
<text class="role-scope" v-if="role.scope_name">
({{ role.scope_name }})
</text>
<text class="role-level">Level {{ role.level }}</text>
</view>
<view class="role-actions">
<text
class="delete-btn"
@click="confirmDeleteRole(role)"
v-if="canManageUserRoles"
>
绉婚櫎
</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="modal" v-if="showEditModal">
<view class="modal-content">
<view class="modal-header">
<text class="modal-title">缂栬緫用户名淇℃伅</text>
<text class="close-btn" @click="showEditModal = false">脳</text>
</view>
<view class="modal-body">
<view class="form-group">
<text class="form-label">姓名:</text>
<input type="text" v-model="editForm.name" placeholder="请输入用户姓名" />
</view>
<view class="form-group">
<text class="form-label">邮箱:</text>
<input type="text" v-model="editForm.email" placeholder="请输入邮箱地址" />
</view>
<view class="form-group">
<text class="form-label">手嬫満:</text>
<input type="text" v-model="editForm.phone" placeholder="请输入手机号 />
</view>
<view class="form-group">
<text class="form-label">状态讹拷?</text>
<view class="status-toggle">
<text
class="toggle-option"
:class="{ active: editForm.is_active }"
@click="editForm.is_active = true"
>
后敤
</text>
<text
class="toggle-option"
:class="{ active: !editForm.is_active }"
@click="editForm.is_active = false"
>
绂佺敤
</text>
</view>
</view>
</view>
<view class="modal-footer">
<button class="cancel-btn" @click="showEditModal = false">鍙栨秷</button>
<button class="submit-btn" @click="saveUserInfo">淇濆瓨</button>
</view>
</view>
</view>
<!-- 添加角色妯℃€佹 -->
<view class="modal" v-if="showRoleModal">
<view class="modal-content">
<view class="modal-header">
<text class="modal-title">添加角色</text>
<text class="close-btn" @click="showRoleModal = false">脳</text>
</view>
<view class="modal-body">
<view class="form-group">
<text class="form-label">角掕壊:</text>
<view class="picker-field" @click="showRolePicker = true">
<text>{{ selectedRoleIndex >= 0 ? roleOptions[selectedRoleIndex].name : '请烽€夋嫨角掕壊' }}</text>
<text class="picker-arrow">></text>
</view>
<view v-if="showRolePicker" class="picker-modal">
<picker-view
class="picker-view"
:value="[tempRoleIndex]"
:indicator-style="'height: 50px;'"
@change="onRolePickerViewChange"
>
<picker-view-column style="width:750rpx;">
<view v-for="(r, idx) in roleOptions" :key="r.id" class="picker-item">{{ r.name }}</view>
</picker-view-column>
</picker-view>
<view class="picker-actions">
<button @click="showRolePicker = false">鍙栨秷</button>
<button @click="confirmRolePicker" class="picker-actions-button">纭畾</button>
</view>
</view>
</view>
<view class="form-group" v-if="showScopeSelection">
<text class="form-label">作用域类型</text>
<view class="scope-toggle">
<text
class="toggle-option"
:class="{ active: roleForm.scope_type === 'region' }"
@click="changeScopeType('region')"
>
地板尯
</text>
<text
class="toggle-option"
:class="{ active: roleForm.scope_type === 'school' }"
@click="changeScopeType('school')"
>
瀛︽牎
</text>
<text
class="toggle-option"
:class="{ active: roleForm.scope_type === 'grade' }"
@click="changeScopeType('grade')"
>
骞寸骇
</text>
<text
class="toggle-option"
:class="{ active: roleForm.scope_type === 'class' }"
@click="changeScopeType('class')"
>
鐝骇
</text>
</view>
</view>
<view class="form-group" v-if="['region', 'school', 'grade', 'class'].includes(roleForm.scope_type ?? '')">
<text class="form-label">閫夋嫨地板尯:</text>
<view class="picker-field" @click="showRegionPicker = true">
<text>{{ selectedRegionIndex >= 0 ? regionOptions[selectedRegionIndex].name : '请烽€夋嫨地板尯' }}</text>
<text class="picker-arrow">></text>
</view>
<view v-if="showRegionPicker" class="picker-modal">
<picker-view
class="picker-view"
:value="[tempRegionIndex]"
:indicator-style="'height: 50px;'"
@change="onRegionPickerViewChange"
>
<picker-view-column style="width:750rpx;">
<view v-for="(r, idx) in regionOptions" :key="r.id" class="picker-item">{{ r.name }}</view>
</picker-view-column>
</picker-view>
<view class="picker-actions">
<button @click="showRegionPicker = false">鍙栨秷</button>
<button @click="confirmRegionPicker" class="picker-actions-button">纭畾</button>
</view>
</view>
</view>
<view class="form-group" v-if="['school', 'grade', 'class'].includes(roleForm.scope_type ?? '') && selectedRegionIndex >= 0">
<text class="form-label">閫夋嫨瀛︽牎:</text>
<view class="picker-field" @click="showSchoolPicker = true">
<text>{{ selectedSchoolIndex >= 0 ? schoolOptions[selectedSchoolIndex].name : '请烽€夋嫨瀛︽牎' }}</text>
<text class="picker-arrow">></text>
</view>
<view v-if="showSchoolPicker" class="picker-modal">
<picker-view
class="picker-view"
:value="[tempSchoolIndex]"
:indicator-style="'height: 50px;'"
@change="onSchoolPickerViewChange"
>
<picker-view-column style="width:750rpx;">
<view v-for="(s, idx) in schoolOptions" :key="s.id" class="picker-item">{{ s.name }}</view>
</picker-view-column>
</picker-view>
<view class="picker-actions">
<button @click="showSchoolPicker = false">鍙栨秷</button>
<button @click="confirmSchoolPicker" class="picker-actions-button">纭畾</button>
</view>
</view>
</view>
<view class="form-group" v-if="['grade', 'class'].includes(roleForm.scope_type ?? '') && selectedSchoolIndex >= 0">
<text class="form-label">閫夋嫨骞寸骇:</text>
<view class="picker-field" @click="showGradePicker = true">
<text>{{ selectedGradeIndex >= 0 ? gradeOptions[selectedGradeIndex].name : '请烽€夋嫨骞寸骇' }}</text>
<text class="picker-arrow">></text>
</view>
<view v-if="showGradePicker" class="picker-modal">
<picker-view
class="picker-view"
:value="[tempGradeIndex]"
:indicator-style="'height: 50px;'"
@change="onGradePickerViewChange"
>
<picker-view-column style="width:750rpx;">
<view v-for="(g, idx) in gradeOptions" :key="g.id" class="picker-item">{{ g.name }}</view>
</picker-view-column>
</picker-view>
<view class="picker-actions">
<button @click="showGradePicker = false">鍙栨秷</button>
<button @click="confirmGradePicker" class="picker-actions-button">纭畾</button>
</view>
</view>
</view>
<view class="form-group" v-if="roleForm.scope_type === 'class' && selectedGradeIndex >= 0">
<text class="form-label">閫夋嫨鐝骇:</text>
<view class="picker-field" @click="showClassPicker = true">
<text>{{ selectedClassIndex >= 0 ? classOptions[selectedClassIndex].name : '请烽€夋嫨鐝骇' }}</text>
<text class="picker-arrow">></text>
</view>
<view v-if="showClassPicker" class="picker-modal">
<picker-view
class="picker-view"
:value="[tempClassIndex]"
:indicator-style="'height: 50px;'"
@change="onClassPickerViewChange"
>
<picker-view-column style="width:750rpx;">
<view v-for="(c, idx) in classOptions" :key="c.id" class="picker-item">{{ c.name }}</view>
</picker-view-column>
</picker-view>
<view class="picker-actions">
<button @click="showClassPicker = false">鍙栨秷</button>
<button @click="confirmClassPicker" class="picker-actions-button">纭畾</button>
</view>
</view>
</view>
</view>
<view class="modal-footer">
<button class="cancel-btn" @click="showRoleModal = false">鍙栨秷</button>
<button class="submit-btn" @click="addRole" :disabled="canAddRole === false">娣诲姞</button>
</view>
</view>
</view>
<!-- 删除角色确认 -->
<view class="modal" v-if="showDeleteModal">
<view class="modal-content">
<view class="modal-header">
<text class="modal-title">纭绉婚櫎角掕壊</text>
<text class="close-btn" @click="showDeleteModal = false">脳</text>
</view>
<view class="modal-body">
<text>纭畾瑕佺Щ闄よ用户名鐨勪互涓嬭色插悧></text>
<text class="role-confirm-text">
{{ deleteRole?.role_name }}{{ (deleteRole?.scope_name != null && deleteRole?.scope_name !== '') ? ' (' + deleteRole?.scope_name + ')' : '' }}
</text>
</view>
<view class="modal-footer">
<button class="cancel-btn" @click="showDeleteModal = false">鍙栨秷</button>
<button class="confirm-btn" @click="deleteUserRole">纭</button>
</view>
</view>
</view>
</admin-layout>
</view>
</template>
<script lang="uts">
import { ref, computed, onMounted } from 'vue'
import AdminLayout from '../layout.uvue'
import supa from '@/components/supadb/aksupainstance.uts'
import { hasPermission, getUserPermissions } from '@/utils/permissionService.uts'
import { state as userStore } from '@/utils/store.uts'
import { UserDetail, UserRole, EditForm, RoleForm, RoleOption, RegionOption, SchoolOption, GradeOption, ClassOption } from '../admintypes.uts'
export default {
name: 'UserDetail',
components: {
AdminLayout
},
setup() {
const db = supa
// 鑾峰彇用户名ID
const userId = ref('')
// let query: any = undefined
// try {
// query = (uni.getEnterOptionsSync() as any)?.query
// } catch (e) {
// query = undefined
// }
// if (query != null && typeof query.id === 'string') {
// userId.value = query.id
// }
// 鍩虹状态讹拷?
const loading = ref(true)
const user = ref<UserDetail>({
id: '',
username: '',
is_active: false,
created_at: ''
})
const userRoles = ref<UserRole[]>([])
const showRolePicker = ref(false)
const tempRoleIndex = ref(-1)
const showRegionPicker = ref(false)
const tempRegionIndex = ref(-1)
const showSchoolPicker = ref(false)
const tempSchoolIndex = ref(-1)
const showGradePicker = ref(false)
const tempGradeIndex = ref(-1)
const showClassPicker = ref(false)
const tempClassIndex = ref(-1)
// 缂栬緫用户名淇℃伅状态讹拷?
const showEditModal = ref(false)
const editForm = ref<EditForm>({
name: '',
email: '',
phone: '',
is_active: true
})
// 添加角色状态讹拷?
const showRoleModal = ref(false)
const roleOptions = ref<RoleOption[]>([])
const selectedRoleIndex = ref(-1)
const roleForm = ref<RoleForm>({
name: '',
level: 0,
description: '',
is_system: false,
scope_type: null,
scope_id: null
})
// 作用域状态
const regionOptions = ref<RegionOption[]>([])
const selectedRegionIndex = ref(-1)
const schoolOptions = ref<SchoolOption[]>([])
const selectedSchoolIndex = ref(-1)
const gradeOptions = ref<GradeOption[]>([])
const selectedGradeIndex = ref(-1)
const classOptions = ref<ClassOption[]>([])
const selectedClassIndex = ref(-1)
// 创犻櫎角掕壊状态讹拷?
const showDeleteModal = ref(false)
const deleteRole = ref<UserRole | null>(null)
// 鏉冮檺状态讹拷?
const canManageUserInfo = ref(false)
const canManageUserRoles = ref(false)
// 计算属性烇拷?
const showScopeSelection = computed(() => {
const role = selectedRoleIndex.value >= 0 ? roleOptions.value[selectedRoleIndex.value] : null
return role != null && role?.requires_scope === true
})
const canAddRole = computed(() => {
if (selectedRoleIndex.value < 0) return false
const role = roleOptions.value[selectedRoleIndex.value]
if (role == null) return false
if (role?.requires_scope !== true) return true
if (roleForm.value.scope_type == null || roleForm.value.scope_type === '') return false
if (roleForm.value.scope_type === 'region') {
return selectedRegionIndex.value >= 0
}
if (roleForm.value.scope_type === 'school') {
return selectedRegionIndex.value >= 0 && selectedSchoolIndex.value >= 0
}
if (roleForm.value.scope_type === 'grade') {
return selectedRegionIndex.value >= 0 && selectedSchoolIndex.value >= 0 && selectedGradeIndex.value >= 0
}
if (roleForm.value.scope_type === 'class') {
return selectedRegionIndex.value >= 0 && selectedSchoolIndex.value >= 0 &&
selectedGradeIndex.value >= 0 && selectedClassIndex.value >= 0
}
return false
})
// 格式化日期
const formatDate = (dateStr: string): string => {
if (dateStr == null || dateStr === '') return '-'
try {
const date = new Date(dateStr)
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
} catch (error) {
return dateStr
}
}
// 鑾峰彇用户名淇℃伅
const fetchUserInfo = async () => {
loading.value = true
console.log('fetchUserInfo')
try {
const result = await db.from('ak_users')
.select('*', {})
.eq('id', userId.value)
.execute()
console.log(result)
const { data , error } = result;
if (error != null) {
console.error('鑾峰彇用户名淇℃伅澶辫触:', error)
uni.showToast({
title: '鑾峰彇用户名淇℃伅澶辫触',
icon: 'none'
})
}
if (data != null) {
let adata: UTSJSONObject | null = null
if (Array.isArray(data) && data.length > 0) {
adata = data[0] as UTSJSONObject
} else if (data != null && typeof data === 'object' && typeof data !== 'string') {
adata = data as UTSJSONObject
}
if (adata != null) {
user.value = {
id: adata.getString("id") ?? '',
username: adata.getString("username") ?? '',
is_active: adata.getBoolean("is_active")??false,
created_at: adata.getString("created_at") ?? '',
name: adata.getString("name") ?? '',
email: adata.getString("email") ?? '',
phone: adata.getString("phone") ?? '',
last_login: adata.getString("last_login") ?? ''
} as UserDetail
console.log(user.value)
// 设置编辑表单的初始值嬶拷?
editForm.value = {
name: user.value.name ?? '',
email: user.value.email ?? '',
phone: user.value.phone ?? '',
is_active: user.value.is_active ?? true
} as EditForm
console.log(editForm.value)
} else {
uni.showToast({
title: '手句笉创拌用户名',
icon: 'none'
})
uni.navigateBack()
}
} else {
uni.showToast({
title: '手句笉创拌用户名',
icon: 'none'
})
uni.navigateBack()
}
} catch (error) {
console.error('鑾峰彇用户名淇℃伅寮傚父:', error)
uni.showToast({
title: '鑾峰彇用户名淇℃伅澶辫触',
icon: 'none'
})
uni.navigateBack()
} finally {
loading.value = false
}
}
console.log('ak detail 11')
// 鑾峰彇用户名角掕壊
const fetchUserRoles = async () => {
try {
const result = await db.from('ak_user_roles')
.select(`
id,
user_id,
role_id,
scope_type,
scope_id,
role:ak_roles(name, level)
`, {})
.eq('user_id', userId.value)
.order('scope_type', { ascending: true })
.execute();
console.log(result)
const { data, error } = result;
if (error!=null) {
console.error('鑾峰彇用户名角掕壊澶辫触:', error)
return
}
if (data as Array<UTSJSONObject> != null) {
const arr: UserRole[] = []
for (let i = 0; i < data.length; i++) {
const item = data[i] as UTSJSONObject
arr.push({
id: item.getString("id") ?? '',
user_id: item.getString("user_id") ?? '',
role_id: item.getString("role_id") ?? '',
role_name: (() => {
const roleObj = item.get("role")
return roleObj != null ? (roleObj as UTSJSONObject).getString("name") ?? '' : ''
})(),
level: (() => {
const roleObj = item.get("role")
return roleObj != null ? (roleObj as UTSJSONObject).getNumber("level") ?? 0 : 0
})(),
scope_type: item.getString("scope_type") ?? '',
scope_id: item.getString("scope_id") ?? '',
scope_name: item.getString("name")
})
}
userRoles.value = arr
}
} catch (error) {
console.error('鑾峰彇用户名角掕壊寮傚父:', error)
}
}
// 鑾峰彇角掕壊创楄〃
const fetchRoles = async () => {
try {
const result = await db.from('ak_roles')
.select('*', {})
.order('level', { ascending: false })
.execute()
const { data, error } = result;
console.log(data,error)
if (error != null) {
console.error('鑾峰彇角掕壊创楄〃澶辫触:', error)
return
}
if (data as Array<UTSJSONObject> != null) {
const arr: RoleOption[] = []
for (let i = 0; i < data.length; i++) {
const role = data[i] as UTSJSONObject
arr.push({
id: role.getString("id") ?? '',
name: role.getString("name") ?? '',
level: role.getNumber("level") ?? 0,
requires_scope: role.getBoolean("requires_scope") ?? false
})
}
roleOptions.value = arr
}
} catch (error) {
console.error('鑾峰彇角掕壊创楄〃寮傚父:', error)
}
}
// 鑾峰彇地板尯创楄〃
const fetchRegions = async () => {
try {
const result = await db.from('ak_regions')
.select('*', {})
.order('level', { ascending: true })
.order('name', { ascending: true })
.execute();
const { data, error } = result;
console.log(data, error)
if (error != null) {
console.error('鑾峰彇地板尯创楄〃澶辫触:', error)
return
}
if (data as Array<UTSJSONObject> != null) {
const arr: RegionOption[] = []
for (let i = 0; i < data.length; i++) {
const region = data[i] as UTSJSONObject
arr.push({
id: region.getString("id") ?? '',
name: region.getString("name") ?? ''
})
}
regionOptions.value = arr
}
} catch (error) {
console.error('鑾峰彇地板尯创楄〃寮傚父:', error)
}
}
// 鑾峰彇瀛︽牎创楄〃
const fetchSchools = async (regionId: string) => {
try {
const result = await db.from('ak_schools')
.select('*', {})
.eq('region_id', regionId)
.order('name', { ascending: true })
.execute()
const { data, error } = result;
if (error != null) {
console.error('鑾峰彇瀛︽牎创楄〃澶辫触:', error)
return
}
if (data as Array<UTSJSONObject> != null) {
const arr: SchoolOption[] = []
for (let i = 0; i < data.length; i++) {
const school = data[i] as UTSJSONObject
arr.push({
id: school.getString("id") ?? '',
name: school.getString("name") ?? '',
region_id: school.getString("region_id") ?? ''
})
}
schoolOptions.value = arr
selectedSchoolIndex.value = -1
}
} catch (error) {
console.error('鑾峰彇瀛︽牎创楄〃寮傚父:', error)
}
}
// 鑾峰彇骞寸骇创楄〃
const fetchGrades = async (schoolId: string) => {
try {
const result = await db.from('ak_grades')
.select('*', {})
.eq('school_id', schoolId)
.order('order_num', { ascending: true })
.execute()
const { data, error } = result;
if (error != null) {
console.error('鑾峰彇骞寸骇创楄〃澶辫触:', error)
return
}
if (data as Array<UTSJSONObject> != null) {
const arr: GradeOption[] = []
for (let i = 0; i < data.length; i++) {
const grade = data[i] as UTSJSONObject
arr.push({
id: grade.getString("id") ?? '',
name: grade.getString("name") ?? '',
school_id: grade.getString("school_id") ?? ''
})
}
gradeOptions.value = arr
selectedGradeIndex.value = -1
// 閲嶇疆鐝骇閫夋嫨
classOptions.value = []
selectedClassIndex.value = -1
}
} catch (error) {
console.error('鑾峰彇骞寸骇创楄〃寮傚父:', error)
}
}
// 鑾峰彇鐝骇创楄〃
const fetchClasses = async (gradeId: string) => {
try {
const result = await db.from('ak_classes')
.select('*', {})
.eq('grade_id', gradeId)
.order('name', { ascending: true })
.execute()
const { data, error } = result;
if (error != null) {
console.error('鑾峰彇鐝骇创楄〃澶辫触:', error)
return
}
if (data as Array<UTSJSONObject> != null) {
const arr: ClassOption[] = []
for (let i = 0; i < data.length; i++) {
const cls = data[i] as UTSJSONObject
arr.push({
id: cls.getString("id") ?? '',
name: cls.getString("name") ?? '',
grade_id: cls.getString("grade_id") ?? ''
})
}
classOptions.value = arr
selectedClassIndex.value = -1
}
} catch (error) {
console.error('鑾峰彇鐝骇创楄〃寮傚父:', error)
}
}
// 检查权限
const checkPermissions = async () => {
try {
const uid = userId.value;
canManageUserInfo.value = await hasPermission({ userId: uid, permissionCode: 'admin.users.manage' })
canManageUserRoles.value = await hasPermission({ userId: uid, permissionCode: 'admin.roles.assign' })
} catch (error) {
console.error('权限检查异常', error)
}
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
// 淇濆瓨用户名淇℃伅
const saveUserInfo = async () => {
try {
const result = await db.from('ak_users')
.update({
name: editForm.value.name,
email: editForm.value.email,
phone: editForm.value.phone,
is_active: editForm.value.is_active
})
.eq('id', userId.value)
.execute()
const { error } = result;
if (error != null) {
console.error('修存柊用户名淇℃伅澶辫触:', error)
uni.showToast({
title: '淇濆瓨澶辫触',
icon: 'none'
})
return
}
uni.showToast({
title: '淇濆瓨密愬姛',
icon: 'success'
})
// 修存柊最湴用户名淇℃伅
user.value.name = editForm.value.name
user.value.email = editForm.value.email
user.value.phone = editForm.value.phone
user.value.is_active = editForm.value.is_active
showEditModal.value = false
} catch (error) {
console.error('修存柊用户名淇℃伅寮傚父:', error)
uni.showToast({
title: '淇濆瓨澶辫触',
icon: 'none'
})
}
}
// 角掕壊閫夋嫨
const onRolePickerViewChange = (e: UniPickerViewChangeEvent) => {
let index = -1
if (e != null && e.detail != null) {
index = e.detail.value[0]
}
selectedRoleIndex.value = index
const role = roleOptions.value[index]
roleForm.value.role_id = role.id
roleForm.value.role_name = role.name
roleForm.value.level = role.level
if (!role.requires_scope) {
roleForm.value.scope_type = null
roleForm.value.scope_id = null
} else if (roleForm.value.scope_type == null || roleForm.value.scope_type === '') {
roleForm.value.scope_type = 'region'
}
}
// 淇敼作用域类型
const changeScopeType = (type: string) => {
roleForm.value.scope_type = type
roleForm.value.scope_id = null
selectedRegionIndex.value = -1
selectedSchoolIndex.value = -1
selectedGradeIndex.value = -1
selectedClassIndex.value = -1
// 閲嶇疆鐩稿叧閫夐」
schoolOptions.value = []
gradeOptions.value = []
classOptions.value = []
}
// 地板尯閫夋嫨
const onRegionPickerViewChange = (e: UniPickerViewChangeEvent) => {
let index = -1
if (e != null && e.detail != null) {
index = e.detail.value[0]
}
selectedRegionIndex.value = index
const region = regionOptions.value[index]
if (roleForm.value.scope_type === 'region') {
roleForm.value.scope_id = region.id
} else if (["school", "grade", "class"].includes(roleForm.value.scope_type ?? '')) {
fetchSchools(region.id)
}
}
// 瀛︽牎閫夋嫨
const onSchoolPickerViewChange = (e: UniPickerViewChangeEvent) => {
let index = -1
if (e != null && e.detail != null) {
index = e.detail.value[0]
}
selectedSchoolIndex.value = index
const school = schoolOptions.value[index]
if (roleForm.value.scope_type === 'school') {
roleForm.value.scope_id = school.id
} else if (["grade", "class"].includes(roleForm.value.scope_type ?? '')) {
fetchGrades(school.id)
}
}
// 骞寸骇閫夋嫨
const onGradePickerViewChange = (e: UniPickerViewChangeEvent) => {
let index = -1
if (e != null && e.detail != null) {
index = e.detail.value[0]
}
selectedGradeIndex.value = index
const grade = gradeOptions.value[index]
if (roleForm.value.scope_type === 'grade') {
roleForm.value.scope_id = grade.id
} else if (roleForm.value.scope_type === 'class') {
fetchClasses(grade.id)
}
}
// 鐝骇閫夋嫨
const onClassPickerViewChange = (e: UniPickerViewChangeEvent) => {
let index = -1
if (e != null && e.detail != null) {
index = e.detail.value[0]
}
selectedClassIndex.value = index
const cls = classOptions.value[index]
roleForm.value.scope_id = cls.id
}
// 添加角色
const addRole = async () => {
if (!canAddRole.value) return
try {
// 妫€鏌ユ槸后﹀凡最夌浉后岀殑角掕壊创嗛厤
const existingRole = userRoles.value.find(r =>
r.role_id === roleForm.value.role_id &&
r.scope_type === roleForm.value.scope_type &&
r.scope_id === roleForm.value.scope_id
)
if (existingRole != null) {
uni.showToast({
title: '请ヨ色插凡创嗛厤缁欐用户名',
icon: 'none'
})
return
}
const result = await db.from('ak_user_roles')
.insert({
user_id: userId.value,
role_id: roleForm.value.role_id,
scope_type: roleForm.value.scope_type ,
scope_id: roleForm.value.scope_id
})
.execute()
const { error } = result;
if (error != null) {
console.error('添加角色澶辫触:', error)
uni.showToast({
title: '添加角色澶辫触',
icon: 'none'
})
return
}
uni.showToast({
title: '添加角色密愬姛',
icon: 'success'
})
fetchUserRoles()
showRoleModal.value = false
selectedRoleIndex.value = -1
selectedRegionIndex.value = -1
selectedSchoolIndex.value = -1
selectedGradeIndex.value = -1
selectedClassIndex.value = -1
roleForm.value = {
role_id: '',
role_name: '',
name: '',
level: 0,
description: '',
is_system: false,
scope_type: null,
scope_id: null
} as RoleForm
} catch (error) {
console.error('添加角色寮傚父:', error)
uni.showToast({
title: '添加角色澶辫触',
icon: 'none'
})
}
}
// 纭创犻櫎角掕壊
const confirmDeleteRole = (role: UserRole) => {
deleteRole.value = role
showDeleteModal.value = true
}
// 创犻櫎用户名角掕壊
const deleteUserRole = async () => {
const role = deleteRole.value
if (role == null) return
try {
const result = await db.from('ak_user_roles')
.delete()
.eq('id', role.id)
.execute()
const { error } = result;
if (error != null) {
console.error('创犻櫎角掕壊澶辫触:', error)
uni.showToast({
title: '绉婚櫎角掕壊澶辫触',
icon: 'none'
})
return
}
uni.showToast({
title: '角色已删除,
icon: 'success'
})
fetchUserRoles()
} catch (error) {
console.error('创犻櫎角掕壊寮傚父:', error)
uni.showToast({
title: '绉婚櫎角掕壊澶辫触',
icon: 'none'
})
} finally {
showDeleteModal.value = false
deleteRole.value = null
}
}
const confirmRolePicker = () => {
selectedRoleIndex.value = tempRoleIndex.value
showRolePicker.value = false
// 鍙€夛細角﹀彂 onRolePickerViewChange 閫昏緫
// onRolePickerViewChange({ detail: { value: [tempRoleIndex.value] } } as UniPickerViewChangeEvent)
}
const confirmRegionPicker = () => {
selectedRegionIndex.value = tempRegionIndex.value
showRegionPicker.value = false
// onRegionPickerViewChange({ detail: { value: [tempRegionIndex.value] } } as UniPickerViewChangeEvent)
}
const confirmSchoolPicker = () => {
selectedSchoolIndex.value = tempSchoolIndex.value
showSchoolPicker.value = false
// onSchoolPickerViewChange({ detail: { value: [tempSchoolIndex.value] } } as UniPickerViewChangeEvent)
}
const confirmGradePicker = () => {
selectedGradeIndex.value = tempGradeIndex.value
showGradePicker.value = false
// onGradePickerViewChange({ detail: { value: [tempGradeIndex.value] } } as UniPickerViewChangeEvent)
}
const confirmClassPicker = () => {
selectedClassIndex.value = tempClassIndex.value
showClassPicker.value = false
// onClassPickerViewChange({ detail: { value: [tempClassIndex.value] } } as any)
}
// 椤甸潰鍔犺浇
onLoad((op:UTSJSONObject) => {
console.log(op)
userId.value = op?.getString("id") ?? ""
if (userId.value==null) {
uni.showToast({
title: '用户名ID涓嶈兘涓虹┖',
icon: 'none'
})
}
fetchUserInfo()
fetchUserRoles()
fetchRoles()
fetchRegions()
checkPermissions()
})
console.log('ak detail 33')
return {
loading,
user,
userRoles,
showEditModal,
editForm,
showRoleModal,
roleOptions,
selectedRoleIndex,
roleForm,
showScopeSelection,
regionOptions,
selectedRegionIndex,
schoolOptions,
selectedSchoolIndex,
gradeOptions,
selectedGradeIndex,
classOptions,
selectedClassIndex,
canAddRole,
showDeleteModal,
deleteRole,
canManageUserInfo,
canManageUserRoles,
// 鏂规硶
formatDate,
goBack,
saveUserInfo,
changeScopeType,
// 宸插簾寮冪殑 handleRegionChange/handleSchoolChange/handleGradeChange/handleClassChange 涓嶅啀瀵煎嚭
addRole,
confirmDeleteRole,
deleteUserRole,
// picker-view寮圭獥状态佸拰绱㈠紩
showRolePicker,
tempRoleIndex,
showRegionPicker,
tempRegionIndex,
showSchoolPicker,
tempSchoolIndex,
showGradePicker,
tempGradeIndex,
showClassPicker,
tempClassIndex,
// picker-view浜嬩欢
onRolePickerViewChange,
confirmRolePicker,
onRegionPickerViewChange,
confirmRegionPicker,
onSchoolPickerViewChange,
confirmSchoolPicker,
onGradePickerViewChange,
confirmGradePicker,
onClassPickerViewChange,
confirmClassPicker,
}
}
}
</script>
<style>
.user-detail-page {
flex: 1;
}
.container {
padding: 20px;
}
.page-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.back-button {
display: flex;
align-items: center;
padding: 5px 10px;
margin-right: 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.page-title {
flex: 1;
font-size: 24px;
font-weight: bold;
}
.header-actions {
display: flex;
align-items: center;
}
.primary-btn {
height: 36px;
padding: 0 15px;
background-color: #1890ff;
color: white;
border: none;
border-radius: 4px;
}
.loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.user-info-card, .roles-card {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
overflow: hidden;
}
.card-header {
padding: 15px 20px;
border-bottom: 1px solid #ebeef5;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 18px;
font-weight: bold;
}
.edit-link {
color: #1890ff;
cursor: pointer;
}
.card-body {
padding: 15px 20px;
}
.info-row {
display: flex;
padding: 10px 0;
}
.info-label {
width: 100px;
font-weight: normal;
}
.info-value {
flex: 1;
}
.status-badge {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
}
.status-badge.active {
background-color: #f0f9eb;
color: #67c23a;
}
.status-badge.inactive {
background-color: #fef0f0;
color: #f56c6c;
}
.empty-text {
color: #909399;
text-align: center;
padding: 20px 0;
}
.role-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #ebeef5;
}
.role-item:last-child {
border-bottom: none;
}
.role-info {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.role-name {
font-weight: normal;
margin-right: 5px;
}
.role-scope {
color: #606266;
margin-right: 10px;
}
.role-level {
background-color: #f5f7fa;
color: #909399;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
}
.role-actions {
display: flex;
padding: 10px;
}
.delete-btn {
color: #f56c6c;
cursor: pointer;
}
/* 妯℃€佹鏍峰紡 */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background-color: white;
width: 400px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.modal-header {
padding: 15px;
border-bottom: 1px solid #ebeef5;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-title {
font-size: 18px;
font-weight: bold;
}
.close-btn {
font-size: 20px;
cursor: pointer;
}
.modal-body {
padding: 15px;
}
.form-group {
margin-bottom: 15px;
}
.form-label {
display: block;
margin-bottom: 5px;
}
.modal-body input {
width: 100%;
height: 36px;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 0 10px;
}
.picker-text {
display: inline-block;
width: 100%;
height: 36px;
line-height: 36px;
padding: 0 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.status-toggle, .scope-toggle {
display: flex;
}
.toggle-option {
flex: 1;
height: 36px;
line-height: 36px;
text-align: center;
border: 1px solid #dcdfe6;
font-size: 13px;
}
.toggle-option:first-child {
border-radius: 4px 0 0 4px;
}
.toggle-option:last-child {
border-radius: 0 4px 4px 0;
}
.toggle-option.active {
background-color: #1890ff;
color: white;
border-color: #1890ff;
}
.role-confirm-text {
display: block;
font-weight: normal;
margin-top: 10px;
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
}
.modal-footer {
padding: 10px 15px;
border-top: 1px solid #ebeef5;
display: flex;
justify-content: flex-end;
}
.cancel-btn, .confirm-btn, .submit-btn {
padding: 8px 15px;
border-radius: 4px;
margin-left: 10px;
}
.cancel-btn {
border: 1px solid #dcdfe6;
background-color: white;
}
.confirm-btn, .submit-btn {
background-color: #1890ff;
color: white;
border: none;
}
</style>