feat: ai code

This commit is contained in:
xingyu4j
2025-10-22 14:52:42 +08:00
parent 7aacec3e69
commit 66647802af
26 changed files with 148 additions and 181 deletions

View File

@@ -83,7 +83,7 @@ const routes: RouteRecordRaw[] = [
}, },
}, },
{ {
path: 'console/workflow/create', path: String.raw`workflow/create/:id(\d+)/:type(update|create)`,
component: () => import('#/views/ai/workflow/form/index.vue'), component: () => import('#/views/ai/workflow/form/index.vue'),
name: 'AiWorkflowCreate', name: 'AiWorkflowCreate',
meta: { meta: {

View File

@@ -202,7 +202,8 @@ async function updateConversationTitle(
if ( if (
filterConversationList.length > 0 && filterConversationList.length > 0 &&
filterConversationList[0] && // tip避免切换对话 filterConversationList[0] && // tip避免切换对话
activeConversationId.value === filterConversationList[0].id activeConversationId.value ===
(filterConversationList[0].id as number)
) { ) {
emits('onConversationClick', filterConversationList[0]); emits('onConversationClick', filterConversationList[0]);
} }
@@ -306,7 +307,7 @@ onMounted(async () => {
<template> <template>
<Layout.Sider <Layout.Sider
width="280px" width="280px"
class="conversation-container relative flex h-full flex-col justify-between overflow-hidden p-4" class="relative flex h-full flex-col justify-between overflow-hidden p-4"
> >
<Drawer /> <Drawer />
<!-- 左顶部对话 --> <!-- 左顶部对话 -->
@@ -329,7 +330,7 @@ onMounted(async () => {
</Input> </Input>
<!-- 左中间对话列表 --> <!-- 左中间对话列表 -->
<div class="conversation-list mt-2 flex-1 overflow-auto"> <div class="mt-2 flex-1 overflow-auto">
<!-- 情况一加载中 --> <!-- 情况一加载中 -->
<Empty v-if="loading" description="." v-loading="loading" /> <Empty v-if="loading" description="." v-loading="loading" />
@@ -337,11 +338,10 @@ onMounted(async () => {
<div <div
v-for="conversationKey in Object.keys(conversationMap)" v-for="conversationKey in Object.keys(conversationMap)"
:key="conversationKey" :key="conversationKey"
class=""
> >
<div <div
v-if="conversationMap[conversationKey].length > 0" v-if="conversationMap[conversationKey].length > 0"
class="conversation-item classify-title pt-2" class="classify-title pt-2"
> >
<b class="mx-1"> <b class="mx-1">
{{ conversationKey }} {{ conversationKey }}
@@ -354,24 +354,24 @@ onMounted(async () => {
@click="handleConversationClick(conversation.id)" @click="handleConversationClick(conversation.id)"
@mouseover="hoverConversationId = conversation.id" @mouseover="hoverConversationId = conversation.id"
@mouseout="hoverConversationId = null" @mouseout="hoverConversationId = null"
class="conversation-item mt-1" class="mt-1"
> >
<div <div
class="conversation flex cursor-pointer flex-row items-center justify-between rounded-lg px-2 leading-10" class="flex cursor-pointer flex-row items-center justify-between rounded-lg px-2 leading-10"
:class="[ :class="[
conversation.id === activeConversationId conversation.id === activeConversationId
? 'bg-primary-200' ? 'bg-success-600'
: '', : '',
]" ]"
> >
<div class="title-wrapper flex items-center"> <div class="flex items-center">
<Avatar <Avatar
v-if="conversation.roleAvatar" v-if="conversation.roleAvatar"
:src="conversation.roleAvatar" :src="conversation.roleAvatar"
/> />
<SvgGptIcon v-else class="size-8" /> <SvgGptIcon v-else class="size-8" />
<span <span
class="max-w-36 overflow-hidden text-ellipsis whitespace-nowrap p-2 text-sm font-normal text-gray-600" class="max-w-32 overflow-hidden text-ellipsis whitespace-nowrap p-2 text-sm font-normal"
> >
{{ conversation.title }} {{ conversation.title }}
</span> </span>
@@ -379,7 +379,7 @@ onMounted(async () => {
<div <div
v-show="hoverConversationId === conversation.id" v-show="hoverConversationId === conversation.id"
class="button-wrapper relative right-0.5 flex items-center text-gray-400" class="relative right-0.5 flex items-center text-gray-400"
> >
<Button <Button
class="mr-0 px-1" class="mr-0 px-1"

View File

@@ -428,7 +428,7 @@ async function textRoll() {
// 设置状态 // 设置状态
textRoleRunning.value = true; textRoleRunning.value = true;
receiveMessageDisplayedText.value = ''; receiveMessageDisplayedText.value = '';
const task = async () => { async function task() {
// 调整速度 // 调整速度
const diff = const diff =
(receiveMessageFullText.value.length - (receiveMessageFullText.value.length -
@@ -472,7 +472,7 @@ async function textRoll() {
clearTimeout(timer); clearTimeout(timer);
} }
} }
}; }
let timer = setTimeout(task, textSpeed.value); let timer = setTimeout(task, textSpeed.value);
} catch {} } catch {}
} }
@@ -503,7 +503,7 @@ onMounted(async () => {
<!-- 左侧对话列表 --> <!-- 左侧对话列表 -->
<ConversationList <ConversationList
class="!bg-card" class="!bg-card"
:active-id="activeConversationId as any" :active-id="activeConversationId"
ref="conversationListRef" ref="conversationListRef"
@on-conversation-create="handleConversationCreateSuccess" @on-conversation-create="handleConversationCreateSuccess"
@on-conversation-click="handleConversationClick" @on-conversation-click="handleConversationClick"
@@ -578,7 +578,7 @@ onMounted(async () => {
class="border-border my-5 mb-5 mt-2 flex flex-col rounded-xl border px-2 py-2.5" class="border-border my-5 mb-5 mt-2 flex flex-col rounded-xl border px-2 py-2.5"
> >
<textarea <textarea
class="box-border h-24 resize-none overflow-auto border-none px-0 py-1 focus:outline-none" class="box-border h-24 resize-none overflow-auto rounded-md px-0 py-1 focus:outline-none"
v-model="prompt" v-model="prompt"
@keydown="handleSendByKeydown" @keydown="handleSendByKeydown"
@input="handlePromptInput" @input="handlePromptInput"

View File

@@ -84,9 +84,9 @@ onMounted(async () => {
<TableAction :actions="[]" /> <TableAction :actions="[]" />
</template> </template>
<template #userId="{ row }"> <template #userId="{ row }">
<span>{{ <span>
userList.find((item) => item.id === row.userId)?.nickname {{ userList.find((item) => item.id === row.userId)?.nickname }}
}}</span> </span>
</template> </template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction

View File

@@ -44,7 +44,7 @@ watch(
<template> <template>
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="mt-2 text-gray-600"> <div class="mt-2">
<Image class="rounded-lg" :src="detail?.picUrl" /> <Image class="rounded-lg" :src="detail?.picUrl" />
</div> </div>
</div> </div>
@@ -52,7 +52,7 @@ watch(
<!-- 时间 --> <!-- 时间 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">时间</div> <div class="text-lg font-bold">时间</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
<div> <div>
提交时间{{ formatDate(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }} 提交时间{{ formatDate(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
</div> </div>
@@ -65,7 +65,7 @@ watch(
<!-- 模型 --> <!-- 模型 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">模型</div> <div class="text-lg font-bold">模型</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ detail.model }}({{ detail.height }}x{{ detail.width }}) {{ detail.model }}({{ detail.height }}x{{ detail.width }})
</div> </div>
</div> </div>
@@ -73,7 +73,7 @@ watch(
<!-- 提示词 --> <!-- 提示词 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">提示词</div> <div class="text-lg font-bold">提示词</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ detail.prompt }} {{ detail.prompt }}
</div> </div>
</div> </div>
@@ -81,7 +81,7 @@ watch(
<!-- 图片地址 --> <!-- 图片地址 -->
<div class="mb-5 w-full overflow-hidden break-words"> <div class="mb-5 w-full overflow-hidden break-words">
<div class="text-lg font-bold">图片地址</div> <div class="text-lg font-bold">图片地址</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ detail.picUrl }} {{ detail.picUrl }}
</div> </div>
</div> </div>
@@ -95,7 +95,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">采样方法</div> <div class="text-lg font-bold">采样方法</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
StableDiffusionSamplers.find( StableDiffusionSamplers.find(
(item) => item.key === detail?.options?.sampler, (item) => item.key === detail?.options?.sampler,
@@ -112,7 +112,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">CLIP</div> <div class="text-lg font-bold">CLIP</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
StableDiffusionClipGuidancePresets.find( StableDiffusionClipGuidancePresets.find(
(item) => item.key === detail?.options?.clipGuidancePreset, (item) => item.key === detail?.options?.clipGuidancePreset,
@@ -129,7 +129,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">风格</div> <div class="text-lg font-bold">风格</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
StableDiffusionStylePresets.find( StableDiffusionStylePresets.find(
(item) => item.key === detail?.options?.stylePreset, (item) => item.key === detail?.options?.stylePreset,
@@ -146,7 +146,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">迭代步数</div> <div class="text-lg font-bold">迭代步数</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.steps }}</div> <div class="mt-2">{{ detail?.options?.steps }}</div>
</div> </div>
<div <div
@@ -157,7 +157,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">引导系数</div> <div class="text-lg font-bold">引导系数</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.scale }}</div> <div class="mt-2">{{ detail?.options?.scale }}</div>
</div> </div>
<div <div
@@ -168,7 +168,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">随机因子</div> <div class="text-lg font-bold">随机因子</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.seed }}</div> <div class="mt-2">{{ detail?.options?.seed }}</div>
</div> </div>
<!-- Dall3 专属 --> <!-- Dall3 专属 -->
@@ -177,7 +177,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">风格选择</div> <div class="text-lg font-bold">风格选择</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
{{ {{
Dall3StyleList.find((item) => item.key === detail?.options?.style)?.name Dall3StyleList.find((item) => item.key === detail?.options?.style)?.name
}} }}
@@ -192,7 +192,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">模型版本</div> <div class="text-lg font-bold">模型版本</div>
<div class="mt-2 text-gray-600">{{ detail?.options?.version }}</div> <div class="mt-2">{{ detail?.options?.version }}</div>
</div> </div>
<div <div
@@ -203,7 +203,7 @@ watch(
class="mb-5 w-full overflow-hidden break-words" class="mb-5 w-full overflow-hidden break-words"
> >
<div class="text-lg font-bold">参考图</div> <div class="text-lg font-bold">参考图</div>
<div class="mt-2 text-gray-600"> <div class="mt-2">
<Image :src="detail.options.referImageUrl" /> <Image :src="detail.options.referImageUrl" />
</div> </div>
</div> </div>

View File

@@ -47,15 +47,15 @@ const platformOptions = [
const models = ref<AiModelModelApi.Model[]>([]); // 模型列表 const models = ref<AiModelModelApi.Model[]>([]); // 模型列表
/** 绘画 start */ /** 绘画 start */
const handleDrawStart = async () => {}; async function handleDrawStart() {}
/** 绘画 complete */ /** 绘画 complete */
const handleDrawComplete = async () => { async function handleDrawComplete() {
await imageListRef.value.getImageList(); await imageListRef.value.getImageList();
}; }
/** 重新生成:将画图详情填充到对应平台 */ /** 重新生成:将画图详情填充到对应平台 */
const handleRegeneration = async (image: AiImageApi.Image) => { async function handleRegeneration(image: AiImageApi.Image) {
// 切换平台 // 切换平台
selectPlatform.value = image.platform; selectPlatform.value = image.platform;
// 根据不同平台填充 image // 根据不同平台填充 image
@@ -79,7 +79,7 @@ const handleRegeneration = async (image: AiImageApi.Image) => {
// No default // No default
} }
// TODO @fan貌似 other 重新设置不行? // TODO @fan貌似 other 重新设置不行?
}; }
/** 组件挂载的时候 */ /** 组件挂载的时候 */
onMounted(async () => { onMounted(async () => {

View File

@@ -1,5 +1,6 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@@ -7,6 +8,13 @@ import { getDictOptions } from '@vben/hooks';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -69,15 +77,20 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
fixed: 'left', fixed: 'left',
}, },
{ {
field: 'picUrl',
title: '图片', title: '图片',
minWidth: 110, minWidth: 110,
fixed: 'left', fixed: 'left',
slots: { default: 'picUrl' }, cellRender: {
name: 'CellImage',
},
}, },
{ {
minWidth: 180, field: 'userId',
title: '用户', title: '用户',
slots: { default: 'userId' }, minWidth: 180,
formatter: ({ cellValue }) =>
userList.find((user) => user.id === cellValue)?.nickname || '-',
}, },
{ {
field: 'platform', field: 'platform',

View File

@@ -1,23 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiImageApi } from '#/api/ai/image'; import type { AiImageApi } from '#/api/ai/image';
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue';
import { confirm, DocAlert, Page } from '@vben/common-ui'; import { confirm, DocAlert, Page } from '@vben/common-ui';
import { AiImageStatusEnum } from '@vben/constants'; import { AiImageStatusEnum } from '@vben/constants';
import { Image, message, Switch } from 'ant-design-vue'; import { message, Switch } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteImage, getImagePage, updateImage } from '#/api/ai/image'; import { deleteImage, getImagePage, updateImage } from '#/api/ai/image';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
/** 刷新表格 */ /** 刷新表格 */
function handleRefresh() { function handleRefresh() {
gridApi.query(); gridApi.query();
@@ -40,7 +35,7 @@ async function handleDelete(row: AiImageApi.Image) {
} }
} }
/** 修改是否发布 */ /** 修改是否发布 */
const handleUpdatePublicStatusChange = async (row: AiImageApi.Image) => { async function handleUpdatePublicStatusChange(row: AiImageApi.Image) {
try { try {
// 修改状态的二次确认 // 修改状态的二次确认
const text = row.publicStatus ? '公开' : '私有'; const text = row.publicStatus ? '公开' : '私有';
@@ -54,7 +49,7 @@ const handleUpdatePublicStatusChange = async (row: AiImageApi.Image) => {
} catch { } catch {
row.publicStatus = !row.publicStatus; row.publicStatus = !row.publicStatus;
} }
}; }
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
@@ -83,10 +78,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiImageApi.Image>, } as VxeTableGridOptions<AiImageApi.Image>,
}); });
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -95,20 +86,6 @@ onMounted(async () => {
<DocAlert title="AI 绘图创作" url="https://doc.iocoder.cn/ai/image/" /> <DocAlert title="AI 绘图创作" url="https://doc.iocoder.cn/ai/image/" />
</template> </template>
<Grid table-title="绘画管理列表"> <Grid table-title="绘画管理列表">
<template #toolbar-tools>
<TableAction :actions="[]" />
</template>
<template #picUrl="{ row }">
<Image :src="row.picUrl" class="h-20 w-20" />
</template>
<template #userId="{ row }">
<span>
{{
userList.find((item: SystemUserApi.User) => item.id === row.userId)
?.nickname
}}
</span>
</template>
<template #publicStatus="{ row }"> <template #publicStatus="{ row }">
<Switch <Switch
v-model:checked="row.publicStatus" v-model:checked="row.publicStatus"

View File

@@ -33,10 +33,10 @@ async function getList() {
} }
const debounceGetList = useDebounceFn(getList, 80); const debounceGetList = useDebounceFn(getList, 80);
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { function handleQuery() {
queryParams.pageNo = 1; queryParams.pageNo = 1;
getList(); getList();
}; }
/** 初始化 */ /** 初始化 */
onMounted(async () => { onMounted(async () => {

View File

@@ -65,16 +65,16 @@ async function handleDelete(row: AiKnowledgeDocumentApi.KnowledgeDocument) {
} }
} }
/** 跳转到知识库分段页面 */ /** 跳转到知识库分段页面 */
const handleSegment = (id: number) => { function handleSegment(id: number) {
router.push({ router.push({
name: 'AiKnowledgeSegment', name: 'AiKnowledgeSegment',
query: { documentId: id }, query: { documentId: id },
}); });
}; }
/** 修改是否发布 */ /** 修改是否发布 */
const handleStatusChange = async ( async function handleStatusChange(
row: AiKnowledgeDocumentApi.KnowledgeDocument, row: AiKnowledgeDocumentApi.KnowledgeDocument,
) => { ) {
try { try {
// 修改状态的二次确认 // 修改状态的二次确认
const text = row.status ? '启用' : '禁用'; const text = row.status ? '启用' : '禁用';
@@ -91,7 +91,7 @@ const handleStatusChange = async (
? CommonStatusEnum.DISABLE ? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE; : CommonStatusEnum.ENABLE;
} }
}; }
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),

View File

@@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiKnowledgeKnowledgeApi } from '#/api/ai/knowledge/knowledge';
import type { AiKnowledgeSegmentApi } from '#/api/ai/knowledge/segment'; import type { AiKnowledgeSegmentApi } from '#/api/ai/knowledge/segment';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
@@ -41,12 +40,12 @@ function handleCreate() {
} }
/** 编辑 */ /** 编辑 */
function handleEdit(row: AiKnowledgeKnowledgeApi.Knowledge) { function handleEdit(row: AiKnowledgeSegmentApi.KnowledgeSegment) {
formModalApi.setData(row).open(); formModalApi.setData(row).open();
} }
/** 删除 */ /** 删除 */
async function handleDelete(row: AiKnowledgeKnowledgeApi.Knowledge) { async function handleDelete(row: AiKnowledgeSegmentApi.KnowledgeSegment) {
const hideLoading = message.loading({ const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.id]), content: $t('ui.actionMessage.deleting', [row.id]),
duration: 0, duration: 0,
@@ -88,7 +87,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
refresh: true, refresh: true,
search: true, search: true,
}, },
} as VxeTableGridOptions<AiKnowledgeKnowledgeApi.Knowledge>, } as VxeTableGridOptions<AiKnowledgeSegmentApi.KnowledgeSegment>,
}); });
/** 修改是否发布 */ /** 修改是否发布 */

View File

@@ -27,7 +27,7 @@ defineExpose({
<h3 class="text-primary h-7 w-full text-center text-xl leading-7"> <h3 class="text-primary h-7 w-full text-center text-xl leading-7">
思维导图创作中心 思维导图创作中心
</h3> </h3>
<div class="flex-grow overflow-y-auto"> <div class="mt-4 flex-grow overflow-y-auto">
<div> <div>
<b>您的需求</b> <b>您的需求</b>
<Textarea <Textarea

View File

@@ -73,7 +73,7 @@ watch(props, ({ generatedContent, isGenerating, isEnd, isStart }) => {
}); });
/** 更新思维导图的展示 */ /** 更新思维导图的展示 */
const update = () => { function update() {
try { try {
const { root } = transformer.transform( const { root } = transformer.transform(
processContent(props.generatedContent), processContent(props.generatedContent),
@@ -83,7 +83,7 @@ const update = () => {
} catch (error: any) { } catch (error: any) {
console.error(error); console.error(error);
} }
}; }
/** 处理内容 */ /** 处理内容 */
function processContent(text: string) { function processContent(text: string) {
const arr: string[] = []; const arr: string[] = [];

View File

@@ -1,9 +1,17 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -44,9 +52,11 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
fixed: 'left', fixed: 'left',
}, },
{ {
minWidth: 180, field: 'userId',
title: '用户', title: '用户',
slots: { default: 'userId' }, minWidth: 180,
formatter: ({ cellValue }) =>
userList.find((user) => user.id === cellValue)?.nickname || '-',
}, },
{ {
field: 'prompt', field: 'prompt',

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiMindmapApi } from '#/api/ai/mindmap'; import type { AiMindmapApi } from '#/api/ai/mindmap';
import type { SystemUserApi } from '#/api/system/user';
import { nextTick, onMounted, ref } from 'vue'; import { nextTick, ref } from 'vue';
import { DocAlert, Page, useVbenDrawer } from '@vben/common-ui'; import { DocAlert, Page, useVbenDrawer } from '@vben/common-ui';
@@ -11,14 +10,11 @@ import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteMindMap, getMindMapPage } from '#/api/ai/mindmap'; import { deleteMindMap, getMindMapPage } from '#/api/ai/mindmap';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import Right from '../index/modules/Right.vue'; import Right from '../index/modules/Right.vue';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
const previewVisible = ref(false); // drawer 的显示隐藏
const previewContent = ref(''); const previewContent = ref('');
const [Drawer, drawerApi] = useVbenDrawer({ const [Drawer, drawerApi] = useVbenDrawer({
header: false, header: false,
@@ -75,16 +71,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
} as VxeTableGridOptions<AiMindmapApi.MindMap>, } as VxeTableGridOptions<AiMindmapApi.MindMap>,
}); });
async function openPreview(row: AiMindmapApi.MindMap) { async function openPreview(row: AiMindmapApi.MindMap) {
previewVisible.value = false; previewContent.value = row.generatedContent;
drawerApi.open(); drawerApi.open();
await nextTick(); await nextTick();
previewVisible.value = true;
previewContent.value = row.generatedContent;
} }
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -94,7 +84,6 @@ onMounted(async () => {
</template> </template>
<Drawer class="w-3/5"> <Drawer class="w-3/5">
<Right <Right
v-if="previewVisible"
:generated-content="previewContent" :generated-content="previewContent"
:is-end="true" :is-end="true"
:is-generating="false" :is-generating="false"
@@ -102,17 +91,6 @@ onMounted(async () => {
/> />
</Drawer> </Drawer>
<Grid table-title="思维导图管理列表"> <Grid table-title="思维导图管理列表">
<template #toolbar-tools>
<TableAction :actions="[]" />
</template>
<template #userId="{ row }">
<span>
{{
userList.find((item: SystemUserApi.User) => item.id === row.userId)
?.nickname
}}
</span>
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[

View File

@@ -209,8 +209,15 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
title: '角色头像', title: '角色头像',
slots: { default: 'avatar' }, field: 'avatar',
minWidth: 140, minWidth: 140,
cellRender: {
name: 'CellImage',
props: {
width: 40,
height: 40,
},
},
}, },
{ {
title: '角色类别', title: '角色类别',
@@ -229,13 +236,23 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
title: '知识库', title: '知识库',
slots: { default: 'knowledgeIds' }, field: 'knowledgeIds',
minWidth: 100, minWidth: 100,
formatter: ({ cellValue }) => {
return !cellValue || cellValue.length === 0
? '-'
: `引用${cellValue.length}`;
},
}, },
{ {
title: '工具', title: '工具',
slots: { default: 'toolIds' }, field: 'toolIds',
minWidth: 100, minWidth: 100,
formatter: ({ cellValue }) => {
return !cellValue || cellValue.length === 0
? '-'
: `引用${cellValue.length}`;
},
}, },
{ {
field: 'publicStatus', field: 'publicStatus',

View File

@@ -4,7 +4,7 @@ import type { AiModelChatRoleApi } from '#/api/ai/model/chatRole';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { Image, message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteChatRole, getChatRolePage } from '#/api/ai/model/chatRole'; import { deleteChatRole, getChatRolePage } from '#/api/ai/model/chatRole';
@@ -100,17 +100,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
]" ]"
/> />
</template> </template>
<template #knowledgeIds="{ row }">
<span v-if="!row.knowledgeIds || row.knowledgeIds.length === 0">-</span>
<span v-else>引用 {{ row.knowledgeIds.length }} </span>
</template>
<template #toolIds="{ row }">
<span v-if="!row.toolIds || row.toolIds.length === 0">-</span>
<span v-else>引用 {{ row.toolIds.length }} </span>
</template>
<template #avatar="{ row }">
<Image :src="row.avatar" class="h-8 w-8" />
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[

View File

@@ -1,11 +1,20 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiModelApiKeyApi } from '#/api/ai/model/apiKey';
import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants'; import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { getApiKeySimpleList } from '#/api/ai/model/apiKey'; import { getApiKeySimpleList } from '#/api/ai/model/apiKey';
let apiKeyList: AiModelApiKeyApi.ApiKey[] = [];
async function getApiKeyList() {
apiKeyList = await getApiKeySimpleList();
}
getApiKeyList();
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
return [ return [
@@ -199,7 +208,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
}, },
{ {
title: 'API 秘钥', title: 'API 秘钥',
slots: { default: 'keyId' }, field: 'keyId',
formatter: ({ cellValue }) => {
return (
apiKeyList.find((apiKey) => apiKey.id === cellValue)?.name || '-'
);
},
minWidth: 140, minWidth: 140,
}, },
{ {

View File

@@ -1,23 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiModelApiKeyApi } from '#/api/ai/model/apiKey';
import type { AiModelModelApi } from '#/api/ai/model/model'; import type { AiModelModelApi } from '#/api/ai/model/model';
import { onMounted, ref } from 'vue';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getApiKeySimpleList } from '#/api/ai/model/apiKey';
import { deleteModel, getModelPage } from '#/api/ai/model/model'; import { deleteModel, getModelPage } from '#/api/ai/model/model';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue'; import Form from './modules/form.vue';
const apiKeyList = ref([] as AiModelApiKeyApi.ApiKey[]);
const [FormModal, formModalApi] = useVbenModal({ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form, connectedComponent: Form,
destroyOnClose: true, destroyOnClose: true,
@@ -83,10 +78,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiModelModelApi.Model>, } as VxeTableGridOptions<AiModelModelApi.Model>,
}); });
onMounted(async () => {
// 获得下拉数据
apiKeyList.value = await getApiKeySimpleList();
});
</script> </script>
<template> <template>
@@ -109,15 +100,6 @@ onMounted(async () => {
]" ]"
/> />
</template> </template>
<template #keyId="{ row }">
<span>
{{
apiKeyList.find(
(item: AiModelApiKeyApi.ApiKey) => item.id === row.keyId,
)?.name
}}
</span>
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[

View File

@@ -1,5 +1,6 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@@ -7,6 +8,13 @@ import { getDictOptions } from '@vben/hooks';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -82,7 +90,10 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
minWidth: 180, minWidth: 180,
title: '用户', title: '用户',
slots: { default: 'userId' }, field: 'userId',
formatter: ({ cellValue }) => {
return userList.find((user) => user.id === cellValue)?.nickname || '-';
},
}, },
{ {
field: 'status', field: 'status',

View File

@@ -1,9 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiMusicApi } from '#/api/ai/music'; import type { AiMusicApi } from '#/api/ai/music';
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue';
import { confirm, DocAlert, Page } from '@vben/common-ui'; import { confirm, DocAlert, Page } from '@vben/common-ui';
import { AiMusicStatusEnum } from '@vben/constants'; import { AiMusicStatusEnum } from '@vben/constants';
@@ -12,12 +9,10 @@ import { Button, message, Switch } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteMusic, getMusicPage, updateMusic } from '#/api/ai/music'; import { deleteMusic, getMusicPage, updateMusic } from '#/api/ai/music';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
/** 刷新表格 */ /** 刷新表格 */
function handleRefresh() { function handleRefresh() {
gridApi.query(); gridApi.query();
@@ -83,10 +78,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiMusicApi.Music>, } as VxeTableGridOptions<AiMusicApi.Music>,
}); });
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -99,11 +90,6 @@ onMounted(async () => {
<TableAction :actions="[]" /> <TableAction :actions="[]" />
</template> </template>
<template #userId="{ row }">
<span>
{{ userList.find((item) => item.id === row.userId)?.nickname }}
</span>
</template>
<template #content="{ row }"> <template #content="{ row }">
<Button <Button
type="link" type="link"

View File

@@ -190,8 +190,8 @@ function handleBack() {
/** 初始化 */ /** 初始化 */
onMounted(async () => { onMounted(async () => {
workflowId.value = route.query.id as string; workflowId.value = route.params.id as string;
actionType.value = route.query.type as string; actionType.value = route.params.type as string;
await initData(); await initData();
}); });

View File

@@ -245,7 +245,7 @@ defineExpose({ validate });
</fieldset> </fieldset>
<fieldset <fieldset
class="m-0 mt-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-4" class="bg-card m-0 mt-10 rounded-lg border border-gray-200 px-3 py-4"
> >
<legend class="ml-2 px-2.5 text-base font-semibold text-gray-600"> <legend class="ml-2 px-2.5 text-base font-semibold text-gray-600">
<h3>运行结果</h3> <h3>运行结果</h3>

View File

@@ -28,10 +28,7 @@ function handleCreate() {
function handleEdit(row: any) { function handleEdit(row: any) {
router.push({ router.push({
name: 'AiWorkflowCreate', name: 'AiWorkflowCreate',
query: { params: { id: row.id, type: 'update' },
id: row.id,
type: 'update',
},
}); });
} }

View File

@@ -1,5 +1,6 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemUserApi } from '#/api/system/user';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@@ -7,6 +8,13 @@ import { getDictOptions } from '@vben/hooks';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils'; import { getRangePickerDefaultProps } from '#/utils';
let userList: SystemUserApi.User[] = [];
async function getUserData() {
userList = await getSimpleUserList();
}
getUserData();
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
@@ -62,7 +70,10 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
minWidth: 180, minWidth: 180,
title: '用户', title: '用户',
slots: { default: 'userId' }, field: 'userId',
formatter: ({ cellValue }) => {
return userList.find((user) => user.id === cellValue)?.nickname || '-';
},
}, },
{ {
field: 'type', field: 'type',

View File

@@ -1,9 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { AiWriteApi } from '#/api/ai/write'; import type { AiWriteApi } from '#/api/ai/write';
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue';
import { DocAlert, Page } from '@vben/common-ui'; import { DocAlert, Page } from '@vben/common-ui';
@@ -11,12 +8,10 @@ import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteWrite, getWritePage } from '#/api/ai/write'; import { deleteWrite, getWritePage } from '#/api/ai/write';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
const userList = ref<SystemUserApi.User[]>([]); // 用户列表
/** 刷新表格 */ /** 刷新表格 */
function handleRefresh() { function handleRefresh() {
gridApi.query(); gridApi.query();
@@ -66,10 +61,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<AiWriteApi.AiWritePageReq>, } as VxeTableGridOptions<AiWriteApi.AiWritePageReq>,
}); });
onMounted(async () => {
// 获得下拉数据
userList.value = await getSimpleUserList();
});
</script> </script>
<template> <template>
@@ -78,14 +69,6 @@ onMounted(async () => {
<DocAlert title="AI 写作助手" url="https://doc.iocoder.cn/ai/write/" /> <DocAlert title="AI 写作助手" url="https://doc.iocoder.cn/ai/write/" />
</template> </template>
<Grid table-title="写作管理列表"> <Grid table-title="写作管理列表">
<template #toolbar-tools>
<TableAction :actions="[]" />
</template>
<template #userId="{ row }">
<span>{{
userList.find((item) => item.id === row.userId)?.nickname
}}</span>
</template>
<template #actions="{ row }"> <template #actions="{ row }">
<TableAction <TableAction
:actions="[ :actions="[