feat(@vben/common-ui): 优化 AI 工具模块功能和路由
- 修改路由路径,统一为 /ai前缀 - 新增表单弹窗组件,用于输入项目信息 - 优化聊天消息展示逻辑,支持文档预览和下载 - 新增个人页面路由和组件 - 调整模板数据结构和样式
This commit is contained in:
parent
9cead8075b
commit
ad79e5113e
@ -40,6 +40,13 @@ export namespace ChatflowApi {
|
||||
conversationId: string;
|
||||
appId: string;
|
||||
}
|
||||
|
||||
export interface ChatMessageParams {
|
||||
userId: string;
|
||||
conversationId: string;
|
||||
firstId: string;
|
||||
limit: string;
|
||||
}
|
||||
}
|
||||
|
||||
// 聊天流
|
||||
@ -78,3 +85,10 @@ export function deleteChatflow(data: ChatflowApi.deleteParams) {
|
||||
export function getChatParameters(appId: string) {
|
||||
return requestClient.get(`/v1/chat/parameters/${appId}`);
|
||||
}
|
||||
|
||||
export function getChatflowMessage(
|
||||
appId: string,
|
||||
data: ChatflowApi.ChatMessageParams,
|
||||
) {
|
||||
return requestClient.post(`/v1/chat/messages/${appId}`, data);
|
||||
}
|
||||
|
@ -5,5 +5,6 @@ export * from './log';
|
||||
export * from './menu';
|
||||
export * from './role';
|
||||
export * from './server';
|
||||
export * from './spider';
|
||||
export * from './user';
|
||||
export * from './workflow';
|
||||
|
18
apps/web-antd/src/api/core/spider.ts
Normal file
18
apps/web-antd/src/api/core/spider.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
// 全国公共资源爬虫
|
||||
export function runSpider(data: any) {
|
||||
return requestClient.post(`/spider/run`, { data });
|
||||
}
|
||||
|
||||
export function getSpiderStatus() {
|
||||
return requestClient.get(`/spider/status`);
|
||||
}
|
||||
|
||||
export function getSpiderLogs() {
|
||||
return requestClient.get(`/v1/workflow/list`);
|
||||
}
|
||||
|
||||
export function stopSpider() {
|
||||
return requestClient.get(`/spider/stop`);
|
||||
}
|
@ -15,7 +15,7 @@ const routes: RouteRecordRaw[] = [
|
||||
children: [
|
||||
{
|
||||
name: 'spider',
|
||||
path: '/aiflow/spider',
|
||||
path: '/ai/spider',
|
||||
component: () => import('#/views/spider/index.vue'),
|
||||
meta: {
|
||||
icon: SvgSpider,
|
||||
@ -26,7 +26,7 @@ const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
{
|
||||
name: 'word',
|
||||
path: '/aiflow/word',
|
||||
path: '/ai/word',
|
||||
component: () => import('#/views/word/index.vue'),
|
||||
meta: {
|
||||
icon: SvgWord,
|
||||
@ -37,7 +37,7 @@ const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
{
|
||||
name: 'ppt',
|
||||
path: '/aiflow/ppt',
|
||||
path: '/ai/ppt',
|
||||
component: () => import('#/views/ppt/index.vue'),
|
||||
meta: {
|
||||
icon: SvgPPT,
|
||||
|
@ -11,19 +11,19 @@ const items: WorkflowItem[] = [
|
||||
icon: SvgSpider,
|
||||
title: '数据抓取工具',
|
||||
description: '自动抓取',
|
||||
path: '/spider',
|
||||
path: '/ai/spider',
|
||||
},
|
||||
{
|
||||
icon: SvgWord,
|
||||
title: 'Word文档生成工具',
|
||||
description: '自动生成word文档',
|
||||
path: '/word',
|
||||
path: '/ai/word',
|
||||
},
|
||||
{
|
||||
icon: SvgPPT,
|
||||
title: 'PPT生成工具',
|
||||
description: '自动生成PPT文档',
|
||||
path: '/ppt',
|
||||
path: '/ai/ppt',
|
||||
},
|
||||
];
|
||||
const router = useRouter();
|
||||
|
@ -5,7 +5,7 @@ import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { PptListView, PptWorkView } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import { notification } from 'ant-design-vue';
|
||||
|
||||
import { getWorkflowInfo, getWorkflowList, sendWorkflow } from '#/api';
|
||||
|
||||
@ -58,7 +58,11 @@ async function handleClick(item: PPTTempItem) {
|
||||
}
|
||||
|
||||
async function handleClickMode(item: PPTTempItem) {
|
||||
message.success(`已选取${item.name}为模板`);
|
||||
notification.success({
|
||||
message: `${item.name}`,
|
||||
description: '已选取',
|
||||
duration: 3,
|
||||
});
|
||||
temp = item;
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@ import { SpiderListView, SpiderWorkView } from '@vben/common-ui';
|
||||
|
||||
import { notification } from 'ant-design-vue';
|
||||
|
||||
import { getWorkflowInfo, getWorkflowList, sendWorkflow } from '#/api';
|
||||
|
||||
import { getWorkflowInfo, getWorkflowList, runSpider } from '#/api';
|
||||
// sendWorkflow
|
||||
interface ResultItem {
|
||||
key: number;
|
||||
role: 'ai' | 'user';
|
||||
@ -82,7 +82,7 @@ async function handleClickMode(item: PPTTempItem) {
|
||||
<SpiderWorkView
|
||||
title="目标网址:"
|
||||
:item="temp"
|
||||
:run-spider="sendWorkflow"
|
||||
:run-spider="runSpider"
|
||||
:item-message="itemMessage"
|
||||
/>
|
||||
</div>
|
||||
|
@ -9,11 +9,19 @@ import { message, notification } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
deleteChatflow,
|
||||
getChatflowMessage,
|
||||
getChatList,
|
||||
getChatParameters,
|
||||
sendChatflow,
|
||||
} from '#/api';
|
||||
|
||||
interface ResultItem {
|
||||
key: number;
|
||||
role: 'ai' | 'user';
|
||||
content: string;
|
||||
footer?: any;
|
||||
}
|
||||
|
||||
let temp = reactive<WordTempItem>({
|
||||
id: 'baca08c1-e92b-4dc9-a445-3584803f54d4',
|
||||
name: '海南职创申报书生成',
|
||||
@ -54,11 +62,12 @@ function handleClickMode(item: WordTempItem) {
|
||||
temp = item;
|
||||
getParameters(temp.id);
|
||||
}
|
||||
const itemMessage = ref<ResultItem[]>([]);
|
||||
|
||||
async function deleteLog(item: any) {
|
||||
const res = await deleteChatflow({
|
||||
appId: temp.id,
|
||||
id: item,
|
||||
conversationId: item,
|
||||
userId: '1562',
|
||||
});
|
||||
if (res.code === 0) {
|
||||
@ -74,9 +83,33 @@ async function getParameters(id: string) {
|
||||
params.value = res.suggestedQuestions;
|
||||
}
|
||||
|
||||
function handleClick(item: WordTempItem) {
|
||||
message.error('暂不支持查看历史');
|
||||
temp = item;
|
||||
async function handleClick(item: string) {
|
||||
const res = await getChatflowMessage(temp.id, {
|
||||
userId: '1562',
|
||||
firstId: '',
|
||||
conversationId: item,
|
||||
limit: '',
|
||||
});
|
||||
itemMessage.value = [];
|
||||
if (res.data.length > 0) {
|
||||
res.data.forEach((msg) => {
|
||||
if (msg.inputs) {
|
||||
itemMessage.value.push({
|
||||
key: itemMessage.value.length + 1,
|
||||
role: 'user', // 'user' or 'ai'
|
||||
content: msg.inputs.projectName,
|
||||
footer: msg.footer,
|
||||
});
|
||||
}
|
||||
if (msg.answer) {
|
||||
itemMessage.value.push({
|
||||
key: itemMessage.value.length + 1,
|
||||
role: 'ai',
|
||||
content: msg.answer,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@ -101,6 +134,7 @@ onMounted(() => {
|
||||
:item="temp"
|
||||
:params-data="params"
|
||||
:run-chatflow="sendChatflow"
|
||||
:item-message="itemMessage"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -101,7 +101,7 @@ const openKeys = ref([]);
|
||||
v-model:selected-keys="selectedKeys"
|
||||
mode="vertical"
|
||||
class="mode"
|
||||
:items="items"
|
||||
:items="itemsData"
|
||||
@click="handleMenuClick"
|
||||
/>
|
||||
<!-- 🌟 添加会话 -->
|
||||
|
@ -37,7 +37,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
||||
const data = drawerApi.getData<Record<string, any>>();
|
||||
if (data) {
|
||||
isLoading.value = true; // 开始加载文档,开启加载状态
|
||||
pptx.value = `/pptx/${data}`; // 更新 docx 的值
|
||||
pptx.value = `/pptx/${data}`; // 更新 pptx 的值
|
||||
}
|
||||
// url.value = drawerApi.getData<Record<string, any>>();
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ import {
|
||||
PaperClipOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { Badge, Button, Flex, Typography } from 'ant-design-vue';
|
||||
import { Attachments, Bubble, Sender } from 'ant-design-x-vue';
|
||||
import { Badge, Button, Flex, Space, Typography } from 'ant-design-vue';
|
||||
import { Attachments, Bubble, Sender, Welcome } from 'ant-design-x-vue';
|
||||
|
||||
import WordPreview from '../word/word-preview.vue';
|
||||
|
||||
@ -342,7 +342,7 @@ watch(
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
size: 'nomarl',
|
||||
size: 'normal',
|
||||
type: 'primary',
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
@ -373,8 +373,22 @@ watch(
|
||||
<div class="layout">
|
||||
<PreviewDrawer />
|
||||
<div class="chat">
|
||||
<Space
|
||||
v-if="resultItems.length === 0"
|
||||
direction="vertical"
|
||||
size:16
|
||||
style="flex: 1; padding-top: 20px"
|
||||
>
|
||||
<Welcome
|
||||
variant="borderless"
|
||||
icon="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp"
|
||||
title="欢迎使用PPT自动生成"
|
||||
description="请选择模板列表中需要生成文件的模板,输入参数后开始生成生成文件。"
|
||||
/>
|
||||
</Space>
|
||||
<!-- 🌟 消息列表 -->
|
||||
<Bubble.List
|
||||
v-else
|
||||
variant="shadow"
|
||||
:typing="true"
|
||||
:items="resultItems"
|
||||
|
@ -37,12 +37,14 @@ const conversationsItems = ref(defaultConversationsItems);
|
||||
const activeKey = ref(defaultConversationsItems.value[0]?.key);
|
||||
const itemsData = ref([
|
||||
{
|
||||
key: 'a2a55334-a111-45e6-942f-9f3f70af8826',
|
||||
key: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
|
||||
label: '全国公共资源交易平台_信息爬取',
|
||||
title: '全国公共资源交易平台_信息爬取',
|
||||
},
|
||||
{
|
||||
key: 'c736edd0-925d-4877-9223-56aab7342311',
|
||||
label: '广州公共资源交易中心_信息获取',
|
||||
title: '全国公共资源交易平台_信息爬取',
|
||||
},
|
||||
]);
|
||||
|
||||
@ -71,24 +73,6 @@ const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <Card style="height: calc(100vh - 120px); overflow-y: auto">-->
|
||||
<!-- <CardHeader class="py-4">-->
|
||||
<!-- <CardTitle class="text-lg">{{ title }}</CardTitle>-->
|
||||
<!-- <CardDescription>请选择需要爬取的网站</CardDescription>-->
|
||||
<!-- </CardHeader>-->
|
||||
<!-- <CardContent class="flex flex-wrap p-5 pt-0">-->
|
||||
<!-- <ul class="divide-border w-full divide-y" role="list">-->
|
||||
<!-- <li-->
|
||||
<!-- v-for="item in items"-->
|
||||
<!-- :key="item.id"-->
|
||||
<!-- @click="$emit('click', item)"-->
|
||||
<!-- class="flex cursor-pointer justify-between gap-x-6 py-5"-->
|
||||
<!-- >-->
|
||||
<!-- {{ item.name }}-->
|
||||
<!-- </li>-->
|
||||
<!-- </ul>-->
|
||||
<!-- </CardContent>-->
|
||||
<!-- </Card>-->
|
||||
<div class="menu">
|
||||
<div class="addBtn">数据信息列表</div>
|
||||
<Menu
|
||||
@ -96,12 +80,12 @@ const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
|
||||
v-model:selected-keys="selectedKeys"
|
||||
mode="vertical"
|
||||
class="mode"
|
||||
:items="items"
|
||||
:items="itemsData"
|
||||
@click="handleMenuClick"
|
||||
/>
|
||||
<!-- 🌟 添加会话 -->
|
||||
<!-- <Button type="link" class="addBtn">会话</Button>-->
|
||||
<div class="addBtn">会话</div>
|
||||
<div class="addBtn">获取记录</div>
|
||||
<!-- 🌟 添加会话 -->
|
||||
|
||||
<!-- 🌟 会话管理 -->
|
||||
|
@ -9,7 +9,6 @@ import { h, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben-core/popup-ui';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
@ -18,8 +17,15 @@ import {
|
||||
} from '@vben-core/shadcn-ui';
|
||||
|
||||
import { UserOutlined } from '@ant-design/icons-vue';
|
||||
import { Flex, message, RangePicker } from 'ant-design-vue';
|
||||
import { Attachments, Bubble } from 'ant-design-x-vue';
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
message,
|
||||
notification,
|
||||
RangePicker,
|
||||
Space,
|
||||
} from 'ant-design-vue';
|
||||
import { Attachments, Bubble, Welcome } from 'ant-design-x-vue';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
import SpiderPreview from './spider-preview.vue';
|
||||
@ -78,40 +84,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
}),
|
||||
});
|
||||
|
||||
// const styles = computed(() => {
|
||||
// return {
|
||||
// 'placeholder': {
|
||||
// 'padding-top': '32px',
|
||||
// 'text-align': 'left',
|
||||
// 'flex': 1,
|
||||
// },
|
||||
// } as const
|
||||
// })
|
||||
|
||||
// const placeholderNode = computed(() => h(
|
||||
// Space,
|
||||
// { direction: "vertical", size: 16, style: styles.value.placeholder },
|
||||
// [
|
||||
// h(
|
||||
// Welcome,
|
||||
// {
|
||||
// variant: "borderless",
|
||||
// icon: "https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp",
|
||||
// title: "Hello, I'm Ant Design X",
|
||||
// description: "Base on Ant Design, AGI product interface solution, create a better intelligent vision~",
|
||||
// extra: h(Space, {}, [h(Button, { icon: h(ShareAltOutlined) }), h(Button, { icon: h(EllipsisOutlined) })]),
|
||||
// }
|
||||
// ),
|
||||
// ]
|
||||
// ))
|
||||
|
||||
const resultItems = ref<ResultItem[]>([]);
|
||||
// const items = computed<BubbleListProps['items']>(() => {
|
||||
// if (resultItems.value.length === 0) {
|
||||
// return [{ content: placeholderNode, variant: 'borderless' }]
|
||||
// }
|
||||
// return resultItems
|
||||
// })
|
||||
|
||||
const [PreviewDrawer, previewDrawerApi] = useVbenDrawer({
|
||||
// 连接抽离的组件
|
||||
@ -162,24 +135,36 @@ const startFetching = async () => {
|
||||
}
|
||||
|
||||
try {
|
||||
notification.info({
|
||||
message: '正在获取中...',
|
||||
duration: 3,
|
||||
});
|
||||
const res = await props.runSpider(
|
||||
{
|
||||
appid: props.item.id,
|
||||
},
|
||||
{
|
||||
userId: '1562',
|
||||
conversationId: '',
|
||||
files: [],
|
||||
inputs: {
|
||||
publish_start_time,
|
||||
publish_end_time,
|
||||
},
|
||||
publish_start_time,
|
||||
publish_end_time,
|
||||
llm_api_key: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
|
||||
},
|
||||
// {
|
||||
// appid: props.item.id,
|
||||
// },
|
||||
// {
|
||||
// userId: '1562',
|
||||
// conversationId: '',
|
||||
// files: [],
|
||||
// inputs: {
|
||||
// publish_start_time,
|
||||
// publish_end_time,
|
||||
// },
|
||||
// },
|
||||
);
|
||||
if (res.data.outputs.result) {
|
||||
// 保存抓取结果
|
||||
fetchResult.value = res.data.outputs.result;
|
||||
message.success('抓取成功');
|
||||
notification.success({
|
||||
message: '获取成功',
|
||||
duration: 3,
|
||||
});
|
||||
const { result } = res.data.outputs;
|
||||
|
||||
const filename = ref('');
|
||||
@ -209,7 +194,7 @@ const startFetching = async () => {
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
size: 'nomarl',
|
||||
size: 'normal',
|
||||
type: 'primary',
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
@ -217,7 +202,7 @@ const startFetching = async () => {
|
||||
onClick: () => {
|
||||
// 创建隐藏的 <a> 标签用于触发下载
|
||||
const link = document.createElement('a');
|
||||
link.href = result; // 设置下载链接
|
||||
link.href = msg.content; // 设置下载链接
|
||||
link.download = filename; // 设置下载文件名
|
||||
document.body.append(link); // 将 <a> 标签添加到页面中
|
||||
link.click(); // 触发点击事件开始下载
|
||||
@ -234,31 +219,13 @@ const startFetching = async () => {
|
||||
message.error('抓取无结果');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
message.error(`${error}`);
|
||||
}
|
||||
|
||||
// 模拟抓取完成
|
||||
isFetching.value = false;
|
||||
};
|
||||
|
||||
// 下载文件逻辑
|
||||
const downloadFile = () => {
|
||||
if (!fetchResult.value || !fetchResult.value.startsWith('http')) {
|
||||
message.error('无效的文件链接');
|
||||
return;
|
||||
}
|
||||
|
||||
const fileUrl = fetchResult.value;
|
||||
const fileName = decodeURIComponent(
|
||||
fileUrl.slice(Math.max(0, fileUrl.lastIndexOf('/') + 1)),
|
||||
);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = fileUrl;
|
||||
link.download = fileName;
|
||||
link.click();
|
||||
};
|
||||
|
||||
// 列表角色
|
||||
const roles: BubbleListProps['roles'] = {
|
||||
user: {
|
||||
@ -333,7 +300,7 @@ watch(
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
size: 'nomarl',
|
||||
size: 'normal',
|
||||
type: 'primary',
|
||||
onClick: () => {
|
||||
openPreviewDrawer('right', filename);
|
||||
@ -344,7 +311,7 @@ watch(
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
size: 'nomarl',
|
||||
size: 'normal',
|
||||
type: 'primary',
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
@ -373,10 +340,27 @@ watch(
|
||||
|
||||
<template>
|
||||
<!-- style="flex-direction: column"-->
|
||||
<div class="flex h-full" style="width: 70%">
|
||||
<div
|
||||
class="flex h-full"
|
||||
style="flex-direction: column; width: 70%; padding-top: 20px"
|
||||
>
|
||||
<PreviewDrawer />
|
||||
<!-- <PreviewDrawer />-->
|
||||
<Space
|
||||
v-if="resultItems.length === 0"
|
||||
direction="vertical"
|
||||
size:16
|
||||
style="flex: 1"
|
||||
>
|
||||
<Welcome
|
||||
variant="borderless"
|
||||
icon="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp"
|
||||
title="欢迎使用AI平台信息获取"
|
||||
description="请选择数据信息列表中需要获取信息的链接,选取时间后开始爬取获取信息。"
|
||||
/>
|
||||
</Space>
|
||||
<Bubble.List
|
||||
v-else
|
||||
variant="shadow"
|
||||
:typing="true"
|
||||
:items="resultItems"
|
||||
@ -403,13 +387,13 @@ watch(
|
||||
>
|
||||
{{ isFetching ? '抓取中...' : '开始抓取' }}
|
||||
</Button>
|
||||
<Button
|
||||
class="mx-2"
|
||||
:disabled="fetchStatus !== 'completed'"
|
||||
@click="downloadFile"
|
||||
>
|
||||
下载文件
|
||||
</Button>
|
||||
<!-- <Button-->
|
||||
<!-- class="mx-2"-->
|
||||
<!-- :disabled="fetchStatus !== 'completed'"-->
|
||||
<!-- @click="downloadFile"-->
|
||||
<!-- >-->
|
||||
<!-- 下载文件-->
|
||||
<!-- </Button>-->
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
|
@ -96,7 +96,7 @@ const openKeys = ref([]);
|
||||
v-model:selected-keys="selectedKeys"
|
||||
mode="vertical"
|
||||
class="mode"
|
||||
:items="items"
|
||||
:items="itemsData"
|
||||
@click="handleMenuClick"
|
||||
/>
|
||||
<!-- 🌟 添加会话 -->
|
||||
|
@ -11,7 +11,8 @@ import type { WordTempItem } from './typing';
|
||||
|
||||
import { computed, h, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben-core/popup-ui';
|
||||
import { useVbenForm } from '@vben-core/form-ui';
|
||||
import { useVbenDrawer, useVbenModal } from '@vben-core/popup-ui';
|
||||
|
||||
import {
|
||||
CloudUploadOutlined,
|
||||
@ -19,7 +20,14 @@ import {
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
// import type { VNode } from 'vue';
|
||||
import { Badge, Button, Flex, message, Typography } from 'ant-design-vue';
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Flex,
|
||||
message,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'ant-design-vue';
|
||||
import {
|
||||
Attachments,
|
||||
Bubble,
|
||||
@ -27,15 +35,21 @@ import {
|
||||
Sender,
|
||||
useXAgent,
|
||||
useXChat,
|
||||
Welcome,
|
||||
} from 'ant-design-x-vue';
|
||||
import markdownit from 'markdown-it';
|
||||
|
||||
import WordPreview from './word-preview.vue';
|
||||
|
||||
interface ResultItem {
|
||||
key: number;
|
||||
role: 'ai' | 'user';
|
||||
content: string;
|
||||
footer?: any;
|
||||
}
|
||||
interface WorkflowParams {
|
||||
appid: string;
|
||||
}
|
||||
|
||||
interface WorkflowContext {
|
||||
userId: string;
|
||||
conversationId: string;
|
||||
@ -45,7 +59,6 @@ interface WorkflowContext {
|
||||
};
|
||||
files: [];
|
||||
}
|
||||
|
||||
interface WorkflowResult {
|
||||
conversationId: string;
|
||||
answer: {};
|
||||
@ -55,8 +68,8 @@ interface WorkflowResult {
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface Props {
|
||||
itemMessage?: ResultItem;
|
||||
item?: WordTempItem;
|
||||
paramsData?: {};
|
||||
runChatflow?: (
|
||||
@ -69,6 +82,7 @@ defineOptions({ name: 'PlaygroundIndependentSetup' });
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
item: () => null,
|
||||
itemMessage: () => null,
|
||||
paramsData: () => null,
|
||||
runChatflow: () => async () => ({
|
||||
data: {
|
||||
@ -81,13 +95,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
// const { token } = theme.useToken();
|
||||
|
||||
interface ResultItem {
|
||||
key: number;
|
||||
role: 'ai' | 'user';
|
||||
content: string;
|
||||
footer?: any;
|
||||
}
|
||||
|
||||
// 初始化 markdown 解析器
|
||||
const md = markdownit({ html: true, breaks: true });
|
||||
|
||||
@ -231,11 +238,20 @@ watch(
|
||||
);
|
||||
|
||||
// ==================== Event ====================
|
||||
// function onSubmit(nextContent: string) {
|
||||
// if (!nextContent) return;
|
||||
// onRequest(nextContent);
|
||||
// content.value = '';
|
||||
// }
|
||||
|
||||
// 判断是否是以 .docx 结尾的 URL
|
||||
function isDocxURL(str: string): boolean {
|
||||
return str.endsWith('.docx');
|
||||
}
|
||||
|
||||
// 使用正则提取 /static/ 后面的文件名部分
|
||||
function extractDocxFilename(url: string): null | string {
|
||||
const match = url.match(/\/static\/([a-f0-9-]+\.docx)$/i);
|
||||
if (match) {
|
||||
return match[1]; // 返回类似:9e1c421e-991c-411f-8176-6350a97e70f3.docx
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const startFetching = async () => {
|
||||
// 项目名称为空时阻止发送并提示
|
||||
@ -264,6 +280,9 @@ const startFetching = async () => {
|
||||
files: [],
|
||||
inputs: {
|
||||
projectName: projectName.value,
|
||||
projectContext: '无',
|
||||
keyAvoidTechOrKeyword: '无',
|
||||
userInitialInnovationPoint: '无',
|
||||
},
|
||||
content: content.value || '',
|
||||
},
|
||||
@ -272,20 +291,6 @@ const startFetching = async () => {
|
||||
const { answer } = res;
|
||||
conversationId.value = res.conversationId;
|
||||
|
||||
// 判断是否是以 .docx 结尾的 URL
|
||||
function isDocxURL(str: string): boolean {
|
||||
return str.endsWith('.docx');
|
||||
}
|
||||
|
||||
// 使用正则提取 /static/ 后面的文件名部分
|
||||
function extractDocxFilename(url: string): null | string {
|
||||
const match = url.match(/\/static\/([a-f0-9-]+\.docx)$/i);
|
||||
if (match) {
|
||||
return match[1]; // 返回类似:9e1c421e-991c-411f-8176-6350a97e70f3.docx
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const filename = ref('');
|
||||
|
||||
if (answer && isDocxURL(answer)) {
|
||||
@ -332,7 +337,7 @@ const startFetching = async () => {
|
||||
resultItems.value.push({
|
||||
key: resultItems.value.length + 1,
|
||||
role: 'ai',
|
||||
content: res.answer.trim(),
|
||||
content: res.answer,
|
||||
});
|
||||
}
|
||||
|
||||
@ -346,6 +351,79 @@ const startFetching = async () => {
|
||||
fetchStatus.value = 'completed';
|
||||
};
|
||||
|
||||
function openFormModal() {
|
||||
formModalApi
|
||||
.setData({
|
||||
// 表单值
|
||||
values: {
|
||||
projectName: 'abc',
|
||||
projectContext: '123',
|
||||
keyAvoidTechOrKeyWord: 'abc',
|
||||
userInitialInnovationPoint: 'abc',
|
||||
},
|
||||
})
|
||||
.open();
|
||||
}
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
handleSubmit: startFetching,
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'projectName',
|
||||
label: '项目名称',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'projectContext',
|
||||
label: '项目背景',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'keyAvoidTechOrKeyWord',
|
||||
label: '规避关键词',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'userInitialInnovationPoint',
|
||||
label: '初步创新点',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, formModalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
onCancel() {
|
||||
formModalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
await formApi.validateAndSubmitForm();
|
||||
// modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
if (isOpen) {
|
||||
const { values } = formModalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
formApi.setValues(values);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: '内嵌表单示例',
|
||||
});
|
||||
|
||||
const onPromptsItemClick: PromptsProps['onItemClick'] = (info) => {
|
||||
content.value = info.data.description;
|
||||
};
|
||||
@ -353,67 +431,110 @@ const onPromptsItemClick: PromptsProps['onItemClick'] = (info) => {
|
||||
const handleFileChange: AttachmentsProps['onChange'] = (info) =>
|
||||
(attachedFiles.value = info.fileList);
|
||||
|
||||
// ==================== Nodes ====================
|
||||
// const placeholderNode = computed(() =>
|
||||
// h(
|
||||
// Space,
|
||||
// { direction: 'vertical', size: 16, style: value.placeholder },
|
||||
// [
|
||||
// h(Welcome, {
|
||||
// variant: 'borderless',
|
||||
// icon: 'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp',
|
||||
// title: "Hello, I'm Ant Design X",
|
||||
// description:
|
||||
// 'Base on Ant Design, AGI product interface solution, create a better intelligent vision~',
|
||||
// extra: h(Space, {}, [
|
||||
// h(Button, { icon: h(ShareAltOutlined) }),
|
||||
// h(Button, { icon: h(EllipsisOutlined) }),
|
||||
// ]),
|
||||
// }),
|
||||
// h(Prompts, {
|
||||
// title: 'Do you want?',
|
||||
// items: placeholderPromptsItems,
|
||||
// styles: {
|
||||
// list: {
|
||||
// width: '100%',
|
||||
// },
|
||||
// item: {
|
||||
// flex: 1,
|
||||
// },
|
||||
// },
|
||||
// onItemClick: onPromptsItemClick,
|
||||
// }),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// 监听 itemMessage 变化并更新 resultItems
|
||||
watch(
|
||||
() => props.itemMessage,
|
||||
(newVal) => {
|
||||
resultItems.value = [];
|
||||
if (newVal && newVal.length > 0) {
|
||||
newVal.forEach((msg) => {
|
||||
if (msg.role === 'user') {
|
||||
resultItems.value.push({
|
||||
key: resultItems.value.length + 1,
|
||||
role: msg.role, // 'user' or 'ai'
|
||||
content: msg.content,
|
||||
footer: msg.footer,
|
||||
});
|
||||
} else {
|
||||
const filename = ref('');
|
||||
|
||||
// const items = computed<BubbleListProps['items']>(() => {
|
||||
// // if (messages.value.length === 0) {
|
||||
// // return [{ content: placeholderNode, variant: 'borderless' }];
|
||||
// // }
|
||||
// return messages.value.map(({ id, message, status }) => ({
|
||||
// key: id,
|
||||
// loading: status === 'loading',
|
||||
// role: status === 'local' ? 'local' : 'ai',
|
||||
// content: message,
|
||||
// }));
|
||||
// });
|
||||
if (msg.content && isDocxURL(msg.content)) {
|
||||
filename.value = extractDocxFilename(msg.content);
|
||||
resultItems.value.push({
|
||||
key: resultItems.value.length + 1,
|
||||
role: msg.role, // 'user' or 'ai'
|
||||
content: '文档已生成',
|
||||
footer: h(Flex, null, [
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
size: 'normal',
|
||||
type: 'primary',
|
||||
onClick: () => {
|
||||
openPreviewDrawer('right', filename);
|
||||
},
|
||||
},
|
||||
'文档预览',
|
||||
),
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
size: 'normal',
|
||||
type: 'primary',
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
},
|
||||
onClick: () => {
|
||||
// 创建隐藏的 <a> 标签用于触发下载
|
||||
const link = document.createElement('a');
|
||||
link.href = msg.content; // 设置下载链接
|
||||
link.download = filename; // 设置下载文件名
|
||||
document.body.append(link); // 将 <a> 标签添加到页面中
|
||||
link.click(); // 触发点击事件开始下载
|
||||
link.remove(); // 下载完成后移除 <a> 标签
|
||||
},
|
||||
},
|
||||
'文档下载',
|
||||
),
|
||||
]),
|
||||
});
|
||||
} else {
|
||||
resultItems.value.push({
|
||||
key: resultItems.value.length + 1,
|
||||
role: msg.role, // 'user' or 'ai'
|
||||
content: msg.content,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<PreviewDrawer />
|
||||
<Modal>
|
||||
<Form />
|
||||
</Modal>
|
||||
<div class="chat">
|
||||
<a-space class="w-full" direction="vertical" style="padding-top: 20px">
|
||||
<a-input
|
||||
v-model:value="projectName"
|
||||
required
|
||||
placeholder="项目名称"
|
||||
:status="inputStatus"
|
||||
<Space
|
||||
v-if="resultItems.length === 0"
|
||||
direction="vertical"
|
||||
size:16
|
||||
style="flex: 1; padding-top: 20px"
|
||||
>
|
||||
<Welcome
|
||||
variant="borderless"
|
||||
icon="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp"
|
||||
title="欢迎使用AI申报书WORD生成"
|
||||
description="请选择模板列表中需要生成文件的模板,输入参数后开始生成生成内容或文件。"
|
||||
/>
|
||||
</a-space>
|
||||
</Space>
|
||||
|
||||
<!-- <a-space class="w-full" direction="vertical" style="padding-top: 20px">-->
|
||||
<!-- <a-input-->
|
||||
<!-- v-model:value="projectName"-->
|
||||
<!-- required-->
|
||||
<!-- placeholder="项目名称"-->
|
||||
<!-- :status="inputStatus"-->
|
||||
<!-- />-->
|
||||
<!-- </a-space>-->
|
||||
<!-- 🌟 消息列表 -->
|
||||
<Bubble.List
|
||||
v-else
|
||||
:items="resultItems"
|
||||
:roles="roles"
|
||||
style="flex: 1"
|
||||
@ -432,7 +553,7 @@ const handleFileChange: AttachmentsProps['onChange'] = (info) =>
|
||||
:value="content"
|
||||
class="sender"
|
||||
:loading="agentRequestLoading"
|
||||
@submit="startFetching"
|
||||
@submit="openFormModal"
|
||||
@change="(value) => (content = value)"
|
||||
>
|
||||
<template #prefix>
|
||||
|
Loading…
Reference in New Issue
Block a user