feat: 增加工作流和知识库三级权限

- 抽取统一资源访问骨架与部门可见范围判断

- 接入工作流和知识库的 READ/MANAGE 权限校验

- 增加可见范围配置与只读态前端交互
This commit is contained in:
2026-03-29 17:25:55 +08:00
parent f49d94e2fe
commit 22ceabff96
58 changed files with 3053 additions and 85 deletions

View File

@@ -23,9 +23,9 @@ import {
ElDropdown,
ElDropdownItem,
ElDropdownMenu,
ElInput,
ElMessage,
ElMessageBox,
ElInput,
ElTable,
ElTableColumn,
ElTree,
@@ -43,6 +43,10 @@ const props = defineProps({
type: String,
required: true,
},
manageable: {
type: Boolean,
default: true,
},
});
const pageDataRef = ref();
@@ -143,6 +147,10 @@ const handleResetSearch = () => {
};
const openAddDialog = () => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
editData.value = {
collectionId: props.knowledgeId,
categoryId:
@@ -189,12 +197,17 @@ const exportFaqExcel = async () => {
}
};
const handleImportSuccess = () => {
const handleImportSuccess = async () => {
await reloadCategoryTree();
refreshList();
};
const handleMoreActionCommand = (command: string) => {
if (command === 'import') {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
importDialogVisible.value = true;
return;
}
@@ -208,6 +221,10 @@ const handleMoreActionCommand = (command: string) => {
};
const openEditDialog = (row: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
editData.value = {
id: row.id,
collectionId: row.collectionId,
@@ -223,6 +240,10 @@ const openEditDialog = (row: any) => {
};
const saveFaq = async (payload: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
const url = payload.id ? '/api/v1/faqItem/update' : '/api/v1/faqItem/save';
const res = await api.post(url, payload);
if (res.errorCode === 0) {
@@ -237,6 +258,10 @@ const saveFaq = async (payload: any) => {
};
const removeFaq = (row: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
ElMessageBox.confirm($t('message.deleteAlert'), $t('message.noticeTitle'), {
confirmButtonText: $t('button.confirm'),
cancelButtonText: $t('button.cancel'),
@@ -259,6 +284,10 @@ const handleCategoryClick = (data: any) => {
};
const openAddRootCategory = () => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
categoryDialogTitle.value = $t('documentCollection.faq.addCategory');
categoryDialogDisableParent.value = false;
categoryEditData.value = {
@@ -271,6 +300,10 @@ const openAddRootCategory = () => {
};
const openAddSiblingCategory = (node: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
categoryDialogTitle.value = $t('documentCollection.faq.addSiblingCategory');
categoryDialogDisableParent.value = false;
categoryEditData.value = {
@@ -286,6 +319,10 @@ const openAddSiblingCategory = (node: any) => {
};
const openAddChildCategory = (node: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
if (node.isDefault) {
ElMessage.warning(
$t('documentCollection.faq.defaultCategoryChildForbidden'),
@@ -308,6 +345,10 @@ const openAddChildCategory = (node: any) => {
};
const openEditCategory = (node: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
categoryDialogTitle.value = $t('documentCollection.faq.editCategory');
categoryDialogDisableParent.value = !!node.isDefault;
categoryEditData.value = {
@@ -324,6 +365,10 @@ const openEditCategory = (node: any) => {
};
const removeCategory = (node: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
if (node.isDefault) {
ElMessage.warning(
$t('documentCollection.faq.defaultCategoryDeleteForbidden'),
@@ -612,6 +657,10 @@ const demoteCategory = async (node: any) => {
};
const saveCategory = async (payload: any) => {
if (!props.manageable) {
ElMessage.warning($t('documentCollection.managePermissionHint'));
return;
}
const url = payload.id
? '/api/v1/faqCategory/update'
: '/api/v1/faqCategory/save';
@@ -692,6 +741,7 @@ onMounted(() => {
<div class="faq-category-header">
<span>{{ $t('documentCollection.faq.categoryTree') }}</span>
<ElButton
v-if="props.manageable"
link
type="primary"
:icon="Plus"
@@ -719,7 +769,7 @@ onMounted(() => {
data.categoryName
}}</span>
<div
v-if="!data.isVirtual && !data.isDefault"
v-if="props.manageable && !data.isVirtual && !data.isDefault"
class="faq-category-node-actions"
@click.stop
>
@@ -770,7 +820,10 @@ onMounted(() => {
>
{{ $t('documentCollection.faq.addChildCategory') }}
</ElDropdownItem>
<ElDropdownItem :icon="Edit" @click="openEditCategory(data)">
<ElDropdownItem
:icon="Edit"
@click="openEditCategory(data)"
>
{{ $t('button.edit') }}
</ElDropdownItem>
<ElDropdownItem
@@ -791,6 +844,9 @@ onMounted(() => {
<div class="faq-content-pane">
<div class="faq-header">
<div v-if="!props.manageable" class="faq-readonly-tip">
{{ $t('documentCollection.managePermissionHint') }}
</div>
<div class="faq-toolbar">
<div class="faq-search-actions">
<ElInput
@@ -809,19 +865,25 @@ onMounted(() => {
</div>
<div class="faq-primary-actions">
<ElButton type="primary" :icon="Plus" @click="openAddDialog">
<ElButton
v-if="props.manageable"
type="primary"
:icon="Plus"
@click="openAddDialog"
>
{{ $t('button.add') }}
</ElButton>
<ElDropdown
trigger="click"
@command="handleMoreActionCommand"
>
<ElDropdown trigger="click" @command="handleMoreActionCommand">
<ElButton :icon="MoreFilled">
{{ $t('documentCollection.faq.import.moreActions') }}
</ElButton>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem command="import" :icon="Upload">
<ElDropdownItem
v-if="props.manageable"
command="import"
:icon="Upload"
>
{{ $t('button.import') }}
</ElDropdownItem>
<ElDropdownItem
@@ -871,6 +933,7 @@ onMounted(() => {
show-overflow-tooltip
/>
<ElTableColumn
v-if="props.manageable"
:label="$t('common.handle')"
width="170"
align="right"
@@ -1002,6 +1065,13 @@ onMounted(() => {
margin-bottom: 12px;
}
.faq-readonly-tip {
margin-bottom: 10px;
font-size: 12px;
line-height: 1.6;
color: var(--el-text-color-secondary);
}
.faq-toolbar {
display: flex;
align-items: center;
@@ -1062,8 +1132,11 @@ onMounted(() => {
}
:deep(
.faq-category-tree > .el-tree-node > .el-tree-node__content > .el-tree-node__expand-icon
) {
.faq-category-tree
> .el-tree-node
> .el-tree-node__content
> .el-tree-node__expand-icon
) {
display: none;
}
@@ -1077,7 +1150,9 @@ onMounted(() => {
background-color: var(--el-fill-color-light);
}
:deep(.faq-category-tree .el-tree-node__content:hover .faq-category-node-actions) {
:deep(
.faq-category-tree .el-tree-node__content:hover .faq-category-node-actions
) {
opacity: 1;
}
@@ -1086,7 +1161,9 @@ onMounted(() => {
color: hsl(var(--primary));
}
:deep(.el-tree-node.is-current > .el-tree-node__content .faq-category-node-actions) {
:deep(
.el-tree-node.is-current > .el-tree-node__content .faq-category-node-actions
) {
opacity: 1;
}