feat: 归档L03与L09审批发布能力
- 新增统一审批中心与审批管理页面,支持流程配置、审批详情与角色/用户审批对象 - 接入聊天助手、知识库、工作流的发布与删除审批,并补齐发布态校验与快照展示
This commit is contained in:
@@ -44,6 +44,12 @@ import CardPage from '#/components/page/CardList.vue';
|
||||
import PageData from '#/components/page/PageData.vue';
|
||||
import PageSide from '#/components/page/PageSide.vue';
|
||||
import DocumentCollectionModal from '#/views/ai/documentCollection/DocumentCollectionModal.vue';
|
||||
import AiResourceCornerMeta from '#/views/ai/shared/AiResourceCornerMeta.vue';
|
||||
import {
|
||||
isAiResourceApprovalPending,
|
||||
isAiResourcePublished,
|
||||
normalizeAiPublishStatus,
|
||||
} from '#/views/ai/shared/publish-status';
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
@@ -178,16 +184,31 @@ const actions: ActionButton[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
text: $t('button.delete'),
|
||||
icon: Delete,
|
||||
tone: 'danger',
|
||||
permission: '/api/v1/documentCollection/remove',
|
||||
icon: Promotion,
|
||||
text: (row) =>
|
||||
isAiResourcePublished(row.publishStatus)
|
||||
? $t('button.republish')
|
||||
: $t('button.submitPublishApproval'),
|
||||
permission: '/api/v1/documentCollection/save',
|
||||
placement: 'inline',
|
||||
onClick(row) {
|
||||
if (!ensureManageKnowledgeItem(row)) {
|
||||
return;
|
||||
}
|
||||
handleDelete(row);
|
||||
submitPublishApproval(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: $t('button.submitDeleteApproval'),
|
||||
icon: Delete,
|
||||
tone: 'danger',
|
||||
permission: '/api/v1/documentCollection/remove',
|
||||
placement: 'menu',
|
||||
onClick(row) {
|
||||
if (!ensureManageKnowledgeItem(row)) {
|
||||
return;
|
||||
}
|
||||
submitDeleteApproval(row);
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -195,24 +216,92 @@ const actions: ActionButton[] = [
|
||||
onMounted(() => {
|
||||
getCategoryList();
|
||||
});
|
||||
const handleDelete = (item: any) => {
|
||||
ElMessageBox.confirm($t('message.deleteAlert'), $t('message.noticeTitle'), {
|
||||
confirmButtonText: $t('message.ok'),
|
||||
cancelButtonText: $t('message.cancel'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
api
|
||||
.post('/api/v1/documentCollection/remove', { id: item.id })
|
||||
.then((res) => {
|
||||
if (res.errorCode === 0) {
|
||||
ElMessage.success($t('message.deleteOkMessage'));
|
||||
pageDataRef.value.setQuery({});
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
const submitPublishApproval = async (item: any) => {
|
||||
if (isAiResourceApprovalPending(item.publishStatus)) {
|
||||
ElMessage.warning($t('documentCollection.publishPendingHint'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
$t('documentCollection.submitPublishApprovalConfirm'),
|
||||
$t('message.noticeTitle'),
|
||||
{
|
||||
confirmButtonText: $t('button.confirm'),
|
||||
cancelButtonText: $t('button.cancel'),
|
||||
type: 'info',
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const res = await api.post(
|
||||
'/api/v1/documentCollection/submitPublishApproval',
|
||||
{
|
||||
id: item.id,
|
||||
},
|
||||
);
|
||||
if (res.errorCode === 0) {
|
||||
ElMessage.success(res.message || $t('message.saveOkMessage'));
|
||||
reloadKnowledgeList();
|
||||
}
|
||||
};
|
||||
const submitDeleteApproval = async (item: any) => {
|
||||
if (isAiResourceApprovalPending(item.publishStatus)) {
|
||||
ElMessage.warning($t('documentCollection.deletePendingHint'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
$t('documentCollection.submitDeleteApprovalConfirm'),
|
||||
$t('message.noticeTitle'),
|
||||
{
|
||||
confirmButtonText: $t('button.confirm'),
|
||||
cancelButtonText: $t('button.cancel'),
|
||||
type: 'warning',
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const res = await api.post(
|
||||
'/api/v1/documentCollection/submitDeleteApproval',
|
||||
{
|
||||
id: item.id,
|
||||
},
|
||||
);
|
||||
if (res.errorCode === 0) {
|
||||
ElMessage.success(res.message || $t('message.saveOkMessage'));
|
||||
reloadKnowledgeList();
|
||||
}
|
||||
};
|
||||
function resolvePublishStatusMeta(status?: string) {
|
||||
switch (normalizeAiPublishStatus(status)) {
|
||||
case 'DELETE_PENDING': {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusDeletePending'),
|
||||
tone: 'danger',
|
||||
};
|
||||
}
|
||||
case 'PUBLISH_PENDING': {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusPublishPending'),
|
||||
tone: 'pending',
|
||||
};
|
||||
}
|
||||
case 'PUBLISHED': {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusPublished'),
|
||||
tone: 'published',
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return {
|
||||
label: $t('documentCollection.publishStatusDraft'),
|
||||
tone: 'draft',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pageDataRef = ref();
|
||||
const aiKnowledgeModalRef = ref();
|
||||
@@ -460,21 +549,97 @@ function changeCategory(category: any) {
|
||||
:tag-map="collectionTypeLabelMap"
|
||||
>
|
||||
<template #corner="{ item }">
|
||||
<ElPopover
|
||||
v-if="canManageKnowledgeItem(item)"
|
||||
:ref="(el) => setVisibilityScopePopoverRef(item.id, el)"
|
||||
trigger="click"
|
||||
placement="bottom-end"
|
||||
:width="208"
|
||||
popper-class="knowledge-visibility-popover"
|
||||
>
|
||||
<template #reference>
|
||||
<button
|
||||
type="button"
|
||||
class="knowledge-scope-chip"
|
||||
<AiResourceCornerMeta>
|
||||
<template #publish>
|
||||
<div
|
||||
class="knowledge-publish-chip"
|
||||
:class="`knowledge-publish-chip--${resolvePublishStatusMeta(item.publishStatus).tone}`"
|
||||
>
|
||||
<span class="knowledge-publish-chip__dot"></span>
|
||||
<span>{{
|
||||
resolvePublishStatusMeta(item.publishStatus).label
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #scope>
|
||||
<ElPopover
|
||||
v-if="canManageKnowledgeItem(item)"
|
||||
:ref="(el) => setVisibilityScopePopoverRef(item.id, el)"
|
||||
trigger="click"
|
||||
placement="bottom-end"
|
||||
:width="208"
|
||||
popper-class="knowledge-visibility-popover"
|
||||
>
|
||||
<template #reference>
|
||||
<button
|
||||
type="button"
|
||||
class="knowledge-scope-chip"
|
||||
:class="`knowledge-scope-chip--${resolveVisibilityScopeMeta(item.visibilityScope).tone}`"
|
||||
:disabled="updatingScopeId === item.id"
|
||||
@click.stop
|
||||
>
|
||||
<ElIcon class="knowledge-scope-chip__icon">
|
||||
<component
|
||||
:is="
|
||||
resolveVisibilityScopeMeta(item.visibilityScope)
|
||||
.icon
|
||||
"
|
||||
/>
|
||||
</ElIcon>
|
||||
<span class="knowledge-scope-chip__label">
|
||||
{{
|
||||
resolveVisibilityScopeMeta(item.visibilityScope)
|
||||
.label
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<div class="knowledge-scope-panel" @click.stop>
|
||||
<button
|
||||
v-for="option in visibilityScopeOptions"
|
||||
:key="option.value"
|
||||
type="button"
|
||||
class="knowledge-scope-option"
|
||||
:class="[
|
||||
`knowledge-scope-option--${option.tone}`,
|
||||
{
|
||||
'knowledge-scope-option--active':
|
||||
item.visibilityScope === option.value,
|
||||
},
|
||||
]"
|
||||
:disabled="updatingScopeId === item.id"
|
||||
@click.stop="
|
||||
updateVisibilityScope(item, option.value)
|
||||
"
|
||||
>
|
||||
<span class="knowledge-scope-option__leading">
|
||||
<span class="knowledge-scope-option__icon-wrap">
|
||||
<ElIcon class="knowledge-scope-option__icon">
|
||||
<component :is="option.icon" />
|
||||
</ElIcon>
|
||||
</span>
|
||||
<span class="knowledge-scope-option__text">
|
||||
<span class="knowledge-scope-option__label">
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span class="knowledge-scope-option__desc">
|
||||
{{ option.description }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<ElIcon
|
||||
v-if="item.visibilityScope === option.value"
|
||||
class="knowledge-scope-option__check"
|
||||
>
|
||||
<Check />
|
||||
</ElIcon>
|
||||
</button>
|
||||
</div>
|
||||
</ElPopover>
|
||||
<div
|
||||
v-else
|
||||
class="knowledge-scope-chip knowledge-scope-chip--readonly"
|
||||
:class="`knowledge-scope-chip--${resolveVisibilityScopeMeta(item.visibilityScope).tone}`"
|
||||
:disabled="updatingScopeId === item.id"
|
||||
@click.stop
|
||||
>
|
||||
<ElIcon class="knowledge-scope-chip__icon">
|
||||
<component
|
||||
@@ -489,64 +654,9 @@ function changeCategory(category: any) {
|
||||
resolveVisibilityScopeMeta(item.visibilityScope).label
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="knowledge-scope-panel" @click.stop>
|
||||
<button
|
||||
v-for="option in visibilityScopeOptions"
|
||||
:key="option.value"
|
||||
type="button"
|
||||
class="knowledge-scope-option"
|
||||
:class="[
|
||||
`knowledge-scope-option--${option.tone}`,
|
||||
{
|
||||
'knowledge-scope-option--active':
|
||||
item.visibilityScope === option.value,
|
||||
},
|
||||
]"
|
||||
:disabled="updatingScopeId === item.id"
|
||||
@click.stop="updateVisibilityScope(item, option.value)"
|
||||
>
|
||||
<span class="knowledge-scope-option__leading">
|
||||
<span class="knowledge-scope-option__icon-wrap">
|
||||
<ElIcon class="knowledge-scope-option__icon">
|
||||
<component :is="option.icon" />
|
||||
</ElIcon>
|
||||
</span>
|
||||
<span class="knowledge-scope-option__text">
|
||||
<span class="knowledge-scope-option__label">
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span class="knowledge-scope-option__desc">
|
||||
{{ option.description }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<ElIcon
|
||||
v-if="item.visibilityScope === option.value"
|
||||
class="knowledge-scope-option__check"
|
||||
>
|
||||
<Check />
|
||||
</ElIcon>
|
||||
</button>
|
||||
</div>
|
||||
</ElPopover>
|
||||
<div
|
||||
v-else
|
||||
class="knowledge-scope-chip knowledge-scope-chip--readonly"
|
||||
:class="`knowledge-scope-chip--${resolveVisibilityScopeMeta(item.visibilityScope).tone}`"
|
||||
>
|
||||
<ElIcon class="knowledge-scope-chip__icon">
|
||||
<component
|
||||
:is="
|
||||
resolveVisibilityScopeMeta(item.visibilityScope).icon
|
||||
"
|
||||
/>
|
||||
</ElIcon>
|
||||
<span class="knowledge-scope-chip__label">
|
||||
{{ resolveVisibilityScopeMeta(item.visibilityScope).label }}
|
||||
</span>
|
||||
</div>
|
||||
</AiResourceCornerMeta>
|
||||
</template>
|
||||
</CardPage>
|
||||
</template>
|
||||
@@ -600,6 +710,54 @@ function changeCategory(category: any) {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.knowledge-publish-chip {
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 22px;
|
||||
min-width: 70px;
|
||||
padding: 0 9px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 999px;
|
||||
box-shadow: inset 0 1px 0 hsl(var(--card) / 46%);
|
||||
}
|
||||
|
||||
.knowledge-publish-chip__dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: currentColor;
|
||||
border-radius: 999px;
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.knowledge-publish-chip--draft {
|
||||
color: hsl(var(--muted-foreground));
|
||||
background: hsl(var(--muted) / 42%);
|
||||
border-color: hsl(var(--line-subtle));
|
||||
}
|
||||
|
||||
.knowledge-publish-chip--pending {
|
||||
color: hsl(var(--warning));
|
||||
background: hsl(var(--warning) / 12%);
|
||||
border-color: hsl(var(--warning) / 14%);
|
||||
}
|
||||
|
||||
.knowledge-publish-chip--published {
|
||||
color: hsl(var(--success));
|
||||
background: hsl(var(--success) / 12%);
|
||||
border-color: hsl(var(--success) / 14%);
|
||||
}
|
||||
|
||||
.knowledge-publish-chip--danger {
|
||||
color: hsl(var(--destructive));
|
||||
background: hsl(var(--destructive) / 10%);
|
||||
border-color: hsl(var(--destructive) / 14%);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 30px;
|
||||
color: #303133;
|
||||
@@ -608,16 +766,18 @@ h1 {
|
||||
|
||||
.knowledge-scope-chip {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
justify-content: center;
|
||||
min-height: 22px;
|
||||
min-width: 70px;
|
||||
padding: 0 9px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
color: hsl(var(--text-strong));
|
||||
background: hsl(var(--surface-subtle) / 92%);
|
||||
border: 1px solid hsl(var(--line-subtle));
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 999px;
|
||||
transition:
|
||||
border-color 0.18s ease,
|
||||
@@ -632,8 +792,8 @@ button.knowledge-scope-chip {
|
||||
}
|
||||
|
||||
button.knowledge-scope-chip:hover {
|
||||
box-shadow: 0 10px 22px -18px hsl(var(--foreground) / 32%);
|
||||
transform: translateY(-1px);
|
||||
background: hsl(var(--card) / 76%);
|
||||
box-shadow: 0 8px 16px -16px hsl(var(--foreground) / 28%);
|
||||
}
|
||||
|
||||
button.knowledge-scope-chip:focus-visible {
|
||||
@@ -646,7 +806,6 @@ button.knowledge-scope-chip:focus-visible {
|
||||
button.knowledge-scope-chip:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.72;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.knowledge-scope-chip--readonly {
|
||||
@@ -654,7 +813,7 @@ button.knowledge-scope-chip:disabled {
|
||||
}
|
||||
|
||||
.knowledge-scope-chip__icon {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.knowledge-scope-chip__label {
|
||||
@@ -663,20 +822,20 @@ button.knowledge-scope-chip:disabled {
|
||||
|
||||
.knowledge-scope-chip--private {
|
||||
color: hsl(var(--primary));
|
||||
background: hsl(var(--primary) / 9%);
|
||||
border-color: hsl(var(--primary) / 20%);
|
||||
background: hsl(var(--primary) / 10%);
|
||||
border-color: hsl(var(--primary) / 14%);
|
||||
}
|
||||
|
||||
.knowledge-scope-chip--dept {
|
||||
color: hsl(var(--warning));
|
||||
background: hsl(var(--warning) / 12%);
|
||||
border-color: hsl(var(--warning) / 20%);
|
||||
border-color: hsl(var(--warning) / 14%);
|
||||
}
|
||||
|
||||
.knowledge-scope-chip--public {
|
||||
color: hsl(var(--success));
|
||||
background: hsl(var(--success) / 12%);
|
||||
border-color: hsl(var(--success) / 20%);
|
||||
border-color: hsl(var(--success) / 14%);
|
||||
}
|
||||
|
||||
.knowledge-scope-panel {
|
||||
|
||||
Reference in New Issue
Block a user