fetch(modify):修改bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m6s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m6s
This commit is contained in:
@@ -42,10 +42,6 @@ export function useLogin() {
|
|||||||
// 加载状态
|
// 加载状态
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
// 拖拽验证
|
|
||||||
const isPassing = ref(false)
|
|
||||||
const isClickPass = ref(false)
|
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formData = reactive<LoginForm>({
|
const formData = reactive<LoginForm>({
|
||||||
account: '',
|
account: '',
|
||||||
@@ -115,13 +111,6 @@ export function useLogin() {
|
|||||||
const valid = await formRef.value.validate()
|
const valid = await formRef.value.validate()
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
|
|
||||||
// 检查拖拽验证
|
|
||||||
if (!isPassing.value) {
|
|
||||||
isClickPass.value = true
|
|
||||||
ElMessage.warning(t('login.placeholder[2]'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
// 判断使用 Mock 还是真实 API
|
// 判断使用 Mock 还是真实 API
|
||||||
@@ -248,14 +237,6 @@ export function useLogin() {
|
|||||||
}, 150)
|
}, 150)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 重置拖拽验证
|
|
||||||
*/
|
|
||||||
const resetDragVerify = () => {
|
|
||||||
isPassing.value = false
|
|
||||||
isClickPass.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组件挂载时初始化
|
// 组件挂载时初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initForm()
|
initForm()
|
||||||
@@ -266,11 +247,8 @@ export function useLogin() {
|
|||||||
formData,
|
formData,
|
||||||
rules,
|
rules,
|
||||||
loading,
|
loading,
|
||||||
isPassing,
|
|
||||||
isClickPass,
|
|
||||||
mockAccounts,
|
mockAccounts,
|
||||||
setupAccount,
|
setupAccount,
|
||||||
handleLogin,
|
handleLogin
|
||||||
resetDragVerify
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,6 +239,15 @@ async function processBackendMenu(router: Router): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
// 如果是超级管理员(user_type === 1),直接使用所有 asyncRoutes
|
||||||
|
if (userStore.isSuperAdmin) {
|
||||||
|
const menuList = asyncRoutes.map((route) => menuDataToRouter(route))
|
||||||
|
await registerAndStoreMenu(router, menuList, closeLoading)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通用户:使用后端返回的菜单
|
||||||
const backendMenus = userStore.menus || []
|
const backendMenus = userStore.menus || []
|
||||||
const routeMap = buildRouteMap(asyncRoutes)
|
const routeMap = buildRouteMap(asyncRoutes)
|
||||||
|
|
||||||
|
|||||||
@@ -51,11 +51,10 @@ export interface DeviceQueryParams extends PaginationParams {
|
|||||||
|
|
||||||
// 设备列表响应
|
// 设备列表响应
|
||||||
export interface DeviceListResponse {
|
export interface DeviceListResponse {
|
||||||
list: Device[] | null // 设备列表
|
items: Device[] | null // 设备列表
|
||||||
page: number // 当前页码
|
page: number // 当前页码
|
||||||
page_size: number // 每页数量
|
size: number // 每页数量
|
||||||
total: number // 总数
|
total: number // 总数
|
||||||
total_pages: number // 总页数
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 设备卡绑定相关 ==========
|
// ========== 设备卡绑定相关 ==========
|
||||||
|
|||||||
@@ -36,15 +36,17 @@ export interface Permission {
|
|||||||
children?: Permission[] // 子权限列表(树形结构)
|
children?: Permission[] // 子权限列表(树形结构)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 权限树节点
|
// 权限树节点(匹配后端 DtoPermissionTreeNode)
|
||||||
export interface PermissionTreeNode {
|
export interface PermissionTreeNode {
|
||||||
id: string | number
|
id: number // 权限ID
|
||||||
label: string
|
perm_code: string // 权限编码
|
||||||
value: string
|
perm_name: string // 权限名称
|
||||||
permissionCode: string
|
perm_type: number // 权限类型 (1:菜单, 2:按钮)
|
||||||
permissionType: PermissionType
|
url?: string // 请求路径
|
||||||
parentId?: string | number
|
platform?: string // 适用端口 (all:全部, web:Web后台, h5:H5端)
|
||||||
children?: PermissionTreeNode[]
|
sort?: number // 排序值
|
||||||
|
available_for_role_types?: string // 可用角色类型 (1:平台角色, 2:客户角色)
|
||||||
|
children?: PermissionTreeNode[] // 子权限列表
|
||||||
}
|
}
|
||||||
|
|
||||||
// 权限查询参数
|
// 权限查询参数
|
||||||
|
|||||||
@@ -69,30 +69,12 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<div class="drag-verify">
|
|
||||||
<div class="drag-verify-content" :class="{ error: !isPassing && isClickPass }">
|
|
||||||
<ArtDragVerify
|
|
||||||
ref="dragVerify"
|
|
||||||
v-model:value="isPassing"
|
|
||||||
:width="width < 500 ? 328 : 438"
|
|
||||||
:text="$t('login.sliderText')"
|
|
||||||
textColor="var(--art-gray-800)"
|
|
||||||
:successText="$t('login.sliderSuccessText')"
|
|
||||||
:progressBarBg="getCssVar('--el-color-primary')"
|
|
||||||
background="var(--art-gray-200)"
|
|
||||||
handlerBg="var(--art-main-bg-color)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p class="error-text" :class="{ 'show-error-text': !isPassing && isClickPass }">{{
|
|
||||||
$t('login.placeholder[2]')
|
|
||||||
}}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="forget-password">
|
<div class="forget-password">
|
||||||
<ElCheckbox v-model="formData.rememberPassword">{{
|
<ElCheckbox v-model="formData.rememberPassword">{{
|
||||||
$t('login.rememberPwd')
|
$t('login.rememberPwd')
|
||||||
}}</ElCheckbox>
|
}}</ElCheckbox>
|
||||||
<RouterLink :to="RoutesAlias.ForgetPassword">{{ $t('login.forgetPwd') }}</RouterLink>
|
<!--<RouterLink :to="RoutesAlias.ForgetPassword">{{ $t('login.forgetPwd') }}</RouterLink>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 30px">
|
<div style="margin-top: 30px">
|
||||||
@@ -138,24 +120,16 @@
|
|||||||
formData,
|
formData,
|
||||||
rules,
|
rules,
|
||||||
loading,
|
loading,
|
||||||
isPassing,
|
|
||||||
isClickPass,
|
|
||||||
mockAccounts,
|
mockAccounts,
|
||||||
setupAccount,
|
setupAccount,
|
||||||
handleLogin
|
handleLogin
|
||||||
} = useLogin()
|
} = useLogin()
|
||||||
|
|
||||||
const dragVerify = ref()
|
|
||||||
const systemName = AppConfig.systemInfo.name
|
const systemName = AppConfig.systemInfo.name
|
||||||
const { width } = useWindowSize()
|
|
||||||
|
|
||||||
// 处理提交
|
// 处理提交
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
await handleLogin()
|
await handleLogin()
|
||||||
// 重置拖拽验证
|
|
||||||
if (dragVerify.value) {
|
|
||||||
dragVerify.value.reset()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换语言
|
// 切换语言
|
||||||
|
|||||||
@@ -105,11 +105,31 @@
|
|||||||
:label="t('orderManagement.createForm.deviceId')"
|
:label="t('orderManagement.createForm.deviceId')"
|
||||||
prop="device_id"
|
prop="device_id"
|
||||||
>
|
>
|
||||||
<ElInputNumber
|
<ElSelect
|
||||||
v-model="createForm.device_id"
|
v-model="createForm.device_id"
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
reserve-keyword
|
||||||
:placeholder="t('orderManagement.createForm.deviceIdPlaceholder')"
|
:placeholder="t('orderManagement.createForm.deviceIdPlaceholder')"
|
||||||
|
:remote-method="searchDevices"
|
||||||
|
:loading="deviceSearchLoading"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
clearable
|
||||||
|
>
|
||||||
|
<ElOption
|
||||||
|
v-for="device in deviceOptions"
|
||||||
|
:key="device.id"
|
||||||
|
:label="`${device.device_no} (${device.device_name})`"
|
||||||
|
:value="device.id"
|
||||||
|
>
|
||||||
|
<div style="display: flex; justify-content: space-between">
|
||||||
|
<span>{{ device.device_no }}</span>
|
||||||
|
<span style="color: var(--el-text-color-secondary); font-size: 12px">
|
||||||
|
{{ device.device_name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</ElOption>
|
||||||
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElForm>
|
</ElForm>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -220,7 +240,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { OrderService, CardService } from '@/api/modules'
|
import { OrderService, CardService, DeviceService } from '@/api/modules'
|
||||||
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import type {
|
import type {
|
||||||
@@ -232,7 +252,8 @@
|
|||||||
BuyerType,
|
BuyerType,
|
||||||
OrderPaymentMethod,
|
OrderPaymentMethod,
|
||||||
OrderCommissionStatus,
|
OrderCommissionStatus,
|
||||||
StandaloneIotCard
|
StandaloneIotCard,
|
||||||
|
Device
|
||||||
} from '@/types/api'
|
} from '@/types/api'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
@@ -367,6 +388,10 @@
|
|||||||
const iotCardOptions = ref<StandaloneIotCard[]>([])
|
const iotCardOptions = ref<StandaloneIotCard[]>([])
|
||||||
const cardSearchLoading = ref(false)
|
const cardSearchLoading = ref(false)
|
||||||
|
|
||||||
|
// 设备搜索相关
|
||||||
|
const deviceOptions = ref<Device[]>([])
|
||||||
|
const deviceSearchLoading = ref(false)
|
||||||
|
|
||||||
// 搜索IoT卡(根据ICCID)
|
// 搜索IoT卡(根据ICCID)
|
||||||
const searchIotCards = async (query: string) => {
|
const searchIotCards = async (query: string) => {
|
||||||
if (!query) {
|
if (!query) {
|
||||||
@@ -392,6 +417,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索设备(根据设备号device_no)
|
||||||
|
const searchDevices = async (query: string) => {
|
||||||
|
if (!query) {
|
||||||
|
deviceOptions.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceSearchLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await DeviceService.getDevices({
|
||||||
|
device_no: query,
|
||||||
|
page: 1,
|
||||||
|
page_size: 20
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
deviceOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Search devices failed:', error)
|
||||||
|
deviceOptions.value = []
|
||||||
|
} finally {
|
||||||
|
deviceSearchLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 格式化货币 - 将分转换为元
|
// 格式化货币 - 将分转换为元
|
||||||
const formatCurrency = (amount: number): string => {
|
const formatCurrency = (amount: number): string => {
|
||||||
return `¥${(amount / 100).toFixed(2)}`
|
return `¥${(amount / 100).toFixed(2)}`
|
||||||
@@ -606,8 +656,8 @@
|
|||||||
// 显示创建订单对话框
|
// 显示创建订单对话框
|
||||||
const showCreateDialog = async () => {
|
const showCreateDialog = async () => {
|
||||||
createDialogVisible.value = true
|
createDialogVisible.value = true
|
||||||
// 默认加载20条IoT卡数据
|
// 默认加载20条IoT卡和设备数据
|
||||||
await loadDefaultIotCards()
|
await Promise.all([loadDefaultIotCards(), loadDefaultDevices()])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载默认IoT卡列表
|
// 加载默认IoT卡列表
|
||||||
@@ -629,6 +679,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载默认设备列表
|
||||||
|
const loadDefaultDevices = async () => {
|
||||||
|
deviceSearchLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await DeviceService.getDevices({
|
||||||
|
page: 1,
|
||||||
|
page_size: 20
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
deviceOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Load default devices failed:', error)
|
||||||
|
deviceOptions.value = []
|
||||||
|
} finally {
|
||||||
|
deviceSearchLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 对话框关闭后的清理
|
// 对话框关闭后的清理
|
||||||
const handleCreateDialogClosed = () => {
|
const handleCreateDialogClosed = () => {
|
||||||
// 重置表单(会同时清除验证状态)
|
// 重置表单(会同时清除验证状态)
|
||||||
@@ -640,8 +709,9 @@
|
|||||||
createForm.iot_card_id = null
|
createForm.iot_card_id = null
|
||||||
createForm.device_id = null
|
createForm.device_id = null
|
||||||
|
|
||||||
// 清空IoT卡搜索结果
|
// 清空IoT卡和设备搜索结果
|
||||||
iotCardOptions.value = []
|
iotCardOptions.value = []
|
||||||
|
deviceOptions.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建订单
|
// 创建订单
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<ArtTable
|
<ArtTable
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
row-key="ID"
|
row-key="id"
|
||||||
:data="permissionList"
|
:data="permissionList"
|
||||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||||
:default-expand-all="false"
|
:default-expand-all="false"
|
||||||
@@ -110,25 +110,20 @@
|
|||||||
ElMessage,
|
ElMessage,
|
||||||
ElMessageBox,
|
ElMessageBox,
|
||||||
ElTag,
|
ElTag,
|
||||||
ElSwitch,
|
|
||||||
type FormInstance,
|
type FormInstance,
|
||||||
type FormRules
|
type FormRules
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import { PermissionService } from '@/api/modules'
|
import { PermissionService } from '@/api/modules'
|
||||||
import type { Permission, CreatePermissionParams } from '@/types/api'
|
import type { CreatePermissionParams, PermissionTreeNode } from '@/types/api'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||||
import {
|
import {
|
||||||
CommonStatus,
|
|
||||||
getStatusText,
|
|
||||||
PermissionType,
|
PermissionType,
|
||||||
PERMISSION_TYPE_OPTIONS,
|
PERMISSION_TYPE_OPTIONS,
|
||||||
PERMISSION_TYPE_SELECT_OPTIONS,
|
PERMISSION_TYPE_SELECT_OPTIONS,
|
||||||
getPermissionTypeText,
|
getPermissionTypeText,
|
||||||
getPermissionTypeTag,
|
getPermissionTypeTag
|
||||||
STATUS_SELECT_OPTIONS
|
|
||||||
} from '@/config/constants'
|
} from '@/config/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'Permission' })
|
defineOptions({ name: 'Permission' })
|
||||||
@@ -137,8 +132,7 @@
|
|||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
perm_name: '',
|
perm_name: '',
|
||||||
perm_code: '',
|
perm_code: '',
|
||||||
perm_type: undefined as number | undefined,
|
perm_type: undefined as number | undefined
|
||||||
status: undefined as number | undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
@@ -173,16 +167,6 @@
|
|||||||
clearable: true,
|
clearable: true,
|
||||||
placeholder: '请选择'
|
placeholder: '请选择'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '状态',
|
|
||||||
prop: 'status',
|
|
||||||
type: 'select',
|
|
||||||
options: STATUS_SELECT_OPTIONS,
|
|
||||||
config: {
|
|
||||||
clearable: true,
|
|
||||||
placeholder: '请选择'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -192,19 +176,20 @@
|
|||||||
{ label: '权限标识', prop: 'perm_code' },
|
{ label: '权限标识', prop: 'perm_code' },
|
||||||
{ label: '权限类型', prop: 'perm_type' },
|
{ label: '权限类型', prop: 'perm_type' },
|
||||||
{ label: '菜单路径', prop: 'url' },
|
{ label: '菜单路径', prop: 'url' },
|
||||||
|
{ label: '适用端口', prop: 'platform' },
|
||||||
{ label: '排序', prop: 'sort' },
|
{ label: '排序', prop: 'sort' },
|
||||||
{ label: '状态', prop: 'status' },
|
|
||||||
{ label: '创建时间', prop: 'CreatedAt' },
|
|
||||||
{ label: '操作', prop: 'operation' }
|
{ label: '操作', prop: 'operation' }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 权限列表
|
// 权限列表(树形结构)
|
||||||
const permissionList = ref<Permission[]>([])
|
const permissionList = ref<PermissionTreeNode[]>([])
|
||||||
|
// 原始权限树数据(用于搜索过滤)
|
||||||
|
const originalPermissionTree = ref<PermissionTreeNode[]>([])
|
||||||
|
|
||||||
const tableRef = ref()
|
const tableRef = ref()
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const dialogType = ref('add')
|
const dialogType = ref('add')
|
||||||
const currentRow = ref<Permission | null>(null)
|
const currentRow = ref<PermissionTreeNode | null>(null)
|
||||||
const currentPermissionId = ref<number>(0)
|
const currentPermissionId = ref<number>(0)
|
||||||
const submitLoading = ref(false)
|
const submitLoading = ref(false)
|
||||||
|
|
||||||
@@ -242,7 +227,7 @@
|
|||||||
{
|
{
|
||||||
prop: 'perm_name',
|
prop: 'perm_name',
|
||||||
label: '权限名称',
|
label: '权限名称',
|
||||||
minWidth: 200
|
width: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'perm_code',
|
prop: 'perm_code',
|
||||||
@@ -253,7 +238,7 @@
|
|||||||
prop: 'perm_type',
|
prop: 'perm_type',
|
||||||
label: '权限类型',
|
label: '权限类型',
|
||||||
width: 120,
|
width: 120,
|
||||||
formatter: (row: any) => {
|
formatter: (row: PermissionTreeNode) => {
|
||||||
return h(ElTag, { type: getPermissionTypeTag(row.perm_type) }, () =>
|
return h(ElTag, { type: getPermissionTypeTag(row.perm_type) }, () =>
|
||||||
getPermissionTypeText(row.perm_type)
|
getPermissionTypeText(row.perm_type)
|
||||||
)
|
)
|
||||||
@@ -261,43 +246,32 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'url',
|
prop: 'url',
|
||||||
label: '菜单路径',
|
label: '菜单路径'
|
||||||
width: 180
|
},
|
||||||
|
{
|
||||||
|
prop: 'platform',
|
||||||
|
label: '适用端口',
|
||||||
|
width: 120,
|
||||||
|
formatter: (row: PermissionTreeNode) => {
|
||||||
|
const platformMap: Record<string, string> = {
|
||||||
|
all: '全部',
|
||||||
|
web: 'Web后台',
|
||||||
|
h5: 'H5端'
|
||||||
|
}
|
||||||
|
return platformMap[row.platform || 'all'] || row.platform
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'sort',
|
prop: 'sort',
|
||||||
label: '排序',
|
label: '排序',
|
||||||
width: 80
|
width: 80
|
||||||
},
|
},
|
||||||
{
|
|
||||||
prop: 'status',
|
|
||||||
label: '状态',
|
|
||||||
width: 100,
|
|
||||||
formatter: (row: any) => {
|
|
||||||
return h(ElSwitch, {
|
|
||||||
modelValue: row.status,
|
|
||||||
activeValue: CommonStatus.ENABLED,
|
|
||||||
inactiveValue: CommonStatus.DISABLED,
|
|
||||||
activeText: getStatusText(CommonStatus.ENABLED),
|
|
||||||
inactiveText: getStatusText(CommonStatus.DISABLED),
|
|
||||||
inlinePrompt: true,
|
|
||||||
'onUpdate:modelValue': (val: string | number | boolean) =>
|
|
||||||
handleStatusChange(row, val as number)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'CreatedAt',
|
|
||||||
label: '创建时间',
|
|
||||||
width: 180,
|
|
||||||
formatter: (row: any) => formatDateTime(row.CreatedAt)
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
prop: 'operation',
|
prop: 'operation',
|
||||||
label: '操作',
|
label: '操作',
|
||||||
width: 160,
|
width: 120,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
formatter: (row: any) => {
|
formatter: (row: PermissionTreeNode) => {
|
||||||
return h('div', { style: 'display: flex; gap: 8px;' }, [
|
return h('div', { style: 'display: flex; gap: 8px;' }, [
|
||||||
h(ArtButtonTable, {
|
h(ArtButtonTable, {
|
||||||
type: 'edit',
|
type: 'edit',
|
||||||
@@ -312,51 +286,59 @@
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
// 将扁平数据转换为树形结构
|
// 过滤权限树(根据搜索条件)
|
||||||
const buildTreeData = (flatData: Permission[]): Permission[] => {
|
const filterPermissionTree = (
|
||||||
const map = new Map<number, Permission>()
|
tree: PermissionTreeNode[],
|
||||||
const result: Permission[] = []
|
filters: typeof searchForm
|
||||||
|
): PermissionTreeNode[] => {
|
||||||
|
return tree.reduce<PermissionTreeNode[]>((acc, node) => {
|
||||||
|
// 克隆节点避免修改原始数据
|
||||||
|
const newNode = { ...node }
|
||||||
|
|
||||||
// 先创建所有节点的映射
|
// 检查当前节点是否匹配搜索条件
|
||||||
flatData.forEach((item) => {
|
let matches = true
|
||||||
map.set(item.ID, { ...item, children: [] })
|
|
||||||
})
|
|
||||||
|
|
||||||
// 构建树形结构
|
if (filters.perm_name && !newNode.perm_name.includes(filters.perm_name)) {
|
||||||
map.forEach((item) => {
|
matches = false
|
||||||
if (item.parent_id && map.has(item.parent_id)) {
|
|
||||||
const parent = map.get(item.parent_id)!
|
|
||||||
if (!parent.children) {
|
|
||||||
parent.children = []
|
|
||||||
}
|
}
|
||||||
parent.children.push(item)
|
if (filters.perm_code && !newNode.perm_code.includes(filters.perm_code)) {
|
||||||
} else {
|
matches = false
|
||||||
// 没有父节点的是根节点
|
|
||||||
result.push(item)
|
|
||||||
}
|
}
|
||||||
})
|
if (filters.perm_type !== undefined && newNode.perm_type !== filters.perm_type) {
|
||||||
|
matches = false
|
||||||
// 递归排序
|
|
||||||
const sortTree = (nodes: Permission[]): Permission[] => {
|
|
||||||
return nodes
|
|
||||||
.sort((a, b) => (a.sort || 0) - (b.sort || 0))
|
|
||||||
.map((node) => ({
|
|
||||||
...node,
|
|
||||||
children: node.children && node.children.length > 0 ? sortTree(node.children) : undefined
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortTree(result)
|
// 递归过滤子节点
|
||||||
|
if (newNode.children && newNode.children.length > 0) {
|
||||||
|
newNode.children = filterPermissionTree(newNode.children, filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取权限列表
|
// 如果当前节点匹配或有匹配的子节点,则保留
|
||||||
|
if (matches || (newNode.children && newNode.children.length > 0)) {
|
||||||
|
acc.push(newNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取权限树
|
||||||
const getPermissionList = async () => {
|
const getPermissionList = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await PermissionService.getPermissions(searchForm)
|
const response = await PermissionService.getPermissionTree()
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
const flatData = response.data.items || []
|
originalPermissionTree.value = response.data || []
|
||||||
// 将扁平数据转换为树形结构
|
|
||||||
permissionList.value = buildTreeData(flatData)
|
// 应用搜索过滤
|
||||||
|
const hasFilters =
|
||||||
|
searchForm.perm_name || searchForm.perm_code || searchForm.perm_type !== undefined
|
||||||
|
|
||||||
|
if (hasFilters) {
|
||||||
|
permissionList.value = filterPermissionTree(originalPermissionTree.value, searchForm)
|
||||||
|
} else {
|
||||||
|
permissionList.value = originalPermissionTree.value
|
||||||
|
}
|
||||||
|
|
||||||
// 构建权限树选项
|
// 构建权限树选项
|
||||||
buildPermissionTreeOptions()
|
buildPermissionTreeOptions()
|
||||||
}
|
}
|
||||||
@@ -367,14 +349,14 @@
|
|||||||
|
|
||||||
// 构建权限树选项
|
// 构建权限树选项
|
||||||
const buildPermissionTreeOptions = () => {
|
const buildPermissionTreeOptions = () => {
|
||||||
const buildTree = (list: Permission[]): any[] => {
|
const buildTree = (list: PermissionTreeNode[]): any[] => {
|
||||||
return list.map((item) => ({
|
return list.map((item) => ({
|
||||||
value: item.ID,
|
value: item.id,
|
||||||
label: item.perm_name,
|
label: item.perm_name,
|
||||||
children: item.children && item.children.length > 0 ? buildTree(item.children) : undefined
|
children: item.children && item.children.length > 0 ? buildTree(item.children) : undefined
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
permissionTreeOptions.value = buildTree(permissionList.value)
|
permissionTreeOptions.value = buildTree(originalPermissionTree.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
@@ -394,7 +376,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 显示对话框
|
// 显示对话框
|
||||||
const showDialog = (type: string, row?: any) => {
|
const showDialog = (type: string, row?: PermissionTreeNode) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
dialogType.value = type
|
dialogType.value = type
|
||||||
|
|
||||||
@@ -404,12 +386,14 @@
|
|||||||
|
|
||||||
if (type === 'edit' && row) {
|
if (type === 'edit' && row) {
|
||||||
currentRow.value = row
|
currentRow.value = row
|
||||||
currentPermissionId.value = row.ID
|
currentPermissionId.value = row.id
|
||||||
|
// 需要从API获取完整的权限数据,因为树节点可能不包含所有字段
|
||||||
|
// 暂时使用树节点的数据
|
||||||
Object.assign(form, {
|
Object.assign(form, {
|
||||||
perm_name: row.perm_name,
|
perm_name: row.perm_name,
|
||||||
perm_code: row.perm_code,
|
perm_code: row.perm_code,
|
||||||
perm_type: row.perm_type,
|
perm_type: row.perm_type,
|
||||||
parent_id: row.parent_id,
|
parent_id: undefined, // 树结构中没有parent_id,需要从API获取
|
||||||
url: row.url || '',
|
url: row.url || '',
|
||||||
platform: row.platform || 'all',
|
platform: row.platform || 'all',
|
||||||
sort: row.sort || 0
|
sort: row.sort || 0
|
||||||
@@ -421,7 +405,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 删除权限
|
// 删除权限
|
||||||
const deletePermission = (row: Permission) => {
|
const deletePermission = (row: PermissionTreeNode) => {
|
||||||
// 检查是否有子权限
|
// 检查是否有子权限
|
||||||
if (row.children && row.children.length > 0) {
|
if (row.children && row.children.length > 0) {
|
||||||
ElMessage.warning('该权限下存在子权限,请先删除子权限')
|
ElMessage.warning('该权限下存在子权限,请先删除子权限')
|
||||||
@@ -435,7 +419,7 @@
|
|||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
try {
|
try {
|
||||||
await PermissionService.deletePermission(row.ID)
|
await PermissionService.deletePermission(row.id)
|
||||||
ElMessage.success('删除成功')
|
ElMessage.success('删除成功')
|
||||||
await getPermissionList()
|
await getPermissionList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -489,21 +473,6 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 状态切换
|
|
||||||
const handleStatusChange = async (row: any, newStatus: number) => {
|
|
||||||
const oldStatus = row.status
|
|
||||||
// 先更新UI
|
|
||||||
row.status = newStatus
|
|
||||||
try {
|
|
||||||
await PermissionService.updatePermission(row.ID, { status: newStatus })
|
|
||||||
ElMessage.success('状态切换成功')
|
|
||||||
} catch (error) {
|
|
||||||
// 切换失败,恢复原状态
|
|
||||||
row.status = oldStatus
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面加载时获取权限列表
|
// 页面加载时获取权限列表
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getPermissionList()
|
getPermissionList()
|
||||||
|
|||||||
@@ -88,16 +88,20 @@
|
|||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
|
||||||
<!-- 分配权限对话框 -->
|
<!-- 分配权限对话框 -->
|
||||||
<ElDialog v-model="permissionDialogVisible" title="分配权限" width="500px">
|
<ElDialog v-model="permissionDialogVisible" title="分配权限" width="800px">
|
||||||
|
<div class="permission-assignment-container">
|
||||||
|
<!-- 左侧:权限树(用于添加) -->
|
||||||
|
<div class="permission-tree-section">
|
||||||
|
<div class="section-title">可分配权限</div>
|
||||||
<ElTree
|
<ElTree
|
||||||
ref="permissionTreeRef"
|
ref="permissionTreeRef"
|
||||||
:data="permissionTreeData"
|
:data="permissionTreeData"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
node-key="id"
|
node-key="id"
|
||||||
:default-checked-keys="selectedPermissions"
|
|
||||||
:props="{ children: 'children', label: 'label' }"
|
:props="{ children: 'children', label: 'label' }"
|
||||||
:default-expand-all="false"
|
:default-expand-all="false"
|
||||||
class="permission-tree"
|
class="permission-tree"
|
||||||
|
@check="handlePermissionCheck"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<span style="display: flex; align-items: center; gap: 8px">
|
<span style="display: flex; align-items: center; gap: 8px">
|
||||||
@@ -111,16 +115,44 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</ElTree>
|
</ElTree>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:已分配权限列表 -->
|
||||||
|
<div class="assigned-permissions-section">
|
||||||
|
<div class="section-title">已分配权限</div>
|
||||||
|
<div class="assigned-list">
|
||||||
|
<div
|
||||||
|
v-for="perm in assignedPermissionsList"
|
||||||
|
:key="perm.id"
|
||||||
|
class="assigned-item"
|
||||||
|
>
|
||||||
|
<ElTag
|
||||||
|
:type="perm.perm_type === 1 ? 'info' : 'success'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ perm.perm_type === 1 ? '菜单' : '按钮' }}
|
||||||
|
</ElTag>
|
||||||
|
<span class="perm-name">{{ perm.perm_name }}</span>
|
||||||
|
<ElButton
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
@click="removePermission(perm.id)"
|
||||||
|
>
|
||||||
|
移除
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<ElEmpty
|
||||||
|
v-if="assignedPermissionsList.length === 0"
|
||||||
|
description="暂无已分配权限"
|
||||||
|
:image-size="80"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<ElButton @click="permissionDialogVisible = false">取消</ElButton>
|
<ElButton @click="permissionDialogVisible = false">关闭</ElButton>
|
||||||
<ElButton
|
|
||||||
type="primary"
|
|
||||||
@click="handleAssignPermissions"
|
|
||||||
:loading="permissionSubmitLoading"
|
|
||||||
>
|
|
||||||
提交
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
@@ -140,7 +172,7 @@
|
|||||||
ElSwitch
|
ElSwitch
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import type { PlatformRole, Permission } from '@/types/api'
|
import type { PlatformRole, PermissionTreeNode } from '@/types/api'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
import { useAuth } from '@/composables/useAuth'
|
import { useAuth } from '@/composables/useAuth'
|
||||||
@@ -162,8 +194,10 @@
|
|||||||
const currentRoleId = ref<number>(0)
|
const currentRoleId = ref<number>(0)
|
||||||
const selectedPermissions = ref<number[]>([])
|
const selectedPermissions = ref<number[]>([])
|
||||||
const originalPermissions = ref<number[]>([]) // 保存原始权限,用于对比
|
const originalPermissions = ref<number[]>([]) // 保存原始权限,用于对比
|
||||||
const allPermissions = ref<Permission[]>([])
|
|
||||||
const permissionTreeData = ref<any[]>([])
|
const permissionTreeData = ref<any[]>([])
|
||||||
|
const assignedPermissionsList = ref<any[]>([]) // 已分配的权限列表(用于右侧显示)
|
||||||
|
const allPermissionsFlat = ref<PermissionTreeNode[]>([]) // 扁平化的所有权限数据
|
||||||
|
const isInitializingTree = ref(false) // 标记是否正在初始化树的选中状态
|
||||||
|
|
||||||
// 搜索表单初始值
|
// 搜索表单初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
@@ -319,73 +353,62 @@
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTableData()
|
getTableData()
|
||||||
loadAllPermissions()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 将扁平数据转换为树形结构
|
// 将权限树扁平化为一维数组
|
||||||
const buildTreeData = (flatData: Permission[]): any[] => {
|
const flattenPermissionTree = (treeNodes: PermissionTreeNode[]): PermissionTreeNode[] => {
|
||||||
const map = new Map<number, any>()
|
const result: PermissionTreeNode[] = []
|
||||||
const result: any[] = []
|
const flatten = (nodes: PermissionTreeNode[]) => {
|
||||||
|
nodes.forEach((node) => {
|
||||||
// 先创建所有节点的映射
|
|
||||||
flatData.forEach((item) => {
|
|
||||||
map.set(item.ID, {
|
|
||||||
id: item.ID,
|
|
||||||
label: item.perm_name,
|
|
||||||
perm_type: item.perm_type,
|
|
||||||
children: []
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// 构建树形结构
|
|
||||||
flatData.forEach((item) => {
|
|
||||||
const node = map.get(item.ID)!
|
|
||||||
if (item.parent_id && map.has(item.parent_id)) {
|
|
||||||
const parent = map.get(item.parent_id)!
|
|
||||||
parent.children.push(node)
|
|
||||||
} else {
|
|
||||||
// 没有父节点的是根节点
|
|
||||||
result.push(node)
|
result.push(node)
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
flatten(node.children)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
flatten(treeNodes)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// 递归排序和清理空children
|
// 将权限树节点转换为ElTree所需的格式
|
||||||
const sortAndCleanTree = (nodes: any[]): any[] => {
|
const buildTreeData = (treeNodes: PermissionTreeNode[]): any[] => {
|
||||||
return nodes
|
return treeNodes.map((node) => ({
|
||||||
.sort((a, b) => {
|
id: node.id,
|
||||||
const aItem = flatData.find((p) => p.ID === a.id)
|
label: node.perm_name,
|
||||||
const bItem = flatData.find((p) => p.ID === b.id)
|
perm_type: node.perm_type,
|
||||||
return (aItem?.sort || 0) - (bItem?.sort || 0)
|
children: node.children && node.children.length > 0 ? buildTreeData(node.children) : undefined
|
||||||
})
|
|
||||||
.map((node) => ({
|
|
||||||
...node,
|
|
||||||
children:
|
|
||||||
node.children && node.children.length > 0 ? sortAndCleanTree(node.children) : undefined
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortAndCleanTree(result)
|
// 加载所有权限树
|
||||||
}
|
|
||||||
|
|
||||||
// 加载所有权限列表
|
|
||||||
const loadAllPermissions = async () => {
|
const loadAllPermissions = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await PermissionService.getPermissions({ page: 1, page_size: 1000 })
|
const res = await PermissionService.getPermissionTree()
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
allPermissions.value = res.data.items || []
|
const treeData = res.data || []
|
||||||
|
// 扁平化所有权限数据,用于查找
|
||||||
|
allPermissionsFlat.value = flattenPermissionTree(treeData)
|
||||||
// 构建树形数据
|
// 构建树形数据
|
||||||
permissionTreeData.value = buildTreeData(allPermissions.value)
|
permissionTreeData.value = buildTreeData(treeData)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取权限列表失败:', error)
|
console.error('获取权限树失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据权限ID列表构建已分配权限列表
|
||||||
|
const buildAssignedPermissionsList = () => {
|
||||||
|
assignedPermissionsList.value = selectedPermissions.value
|
||||||
|
.map((id) => allPermissionsFlat.value.find((p) => p.id === id))
|
||||||
|
.filter((p) => p !== undefined) as PermissionTreeNode[]
|
||||||
|
}
|
||||||
|
|
||||||
// 显示分配权限对话框
|
// 显示分配权限对话框
|
||||||
const showPermissionDialog = async (row: PlatformRole) => {
|
const showPermissionDialog = async (row: PlatformRole) => {
|
||||||
currentRoleId.value = row.ID
|
currentRoleId.value = row.ID
|
||||||
selectedPermissions.value = []
|
selectedPermissions.value = []
|
||||||
originalPermissions.value = []
|
originalPermissions.value = []
|
||||||
|
assignedPermissionsList.value = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 每次打开对话框时重新加载最新的权限列表
|
// 每次打开对话框时重新加载最新的权限列表
|
||||||
@@ -393,75 +416,136 @@
|
|||||||
|
|
||||||
// 加载当前角色的权限
|
// 加载当前角色的权限
|
||||||
const res = await RoleService.getRolePermissions(row.ID)
|
const res = await RoleService.getRolePermissions(row.ID)
|
||||||
|
|
||||||
if (res.code === 0 || Array.isArray(res.data)) {
|
if (res.code === 0 || Array.isArray(res.data)) {
|
||||||
// 提取权限ID数组
|
// 提取权限ID数组
|
||||||
const permissions = res.data || []
|
const permissions = res.data || []
|
||||||
|
|
||||||
if (Array.isArray(permissions) && permissions.length > 0) {
|
if (Array.isArray(permissions) && permissions.length > 0) {
|
||||||
// 如果返回的是权限对象数组,提取ID
|
// 如果返回的是权限对象数组,提取ID或id字段
|
||||||
if (typeof permissions[0] === 'object' && 'ID' in permissions[0]) {
|
if (typeof permissions[0] === 'object') {
|
||||||
|
// 优先使用ID字段,如果没有则使用id字段
|
||||||
|
if ('ID' in permissions[0]) {
|
||||||
selectedPermissions.value = permissions.map((perm: any) => perm.ID)
|
selectedPermissions.value = permissions.map((perm: any) => perm.ID)
|
||||||
|
} else if ('id' in permissions[0]) {
|
||||||
|
selectedPermissions.value = permissions.map((perm: any) => perm.id)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果返回的是ID数组
|
// 如果返回的是ID数组
|
||||||
selectedPermissions.value = permissions
|
selectedPermissions.value = permissions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存原始权限,用于后续对比
|
// 保存原始权限,用于后续对比
|
||||||
originalPermissions.value = [...selectedPermissions.value]
|
originalPermissions.value = [...selectedPermissions.value]
|
||||||
|
|
||||||
|
// 构建已分配权限列表
|
||||||
|
buildAssignedPermissionsList()
|
||||||
|
|
||||||
// 数据加载完成后再打开对话框
|
// 数据加载完成后再打开对话框
|
||||||
permissionDialogVisible.value = true
|
permissionDialogVisible.value = true
|
||||||
|
|
||||||
|
// 等待DOM更新后设置树的初始选中状态
|
||||||
|
await nextTick()
|
||||||
|
if (permissionTreeRef.value) {
|
||||||
|
isInitializingTree.value = true
|
||||||
|
permissionTreeRef.value.setCheckedKeys(selectedPermissions.value, false)
|
||||||
|
// 延迟一点时间后取消初始化标记
|
||||||
|
setTimeout(() => {
|
||||||
|
isInitializingTree.value = false
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取角色权限失败:', error)
|
console.error('获取角色权限失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交分配权限
|
// 处理权限勾选(只允许添加,不允许取消)
|
||||||
const handleAssignPermissions = async () => {
|
const handlePermissionCheck = async (data: any, checkedInfo: any) => {
|
||||||
if (!permissionTreeRef.value) return
|
if (!permissionTreeRef.value) return
|
||||||
|
|
||||||
permissionSubmitLoading.value = true
|
// 如果正在初始化树,忽略此次check事件
|
||||||
|
if (isInitializingTree.value) return
|
||||||
|
|
||||||
|
// 获取当前勾选的所有节点(不包括半选状态)
|
||||||
|
const checkedKeys = permissionTreeRef.value.getCheckedKeys(false)
|
||||||
|
|
||||||
|
// 找出新增的权限(当前勾选中不在已分配列表中的)
|
||||||
|
const newPermissions = checkedKeys.filter((id) => !selectedPermissions.value.includes(id))
|
||||||
|
|
||||||
|
if (newPermissions.length > 0) {
|
||||||
try {
|
try {
|
||||||
// 获取选中的节点(包括半选中的父节点)
|
// 调用API分配权限
|
||||||
const checkedKeys = permissionTreeRef.value.getCheckedKeys()
|
await RoleService.assignPermissions(currentRoleId.value, newPermissions)
|
||||||
const halfCheckedKeys = permissionTreeRef.value.getHalfCheckedKeys()
|
|
||||||
const currentPermissions = [...checkedKeys, ...halfCheckedKeys]
|
|
||||||
|
|
||||||
// 对比原始权限和当前选中的权限,找出需要新增和移除的权限
|
// 更新已分配权限列表
|
||||||
const addedPermissions = currentPermissions.filter(
|
selectedPermissions.value = [...selectedPermissions.value, ...newPermissions]
|
||||||
(id) => !originalPermissions.value.includes(id)
|
buildAssignedPermissionsList()
|
||||||
)
|
|
||||||
const removedPermissions = originalPermissions.value.filter(
|
|
||||||
(id) => !currentPermissions.includes(id)
|
|
||||||
)
|
|
||||||
|
|
||||||
// 使用 Promise.all 并发执行新增和移除操作
|
ElMessage.success('权限添加成功')
|
||||||
const promises: Promise<any>[] = []
|
} catch (error) {
|
||||||
|
console.error('添加权限失败:', error)
|
||||||
|
ElMessage.error('权限添加失败')
|
||||||
|
|
||||||
// 如果有新增的权限,调用分配接口
|
// 如果添加失败,恢复树的选中状态
|
||||||
if (addedPermissions.length > 0) {
|
nextTick(() => {
|
||||||
promises.push(RoleService.assignPermissions(currentRoleId.value, addedPermissions))
|
permissionTreeRef.value?.setCheckedKeys(selectedPermissions.value, false)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有移除的权限,调用移除接口
|
// 重新设置树的选中状态为已分配的权限(阻止取消勾选)
|
||||||
if (removedPermissions.length > 0) {
|
nextTick(() => {
|
||||||
removedPermissions.forEach((permId) => {
|
permissionTreeRef.value?.setCheckedKeys(selectedPermissions.value, false)
|
||||||
promises.push(RoleService.removePermission(currentRoleId.value, permId))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待所有操作完成
|
// 移除单个权限
|
||||||
if (promises.length > 0) {
|
const removePermission = async (permId: number) => {
|
||||||
await Promise.all(promises)
|
try {
|
||||||
ElMessage.success('权限更新成功')
|
await RoleService.removePermission(currentRoleId.value, permId)
|
||||||
|
|
||||||
|
// 重新从服务器获取最新的权限列表
|
||||||
|
const res = await RoleService.getRolePermissions(currentRoleId.value)
|
||||||
|
|
||||||
|
if (res.code === 0 || Array.isArray(res.data)) {
|
||||||
|
const permissions = res.data || []
|
||||||
|
|
||||||
|
// 清空并重新设置权限列表
|
||||||
|
if (Array.isArray(permissions) && permissions.length > 0) {
|
||||||
|
if (typeof permissions[0] === 'object') {
|
||||||
|
if ('ID' in permissions[0]) {
|
||||||
|
selectedPermissions.value = permissions.map((perm: any) => perm.ID)
|
||||||
|
} else if ('id' in permissions[0]) {
|
||||||
|
selectedPermissions.value = permissions.map((perm: any) => perm.id)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ElMessage.info('权限未发生变化')
|
selectedPermissions.value = permissions
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedPermissions.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
permissionDialogVisible.value = false
|
// 重建已分配权限列表
|
||||||
|
buildAssignedPermissionsList()
|
||||||
|
|
||||||
|
// 更新树的选中状态
|
||||||
|
await nextTick()
|
||||||
|
if (permissionTreeRef.value) {
|
||||||
|
isInitializingTree.value = true
|
||||||
|
permissionTreeRef.value.setCheckedKeys(selectedPermissions.value, false)
|
||||||
|
setTimeout(() => {
|
||||||
|
isInitializingTree.value = false
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage.success('权限移除成功')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error('移除权限失败:', error)
|
||||||
} finally {
|
ElMessage.error('权限移除失败')
|
||||||
permissionSubmitLoading.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +699,35 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.permission-assignment-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
height: 500px;
|
||||||
|
|
||||||
|
.permission-tree-section,
|
||||||
|
.assigned-permissions-section {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-tree-section {
|
||||||
.permission-tree {
|
.permission-tree {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
:deep(.el-tree-node) {
|
:deep(.el-tree-node) {
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
}
|
}
|
||||||
@@ -625,4 +737,38 @@
|
|||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.assigned-permissions-section {
|
||||||
|
.assigned-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
.assigned-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: var(--el-fill-color-lighter);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.perm-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user