feat: 收敛AI资源发布审批生命周期

- 统一工作流、知识库、聊天助手的发布、重新发布、下线与删除链路

- 收敛审批编排、生命周期状态机与展示态,补齐审批管理和快照预览

- 调整审批管理权限模型为单入口页面加内部按钮权限
This commit is contained in:
2026-04-09 17:13:54 +08:00
parent 81125ce55c
commit 4e565aef99
68 changed files with 3859 additions and 817 deletions

View File

@@ -26,7 +26,11 @@ import {
} from 'element-plus';
import { tryit } from 'radash';
import { submitBotDeleteApproval, submitBotPublishApproval } from '#/api';
import {
submitBotDeleteApproval,
submitBotOfflineApproval,
submitBotPublishApproval,
} from '#/api';
import { api } from '#/api/request';
import defaultAvatar from '#/assets/ai/bot/defaultBotAvatar.png';
import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
@@ -34,9 +38,12 @@ import CardList from '#/components/page/CardList.vue';
import PageData from '#/components/page/PageData.vue';
import PageSide from '#/components/page/PageSide.vue';
import {
canAiResourceDelete,
canAiResourceOffline,
canAiResourcePublish,
canAiResourceRepublish,
isAiResourceApprovalPending,
isAiResourcePublished,
normalizeAiPublishStatus,
resolveAiResourceDisplayStatus,
} from '#/views/ai/shared/publish-status';
import { useDictStore } from '#/store';
@@ -105,35 +112,57 @@ const actions: ActionButton[] = [
{
icon: Promotion,
text: (row: BotInfo) =>
isAiResourcePublished(row.publishStatus)
canAiResourceRepublish(row.displayPublishStatus, row.publishStatus)
? $t('button.republish')
: $t('button.submitPublishApproval'),
: $t('button.publish'),
permission: '/api/v1/bot/save',
placement: 'inline',
visible: (row: BotInfo) =>
canAiResourcePublish(row.displayPublishStatus, row.publishStatus) ||
canAiResourceRepublish(row.displayPublishStatus, row.publishStatus),
onClick(row: BotInfo) {
handleSubmitPublishApproval(row);
handlePublishAction(row);
},
},
{
icon: Promotion,
text: $t('button.offline'),
permission: '/api/v1/bot/save',
placement: 'menu',
visible: (row: BotInfo) => canAiResourceOffline(row.displayPublishStatus, row.publishStatus),
onClick(row: BotInfo) {
handleOfflineAction(row);
},
},
{
icon: Delete,
text: $t('button.submitDeleteApproval'),
text: $t('button.delete'),
tone: 'danger',
permission: '/api/v1/bot/remove',
placement: 'menu',
visible: (row: BotInfo) => canAiResourceDelete(row.displayPublishStatus, row.publishStatus),
onClick(row: BotInfo) {
handleSubmitDeleteApproval(row);
},
},
];
const handleSubmitPublishApproval = async (bot: BotInfo) => {
if (isAiResourceApprovalPending(bot.publishStatus)) {
function isRepublishAction(bot: BotInfo) {
return canAiResourceRepublish(bot.displayPublishStatus, bot.publishStatus);
}
const handlePublishAction = async (bot: BotInfo) => {
if (
isAiResourceApprovalPending(bot.displayPublishStatus, bot.publishStatus)
) {
ElMessage.warning($t('bot.publishPendingHint'));
return;
}
try {
await ElMessageBox.confirm(
$t('bot.submitPublishApprovalConfirm'),
isRepublishAction(bot)
? $t('bot.submitRepublishApprovalConfirm')
: $t('bot.submitPublishApprovalConfirm'),
$t('message.noticeTitle'),
{
confirmButtonText: $t('button.confirm'),
@@ -150,8 +179,36 @@ const handleSubmitPublishApproval = async (bot: BotInfo) => {
pageDataRef.value?.reload?.();
}
};
const handleOfflineAction = async (bot: BotInfo) => {
if (
isAiResourceApprovalPending(bot.displayPublishStatus, bot.publishStatus)
) {
ElMessage.warning($t('bot.publishPendingHint'));
return;
}
try {
await ElMessageBox.confirm(
$t('bot.submitOfflineApprovalConfirm'),
$t('message.noticeTitle'),
{
confirmButtonText: $t('button.confirm'),
cancelButtonText: $t('button.cancel'),
type: 'warning',
},
);
} catch {
return;
}
const res = await submitBotOfflineApproval(String(bot.id));
if (res.errorCode === 0) {
ElMessage.success(res.message || $t('message.saveOkMessage'));
pageDataRef.value?.reload?.();
}
};
const handleSubmitDeleteApproval = async (bot: BotInfo) => {
if (isAiResourceApprovalPending(bot.publishStatus)) {
if (
isAiResourceApprovalPending(bot.displayPublishStatus, bot.publishStatus)
) {
ElMessage.warning($t('bot.deletePendingHint'));
return;
}
@@ -174,8 +231,11 @@ const handleSubmitDeleteApproval = async (bot: BotInfo) => {
pageDataRef.value?.reload?.();
}
};
function resolvePublishStatusMeta(status?: string) {
switch (normalizeAiPublishStatus(status)) {
function resolvePublishStatusMetaByInstance(
displayPublishStatus?: string,
publishStatus?: string,
) {
switch (resolveAiResourceDisplayStatus(displayPublishStatus, publishStatus)) {
case 'PUBLISHED':
return {
label: $t('bot.publishStatusPublished'),
@@ -186,6 +246,16 @@ function resolvePublishStatusMeta(status?: string) {
label: $t('bot.publishStatusPublishPending'),
type: 'warning' as const,
};
case 'OFFLINE_PENDING':
return {
label: $t('bot.publishStatusOfflinePending'),
type: 'warning' as const,
};
case 'OFFLINE':
return {
label: $t('bot.publishStatusOffline'),
type: 'info' as const,
};
case 'DELETE_PENDING':
return {
label: $t('bot.publishStatusDeletePending'),
@@ -378,9 +448,9 @@ const getSideList = async () => {
size="small"
effect="plain"
round
:type="resolvePublishStatusMeta(item.publishStatus).type"
:type="resolvePublishStatusMetaByInstance(item.displayPublishStatus, item.publishStatus).type"
>
{{ resolvePublishStatusMeta(item.publishStatus).label }}
{{ resolvePublishStatusMetaByInstance(item.displayPublishStatus, item.publishStatus).label }}
</ElTag>
</template>
</CardList>