Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web
This commit is contained in:
@@ -40,7 +40,7 @@ v-for="(category, categoryName) in filteredCategories"
|
||||
|
||||
<div class="apps-grid">
|
||||
<div
|
||||
v-for="app in category.apps"
|
||||
v-for="app in category.apps"
|
||||
:key="app.id"
|
||||
class="app-card"
|
||||
@click="handleAppClick(app)">
|
||||
@@ -77,7 +77,7 @@ v-for="app in category.apps"
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as ClientApi from '@/api/system/oauth2/client'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
||||
export default {
|
||||
name: 'AppManager',
|
||||
@@ -138,7 +138,7 @@ export default {
|
||||
methods: {
|
||||
async fetchApps() {
|
||||
try {
|
||||
const data = await ClientApi.getOAuth2ClientPage({ pageNo: 1, pageSize: 1000 })
|
||||
const data = await RoleApi.getMyPage({ pageNo: 1, pageSize: 1000 })
|
||||
const list = data?.list || []
|
||||
this.appsData = list.map(item => ({
|
||||
id: item.id,
|
||||
|
||||
233
src/views/system/role/RoleAssignAppForm.vue
Normal file
233
src/views/system/role/RoleAssignAppForm.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<DesignPopup v-model="dialogVisible" title="应用权限" :is-footer="true" width="40%">
|
||||
<div class="p-20px">
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
|
||||
<el-form-item label="角色名称">
|
||||
<el-tag>{{ formData.name }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色标识">
|
||||
<el-tag>{{ formData.code }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="应用权限">
|
||||
<el-card class="w-full h-400px !overflow-y-scroll" shadow="never">
|
||||
<template #header>
|
||||
全选/全不选:
|
||||
<el-switch
|
||||
v-model="treeNodeAll"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
inline-prompt
|
||||
@change="handleCheckedTreeNodeAll"
|
||||
/>
|
||||
</template>
|
||||
<!-- 修复:移除未使用的node变量,优化无子集Tree展示 -->
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="appOptions"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
empty-text="暂无应用数据"
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
v-loading="tableLoading"
|
||||
class="app-tree-list"
|
||||
:indent="0"
|
||||
>
|
||||
<!-- 修复:只解构使用到的data,删除未使用的node -->
|
||||
<template #default="{ data }">
|
||||
<div class="app-tree-node">
|
||||
<span class="node-name">{{ data.name }}</span>
|
||||
<span class="node-clientId">客户端ID:{{ data.clientId }}</span>
|
||||
<span class="node-status">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="data.status" />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
|
||||
defineOptions({ name: 'SystemRoleAssignAppForm' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const formLoading = ref(false)
|
||||
const tableLoading = ref(false)
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
code: '',
|
||||
clientIds: [] as number[]
|
||||
})
|
||||
const formRef = ref()
|
||||
const treeRef = ref()
|
||||
const appOptions = ref<any[]>([])
|
||||
const treeNodeAll = ref(false)
|
||||
|
||||
// 优化Tree配置:明确指定无children,纯列表展示
|
||||
const treeProps = {
|
||||
label: 'name',
|
||||
children: () => [] // 强制返回空数组,彻底避免树形层级
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (row: RoleApi.RoleVO) => {
|
||||
dialogVisible.value = true
|
||||
formLoading.value = true
|
||||
resetForm()
|
||||
// 设置角色信息
|
||||
formData.value.id = row.id
|
||||
formData.value.name = row.name
|
||||
formData.value.code = row.code
|
||||
try {
|
||||
// 加载应用列表(pageSize=99,模拟不分页)
|
||||
await getAppList()
|
||||
// 获取当前角色已有的应用ID
|
||||
const roleAppIds = await RoleApi.getRoleAppIds(row.id)
|
||||
formData.value.clientIds = roleAppIds
|
||||
// 设置Tree选中状态
|
||||
await nextTick()
|
||||
treeRef.value?.setCheckedNodes([])
|
||||
formData.value.clientIds.forEach(id => {
|
||||
treeRef.value?.setChecked(id, true, false)
|
||||
})
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
/** 获取应用列表(固定pageSize=99,不分页) */
|
||||
const getAppList = async () => {
|
||||
tableLoading.value = true
|
||||
try {
|
||||
const data = await RoleApi.getMyPage({
|
||||
pageNo: 1,
|
||||
pageSize: 99 // 设为99,覆盖大部分场景的数量,模拟不分页
|
||||
})
|
||||
// 优化:确保返回数据绝对没有children字段,避免树形展示
|
||||
appOptions.value = data.list.map(item => {
|
||||
const { children, ...rest } = item // 移除可能存在的children字段
|
||||
return { ...rest, children: [] } // 强制设置空children
|
||||
})
|
||||
} finally {
|
||||
tableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 全选/全不选(适配Tree组件) */
|
||||
const handleCheckedTreeNodeAll = () => {
|
||||
if (treeNodeAll.value) {
|
||||
// 全选:选中所有节点
|
||||
treeRef.value?.setCheckedNodes(appOptions.value)
|
||||
formData.value.clientIds = appOptions.value.map(item => item.id)
|
||||
} else {
|
||||
// 全不选:清空选中
|
||||
treeRef.value?.setCheckedNodes([])
|
||||
formData.value.clientIds = []
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success'])
|
||||
const submitForm = async () => {
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 获取Tree选中的节点ID
|
||||
formData.value.clientIds = treeRef.value?.getCheckedKeys(false) as number[]
|
||||
const data = {
|
||||
roleId: formData.value.id,
|
||||
clientIds: formData.value.clientIds
|
||||
}
|
||||
await RoleApi.saveApp(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
treeNodeAll.value = false
|
||||
formData.value = {
|
||||
id: 0,
|
||||
name: '',
|
||||
code: '',
|
||||
clientIds: []
|
||||
}
|
||||
appOptions.value = []
|
||||
treeRef.value?.setCheckedNodes([])
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 自定义Tree节点样式,模拟表格列布局
|
||||
.app-tree-list {
|
||||
::v-deep(.el-tree-node) {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-bottom: 1px solid #f5f7fa;
|
||||
}
|
||||
|
||||
::v-deep(.el-tree-node__content) {
|
||||
padding: 0 10px;
|
||||
height: 40px !important;
|
||||
// 移除树形节点的默认图标
|
||||
.el-tree-node__expand-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-tree-node__children) {
|
||||
padding-left: 0 !important; // 彻底去掉子节点缩进
|
||||
}
|
||||
}
|
||||
|
||||
// 节点内容布局
|
||||
.app-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.node-name {
|
||||
flex: 0 0 120px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.node-clientId {
|
||||
flex: 0 0 150px;
|
||||
color: #666;
|
||||
}
|
||||
.node-status {
|
||||
flex: 0 0 80px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 卡片样式优化
|
||||
::v-deep(.el-card__header) {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
::v-deep(.el-card__body) {
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -58,6 +58,15 @@
|
||||
<span>菜单权限</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:command="{ type: 'app', row }"
|
||||
v-if="checkPermi(['system:permission:assign-role-app'])"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<Icon icon="ep:menu" />
|
||||
<span>应用权限</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:command="{ type: 'data', row }"
|
||||
v-if="checkPermi(['system:permission:assign-role-data-scope'])"
|
||||
@@ -86,6 +95,8 @@
|
||||
<RoleAssignMenuForm ref="assignMenuFormRef" @success="getTableData" />
|
||||
<!-- 表单弹窗:数据权限 -->
|
||||
<RoleDataPermissionForm ref="dataPermissionFormRef" @success="getTableData" />
|
||||
<!-- 表单弹窗:应用权限 -->
|
||||
<RoleAssignAppForm ref="assignAppFormRef" @success="getTableData" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
@@ -96,6 +107,7 @@ import * as RoleApi from '@/api/system/role'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import RoleAssignMenuForm from './RoleAssignMenuForm.vue'
|
||||
import RoleDataPermissionForm from './RoleDataPermissionForm.vue'
|
||||
import RoleAssignAppForm from './RoleAssignAppForm.vue'
|
||||
|
||||
defineOptions({ name: 'SystemRole' })
|
||||
|
||||
@@ -186,7 +198,7 @@ const tablePage = ref({
|
||||
total: 0
|
||||
})
|
||||
const permission = getCurrPermi(['system:role'])
|
||||
|
||||
const assignAppFormRef = ref()
|
||||
const crudRef = ref()
|
||||
|
||||
useCrudHeight(crudRef)
|
||||
@@ -197,6 +209,11 @@ const menuHandle = ({ row, type }) => {
|
||||
if (type == 'menu') openAssignMenuForm(row)
|
||||
else if (type == 'data') openDataPermissionForm(row)
|
||||
else if (type == 'del') rowDel(row)
|
||||
else if (type == 'app') openAssignAppForm(row)
|
||||
}
|
||||
|
||||
const openAssignAppForm = (row) => {
|
||||
assignAppFormRef.value.open(row)
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
|
||||
Reference in New Issue
Block a user