feat(@vben/web-antd): 实现模板动态加载

This commit is contained in:
Kven 2025-06-10 22:37:59 +08:00
parent ce60c7194d
commit 7d762aada2
17 changed files with 251 additions and 204 deletions

View File

@ -58,8 +58,8 @@ export async function sendChatflow(
} }
// word // word
export async function sendWord(data: ChatflowApi.CompletionsBody) { export async function sendWord(appId: any, data: ChatflowApi.CompletionsBody) {
return requestClient.post(`word/completions`, data); return requestClient.post(`word/completions/${appId}`, data);
} }
export async function getChatList( export async function getChatList(

View File

@ -1,5 +1,3 @@
import type { SpiderItem } from '@vben/common-ui';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
export namespace RepositoryApi { export namespace RepositoryApi {
@ -10,7 +8,13 @@ export namespace RepositoryApi {
} }
export async function getAppList(params: RepositoryApi.AppListParams) { export async function getAppList(params: RepositoryApi.AppListParams) {
return requestClient.get<SpiderItem[]>(`/v1/server/apps`, { params }); return requestClient.get(`/v1/server/apps`, { params });
}
export async function getAppListByType(appType: any) {
return requestClient.get(`/v1/server/apps/type`, {
params: { appType },
});
} }
// export const getAppDetail = (id) => { // export const getAppDetail = (id) => {

View File

@ -36,8 +36,8 @@ export function sendWorkflow(
return requestClient.post(`/v1/workflow/run/${params.appid}`, data); return requestClient.post(`/v1/workflow/run/${params.appid}`, data);
} }
export function sendPpt(data: WorkflowApi.WorkflowRunBody) { export function sendPpt(appId: any, data: WorkflowApi.WorkflowRunBody) {
return requestClient.post(`/ppt/run`, data); return requestClient.post(`/ppt/run/${appId}`, data);
} }
export function getWorkflowInfo(data: WorkflowApi.WorkflowLogParams) { export function getWorkflowInfo(data: WorkflowApi.WorkflowLogParams) {

View File

@ -3,7 +3,7 @@ import type { ConversationsProps } from 'ant-design-x-vue';
import type { Props } from '../typing'; import type { Props } from '../typing';
import { computed, ref } from 'vue'; import { computed, ref, watch } from 'vue';
import { Menu } from 'ant-design-vue'; import { Menu } from 'ant-design-vue';
import { Conversations } from 'ant-design-x-vue'; import { Conversations } from 'ant-design-x-vue';
@ -14,6 +14,7 @@ defineOptions({
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
items: () => [], items: () => [],
temp: () => [],
}); });
const emit = defineEmits(['click', 'clickMode']); const emit = defineEmits(['click', 'clickMode']);
const defaultConversationsItems = computed(() => { const defaultConversationsItems = computed(() => {
@ -27,13 +28,16 @@ const defaultConversationsItems = computed(() => {
const conversationsItems = ref(defaultConversationsItems); const conversationsItems = ref(defaultConversationsItems);
const activeKey = ref(defaultConversationsItems.value[0]?.key); const activeKey = ref(defaultConversationsItems.value[0]?.key);
const itemsData = ref([ // props.items key=id, label=name, title=name
{ const transformItems = computed(() => {
key: 'baca08c1-e92b-4dc9-a445-3584803f54d4', return props.temp.map((item) => ({
label: '海南职创申报书生成', key: item.id,
title: '海南职创申报书生成', label: item.name,
}, title: item.name,
]); }));
});
const itemsData = ref([]);
const handleMenuClick = (item: { key: string }) => { const handleMenuClick = (item: { key: string }) => {
const selectedItem = itemsData.value.find((i) => i.key === item.key); const selectedItem = itemsData.value.find((i) => i.key === item.key);
@ -60,6 +64,15 @@ const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
const selectedKeys = ref([]); const selectedKeys = ref([]);
const openKeys = ref([]); const openKeys = ref([]);
// transformItems itemsData
watch(
() => transformItems.value,
(newVal) => {
itemsData.value = newVal;
},
{ immediate: true },
);
</script> </script>
<template> <template>

View File

@ -10,7 +10,7 @@ import { message } from 'ant-design-vue';
// http://47.112.173.8:6802/static/66f3cfd95e364a239d8036390db658ae.pptx // http://47.112.173.8:6802/static/66f3cfd95e364a239d8036390db658ae.pptx
// const url = ref(''); // const url = ref('');
const isLoading = ref(false); // const isLoading = ref(false); //
const pptx = ref('/pptx/66f3cfd95e364a239d8036390db658ae.pptx'); const pptx = ref();
const pptStyle = ref({ const pptStyle = ref({
height: 'calc(100vh - 100px)', height: 'calc(100vh - 100px)',
width: '100%', width: '100%',
@ -37,7 +37,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
const data = drawerApi.getData<Record<string, any>>(); const data = drawerApi.getData<Record<string, any>>();
if (data) { if (data) {
isLoading.value = true; // isLoading.value = true; //
pptx.value = `/pptx/${data}`; // pptx pptx.value = data; // pptx
} }
// url.value = drawerApi.getData<Record<string, any>>(); // url.value = drawerApi.getData<Record<string, any>>();
} }

View File

@ -96,8 +96,7 @@ function openPreviewDrawer(
placement: DrawerPlacement = 'right', placement: DrawerPlacement = 'right',
filename?: string, filename?: string,
) { ) {
const fileData = filename.value; previewDrawerApi.setState({ placement }).setData(filename).open();
previewDrawerApi.setState({ placement }).setData(fileData).open();
} }
// ==================== Event ==================== // ==================== Event ====================
@ -106,18 +105,9 @@ const handleFileChange: AttachmentsProps['onChange'] = (info) =>
(attachedFiles.value = info.fileList); (attachedFiles.value = info.fileList);
// .pptx URL // .pptx URL
function isPptxURL(str: string): boolean { // function isPptxURL(str: string): boolean {
return str.endsWith('.pptx'); // return str.endsWith('.pptx');
} // }
// 使 /static/
function extractPptxFilename(url: string): null | string {
const match = url.match(/\/static\/([a-f0-9-]+\.pptx)$/i);
if (match) {
return match[1]; // 9e1c421e-991c-411f-8176-6350a97e70f3.pptx
}
return null;
}
const startFetching = async () => { const startFetching = async () => {
agentRequestLoading.value = true; agentRequestLoading.value = true;
@ -129,7 +119,7 @@ const startFetching = async () => {
}); });
try { try {
const res = await props.runWorkflow({ const res = await props.runWorkflow(props.item.id, {
userId: userStore.userInfo?.userId || '', userId: userStore.userInfo?.userId || '',
conversationId: '', conversationId: '',
files: [], files: [],
@ -137,17 +127,17 @@ const startFetching = async () => {
declarationDoc: content.value, declarationDoc: content.value,
}, },
}); });
const { result } = res.data.outputs; const files = res.data.outputs.files[0];
content.value = ''; content.value = '';
const filename = ref(''); // const filename = ref('');
if (result && isPptxURL(result)) { // if (result && isPptxURL(result)) {
filename.value = extractPptxFilename(result); // filename.value = extractPptxFilename(result);
} // }
// url http://47.112.173.8:6802/static/66f3cfd95e364a239d8036390db658ae.pptx // url http://47.112.173.8:6802/static/66f3cfd95e364a239d8036390db658ae.pptx
fetchResult.value = `/pptx/${filename.value}`; fetchResult.value = files.url;
resultItems.value.push({ resultItems.value.push({
key: resultItems.value.length + 1, key: resultItems.value.length + 1,
role: 'ai', role: 'ai',
@ -159,7 +149,7 @@ const startFetching = async () => {
size: 'nomarl', size: 'nomarl',
type: 'primary', type: 'primary',
onClick: () => { onClick: () => {
openPreviewDrawer('right', filename); openPreviewDrawer('right', files.url);
}, },
}, },
'文档预览', '文档预览',
@ -175,8 +165,8 @@ const startFetching = async () => {
onClick: () => { onClick: () => {
// <a> // <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = result; // link.href = files.url; //
link.download = filename.value; // link.download = files.filename; //
document.body.append(link); // <a> document.body.append(link); // <a>
link.click(); // link.click(); //
link.remove(); // <a> link.remove(); // <a>
@ -212,11 +202,6 @@ watch(
footer: msg.footer, footer: msg.footer,
}); });
} else { } else {
const filename = ref('');
if (msg.content && isPptxURL(msg.content)) {
filename.value = extractPptxFilename(msg.content);
}
resultItems.value.push({ resultItems.value.push({
key: resultItems.value.length + 1, key: resultItems.value.length + 1,
role: msg.role, // 'user' or 'ai' role: msg.role, // 'user' or 'ai'
@ -228,7 +213,7 @@ watch(
size: 'nomarl', size: 'nomarl',
type: 'primary', type: 'primary',
onClick: () => { onClick: () => {
openPreviewDrawer('right', filename); openPreviewDrawer('right', msg.content.url);
}, },
}, },
'文档预览', '文档预览',
@ -244,8 +229,8 @@ watch(
onClick: () => { onClick: () => {
// <a> // <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = msg.content; // link.href = msg.content.url; //
link.download = filename.value; // link.download = msg.content.filename; //
document.body.append(link); // <a> document.body.append(link); // <a>
link.click(); // link.click(); //
link.remove(); // <a> link.remove(); // <a>

View File

@ -1,18 +1,20 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { PPTTempItem, ResultItem } from './typing'; import type { PPTTempItem, ResultItem } from './typing';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { notification } from 'ant-design-vue'; import { notification } from 'ant-design-vue';
import { getWorkflowInfo, getWorkflowList, sendPpt } from '#/api'; import {
getAppListByType,
getWorkflowInfo,
getWorkflowList,
sendPpt,
} from '#/api';
import { PptListView, PptWorkView } from './components'; import { PptListView, PptWorkView } from './components';
let temp = reactive<PPTTempItem>({ const temp = ref([]);
id: 'ee3889b6-50fa-463e-b956-3b93447727fc',
name: '海南科技项目可研报告PPT生成',
});
const hitsory = ref([]); const hitsory = ref([]);
const loading = ref(true); const loading = ref(true);
@ -27,10 +29,18 @@ const getLogs = async (appid: string, limit: number) => {
loading.value = false; loading.value = false;
}; };
const getTemp = async () => {
const res = await getAppListByType(2);
if (Array.isArray(res) && res.length > 0) {
// id name temp
temp.value = res.map(({ id, name }) => ({ id, name }));
}
};
async function handleClick(item: PPTTempItem) { async function handleClick(item: PPTTempItem) {
temp = item; // temp = item;
const res = await getWorkflowInfo({ const res = await getWorkflowInfo({
appid: temp.appId, appid: item.appId,
workflowRunId: item.workflowRunId, workflowRunId: item.workflowRunId,
}); });
itemMessage.value = []; itemMessage.value = [];
@ -45,22 +55,25 @@ async function handleClick(item: PPTTempItem) {
itemMessage.value.push({ itemMessage.value.push({
key: itemMessage.value.length + 1, key: itemMessage.value.length + 1,
role: 'ai', role: 'ai',
content: res.outputs.result, content: res.outputs.files[0],
}); });
} }
} }
const currentTemp = ref<null | PPTTempItem>(null);
async function handleClickMode(item: PPTTempItem) { async function handleClickMode(item: PPTTempItem) {
notification.success({ notification.success({
message: `${item.name}`, message: `${item.name}`,
description: '已选取', description: '已选取',
duration: 3, duration: 3,
}); });
temp = item; currentTemp.value = item;
await getLogs(item.id, 5);
} }
onMounted(() => { onMounted(() => {
getLogs(temp.id, 5); getTemp();
}); });
</script> </script>
@ -69,6 +82,7 @@ onMounted(() => {
<div class="lg:w-1/6"> <div class="lg:w-1/6">
<PptListView <PptListView
title="选择模板" title="选择模板"
:temp="temp"
:items="hitsory" :items="hitsory"
:loading="loading" :loading="loading"
@click-mode="handleClickMode" @click-mode="handleClickMode"
@ -76,7 +90,7 @@ onMounted(() => {
/> />
</div> </div>
<PptWorkView <PptWorkView
:item="temp" :item="currentTemp"
:run-workflow="sendPpt" :run-workflow="sendPpt"
:item-message="itemMessage" :item-message="itemMessage"
/> />

View File

@ -21,6 +21,7 @@ interface PptHistoryItem {
interface Props { interface Props {
items?: PptHistoryItem[]; items?: PptHistoryItem[];
temp: PPTTempItem[];
title: string; title: string;
loading: boolean; loading: boolean;
} }
@ -50,7 +51,10 @@ interface WorkflowResult {
interface PropsWork { interface PropsWork {
itemMessage?: ResultItem; itemMessage?: ResultItem;
item?: PPTTempItem; item?: PPTTempItem;
runWorkflow?: (context: WorkflowContext) => Promise<WorkflowResult>; runWorkflow?: (
appId: any,
context: WorkflowContext,
) => Promise<WorkflowResult>;
} }
export type { export type {

View File

@ -3,52 +3,37 @@
// //
// import type { SpiderItem } from './typing'; // import type { SpiderItem } from './typing';
import { ref } from 'vue'; import type { Props } from '#/views/spider/typing';
import { computed, ref, watch } from 'vue';
import { Menu } from 'ant-design-vue'; import { Menu } from 'ant-design-vue';
// import { Conversations } from 'ant-design-x-vue';
// interface Props {
// items?: SpiderItem[];
// spiderList?: SpiderItem[];
// title: string;
// }
defineOptions({ defineOptions({
name: 'SpiderListView', name: 'SpiderListView',
}); });
// const props = withDefaults(defineProps<Props>(), {
// items: () => [], // import { Conversations } from 'ant-design-x-vue';
// spiderList: () => [],
// }); const props = withDefaults(defineProps<Props>(), {
items: () => [],
temp: () => [],
});
const emit = defineEmits(['click', 'clickMode']); const emit = defineEmits(['click', 'clickMode']);
const selectedKeys = ref([]); const selectedKeys = ref([]);
const openKeys = ref([]); const openKeys = ref([]);
// const defaultConversationsItems = computed(() => { // props.items key=id, label=name, title=name
// return props.items.map((item) => { const transformItems = computed(() => {
// return { return props.temp.map((item) => ({
// key: item.id, key: item.id,
// label: item.id, label: item.name,
// }; title: item.name,
// }); }));
// }); });
// const conversationsItems = ref(defaultConversationsItems);
// const activeKey = ref(defaultConversationsItems.value[0]?.key); const itemsData = ref();
const itemsData = ref([
{
key: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
label: '全国公共资源交易平台_信息爬取',
title: '全国公共资源交易平台_信息爬取',
url: 'https://ygp.gdzwfw.gov.cn/',
},
{
key: 'c736edd0-925d-4877-9223-56aab7342311',
label: '广州公共资源交易中心_信息获取',
title: '广州公共资源交易中心_信息获取',
url: 'https://www.gzggzy.cn',
},
]);
const handleMenuClick = (item: { key: string }) => { const handleMenuClick = (item: { key: string }) => {
const selectedItem = itemsData.value.find((i) => i.key === item.key); const selectedItem = itemsData.value.find((i) => i.key === item.key);
@ -73,6 +58,14 @@ const handleMenuClick = (item: { key: string }) => {
// emit('click', null); // // emit('click', null); //
// } // }
// }; // };
watch(
() => transformItems.value,
(newVal) => {
itemsData.value = newVal;
},
{ immediate: true },
);
</script> </script>
<template> <template>

View File

@ -2,14 +2,14 @@
// sendWorkflow // sendWorkflow
import type { ResultItem, TempItem, WordFlowItem } from './typing'; import type { ResultItem, TempItem, WordFlowItem } from './typing';
import { ref } from 'vue'; import { onMounted, ref } from 'vue';
import { notification } from 'ant-design-vue'; import { notification } from 'ant-design-vue';
import { import {
getAppListByType,
getSpiderStatus, getSpiderStatus,
getWorkflowInfo, getWorkflowInfo,
getWorkflowList,
runSpider, runSpider,
runSpiderGz, runSpiderGz,
stopSpider, stopSpider,
@ -19,21 +19,25 @@ import { SelfWorkView, SpiderListView, WorkflowWorkView } from './components';
const hitsory = ref([]); const hitsory = ref([]);
const loading = ref(true); const loading = ref(true);
const temp = ref({ const temp = ref([]);
id: '',
name: '',
url: '',
});
const itemMessage = ref<ResultItem[]>([]); const itemMessage = ref<ResultItem[]>([]);
// //
const getLogs = async (appid: string, limit: number) => { // const getLogs = async (appid: string, limit: number) => {
loading.value = true; // loading.value = true;
hitsory.value = await getWorkflowList({ // hitsory.value = await getWorkflowList({
appid, // appid,
limit, // limit,
}); // });
loading.value = false; // loading.value = false;
// };
const getTemp = async () => {
const res = await getAppListByType(3);
if (Array.isArray(res) && res.length > 0) {
// id name temp
temp.value = res.map(({ id, name }) => ({ id, name }));
}
}; };
async function handleClick(item: WordFlowItem) { async function handleClick(item: WordFlowItem) {
@ -51,19 +55,26 @@ async function handleClick(item: WordFlowItem) {
} }
} }
const currentTemp = ref<TempItem>({
id: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
name: '全国公共资源交易平台_信息爬取',
});
async function handleClickMode(item: TempItem) { async function handleClickMode(item: TempItem) {
notification.success({ notification.success({
message: `${item.name}`, message: `${item.name}`,
description: '已选取', description: '已选取',
duration: 3, duration: 3,
}); });
temp.value = item; currentTemp.value = item;
getLogs(item.id, 5);
// getLogs(item.id, 5);
} }
// onMounted(() => { onMounted(() => {
// getLogs(spiderList.value[0].id); getTemp();
// }); // getLogs(spiderList.value[0].id);
});
</script> </script>
<template> <template>
@ -71,6 +82,7 @@ async function handleClickMode(item: TempItem) {
<div class="lg:w-1/5"> <div class="lg:w-1/5">
<SpiderListView <SpiderListView
title="选择模板" title="选择模板"
:temp="temp"
:items="hitsory" :items="hitsory"
:loading="loading" :loading="loading"
@click-mode="handleClickMode" @click-mode="handleClickMode"
@ -78,9 +90,9 @@ async function handleClickMode(item: TempItem) {
/> />
</div> </div>
<SelfWorkView <SelfWorkView
v-if="temp.id === '77c068fd-d5b6-4c33-97d8-db5511a09b26'" v-if="currentTemp.id === '387e1ff7-3385-404d-ba29-80c5c62fbcf7'"
title="目标网址:" title="目标网址:"
:item="temp" :item="currentTemp"
:run-spider="runSpider" :run-spider="runSpider"
:stop-spider="stopSpider" :stop-spider="stopSpider"
:get-spider-status="getSpiderStatus" :get-spider-status="getSpiderStatus"
@ -90,7 +102,7 @@ async function handleClickMode(item: TempItem) {
<WorkflowWorkView <WorkflowWorkView
v-else v-else
title="目标网址:" title="目标网址:"
:item="temp" :item="currentTemp"
:run-spider="runSpider" :run-spider="runSpider"
:stop-spider="stopSpider" :stop-spider="stopSpider"
:get-spider-status="getSpiderStatus" :get-spider-status="getSpiderStatus"

View File

@ -1,7 +1,7 @@
interface TempItem { interface TempItem {
id: string; id: string;
name: string; name: string;
url: string; url?: string;
} }
interface ResultItem { interface ResultItem {
@ -47,6 +47,7 @@ interface ResultItem {
interface Props { interface Props {
itemMessage?: ResultItem; itemMessage?: ResultItem;
temp?: any;
item?: SpiderItem; item?: SpiderItem;
title: string; title: string;
runSpider?: (context: any) => Promise<SpiderResult>; runSpider?: (context: any) => Promise<SpiderResult>;

View File

@ -3,7 +3,7 @@ import type { ConversationsProps } from 'ant-design-x-vue';
import type { PropsHistory } from '../typing'; import type { PropsHistory } from '../typing';
import { computed, h, ref } from 'vue'; import { computed, h, ref, watch } from 'vue';
import { DeleteOutlined } from '@ant-design/icons-vue'; import { DeleteOutlined } from '@ant-design/icons-vue';
// import { Card, CardContent, CardHeader, CardTitle } from '@vben-core/shadcn-ui'; // import { Card, CardContent, CardHeader, CardTitle } from '@vben-core/shadcn-ui';
@ -16,6 +16,7 @@ defineOptions({
const props = withDefaults(defineProps<PropsHistory>(), { const props = withDefaults(defineProps<PropsHistory>(), {
items: () => [], items: () => [],
temp: () => [],
}); });
const emit = defineEmits(['click', 'clickMode', 'delete']); const emit = defineEmits(['click', 'clickMode', 'delete']);
const defaultConversationsItems = computed(() => { const defaultConversationsItems = computed(() => {
@ -29,23 +30,16 @@ const defaultConversationsItems = computed(() => {
const conversationsItems = ref(defaultConversationsItems); const conversationsItems = ref(defaultConversationsItems);
const activeKey = ref(defaultConversationsItems.value[0]?.key); const activeKey = ref(defaultConversationsItems.value[0]?.key);
const itemsData = ref([ // props.items key=id, label=name, title=name
{ const transformItems = computed(() => {
key: 'baca08c1-e92b-4dc9-a445-3584803f54d4', return props.temp.map((item) => ({
label: '海南职创申报书生成', key: item.id,
title: '海南职创申报书生成', label: item.name,
}, title: item.name,
]); }));
});
// const items2: ItemType[] = reactive([ const itemsData = ref();
// getItem(
// '',
// 'grp',
// null,
// [getItem('', 'baca08c1-e92b-4dc9-a445-3584803f54d4')],
// 'group',
// ),
// ]);
const handleMenuClick = (item: { key: string }) => { const handleMenuClick = (item: { key: string }) => {
const selectedItem = itemsData.value.find((i) => i.key === item.key); const selectedItem = itemsData.value.find((i) => i.key === item.key);
@ -80,6 +74,15 @@ const menuConfig: ConversationsProps['menu'] = (conversation) => ({
const selectedKeys = ref([]); const selectedKeys = ref([]);
const openKeys = ref([]); const openKeys = ref([]);
// transformItems itemsData
watch(
() => transformItems.value,
(newVal) => {
itemsData.value = newVal;
},
{ immediate: true },
);
</script> </script>
<template> <template>

View File

@ -10,7 +10,7 @@ import '@vue-office/docx/lib/index.css';
// const url = ref(''); // const url = ref('');
const isLoading = ref(false); // const isLoading = ref(false); //
const docx = ref<any>(`/docx/027c6b7c-fea6-4964-839b-27857c4d3181.docx`); const docx = ref<any>();
const pptStyle = ref({ const pptStyle = ref({
height: 'calc(100vh - 100px)', height: 'calc(100vh - 100px)',
width: '100%', width: '100%',
@ -36,7 +36,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
const data = drawerApi.getData<Record<string, any>>(); const data = drawerApi.getData<Record<string, any>>();
if (data) { if (data) {
isLoading.value = true; isLoading.value = true;
docx.value = `/docx/${data}`; // docx docx.value = data; // docx
} }
// url.value = drawerApi.getData<Record<string, any>>(); // url.value = drawerApi.getData<Record<string, any>>();
} }

View File

@ -159,7 +159,6 @@ const open = ref(false);
const fetchStatus = ref(''); const fetchStatus = ref('');
const resultItems = ref<ResultItem[]>([]); const resultItems = ref<ResultItem[]>([]);
const conversationId = ref(''); const conversationId = ref('');
const fetchResult = ref('');
const layout = { const layout = {
labelCol: { span: 6 }, labelCol: { span: 6 },
wrapperCol: { span: 16 }, wrapperCol: { span: 16 },
@ -175,25 +174,39 @@ function openPreviewDrawer(
placement: DrawerPlacement = 'right', placement: DrawerPlacement = 'right',
filename?: string, filename?: string,
) { ) {
const fileData = filename.value; previewDrawerApi.setState({ placement }).setData(filename).open();
previewDrawerApi.setState({ placement }).setData(fileData).open();
} }
// ==================== Event ==================== // ==================== Event ====================
// .docx URL // .docx URL
function isDocxURL(str: string): boolean { // function isDocxURL(str: string): boolean {
return str.endsWith('.docx'); // return str.endsWith('.docx');
} // }
// 使 /static/ // 使 /static/
function extractDocxFilename(url: string): null | string { // function extractDocxFilename(url: string): null | string {
const match = url.match(/\/static\/([a-f0-9-]+\.docx)$/i); // const match = url.match(/\/static\/([a-f0-9-]+\.docx)$/i);
if (match) { // if (match) {
return match[1]; // 9e1c421e-991c-411f-8176-6350a97e70f3.docx // return match[1]; // 9e1c421e-991c-411f-8176-6350a97e70f3.docx
// }
// return null;
// }
function extractDocxInfo(
markdownLink: string,
): null | { filename: string; url: string } {
const fileRegex = /\[(.*?)\]/;
const urlRegex = /\((.*?)\)/;
const fileMatch = markdownLink.match(fileRegex);
const urlMatch = markdownLink.match(urlRegex);
if (fileMatch && urlMatch) {
const filename = fileMatch[1]; // [filename]
const url = urlMatch[1] || ''; // (url)
return { filename, url };
} }
return null; return null;
} }
const startFetching = async () => { const startFetching = async () => {
if (projectInfo.value.projectName === '') { if (projectInfo.value.projectName === '') {
open.value = true; open.value = true;
@ -217,7 +230,7 @@ const startFetching = async () => {
}); });
try { try {
const res = await props.runChatflow({ const res = await props.runChatflow(props.item.id, {
userId: userStore.userInfo?.userId || '', userId: userStore.userInfo?.userId || '',
conversationId: conversationId.value, conversationId: conversationId.value,
files: [], files: [],
@ -234,10 +247,10 @@ const startFetching = async () => {
const { answer } = res; const { answer } = res;
conversationId.value = res.conversationId; conversationId.value = res.conversationId;
const filename = ref(''); const docxInfo = extractDocxInfo(answer);
if (answer && isDocxURL(answer)) { if (docxInfo) {
filename.value = extractDocxFilename(answer); const { filename, url } = docxInfo;
resultItems.value.push({ resultItems.value.push({
key: resultItems.value.length + 1, key: resultItems.value.length + 1,
role: 'ai', role: 'ai',
@ -246,10 +259,10 @@ const startFetching = async () => {
h( h(
Button, Button,
{ {
size: 'nomarl', size: 'normal',
type: 'primary', type: 'primary',
onClick: () => { onClick: () => {
openPreviewDrawer('right', filename); openPreviewDrawer('right', url);
}, },
}, },
'文档预览', '文档预览',
@ -257,19 +270,16 @@ const startFetching = async () => {
h( h(
Button, Button,
{ {
size: 'nomarl', size: 'normal',
type: 'primary', type: 'primary',
style: { style: { marginLeft: '10px' },
marginLeft: '10px',
},
onClick: () => { onClick: () => {
// <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = answer; // link.href = url;
link.download = filename.value; // link.download = filename;
document.body.append(link); // <a> document.body.append(link);
link.click(); // link.click();
link.remove(); // <a> link.remove();
}, },
}, },
'文档下载', '文档下载',
@ -283,13 +293,10 @@ const startFetching = async () => {
content: res.answer, content: res.answer,
}); });
} }
fetchResult.value = `/static/${filename.value}`;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
//
agentRequestLoading.value = false; agentRequestLoading.value = false;
fetchStatus.value = 'completed'; fetchStatus.value = 'completed';
}; };
@ -308,18 +315,18 @@ watch(
if (msg.role === 'user') { if (msg.role === 'user') {
resultItems.value.push({ resultItems.value.push({
key: resultItems.value.length + 1, key: resultItems.value.length + 1,
role: msg.role, // 'user' or 'ai' role: msg.role,
content: msg.content, content: msg.content,
footer: msg.footer, footer: msg.footer,
}); });
} else { } else {
const filename = ref(''); const docxInfo = extractDocxInfo(msg.content);
if (msg.content && isDocxURL(msg.content)) { if (docxInfo) {
filename.value = extractDocxFilename(msg.content); const { filename, url } = docxInfo;
resultItems.value.push({ resultItems.value.push({
key: resultItems.value.length + 1, key: resultItems.value.length + 1,
role: msg.role, // 'user' or 'ai' role: msg.role,
content: '文档已生成', content: '文档已生成',
footer: h(Flex, null, [ footer: h(Flex, null, [
h( h(
@ -328,7 +335,7 @@ watch(
size: 'normal', size: 'normal',
type: 'primary', type: 'primary',
onClick: () => { onClick: () => {
openPreviewDrawer('right', filename); openPreviewDrawer('right', { value: filename });
}, },
}, },
'文档预览', '文档预览',
@ -338,17 +345,14 @@ watch(
{ {
size: 'normal', size: 'normal',
type: 'primary', type: 'primary',
style: { style: { marginLeft: '10px' },
marginLeft: '10px',
},
onClick: () => { onClick: () => {
// <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = msg.content; // link.href = url;
link.download = filename.value; // link.download = filename;
document.body.append(link); // <a> document.body.append(link);
link.click(); // link.click();
link.remove(); // <a> link.remove();
}, },
}, },
'文档下载', '文档下载',
@ -358,7 +362,7 @@ watch(
} else { } else {
resultItems.value.push({ resultItems.value.push({
key: resultItems.value.length + 1, key: resultItems.value.length + 1,
role: msg.role, // 'user' or 'ai' role: msg.role,
content: msg.content, content: msg.content,
}); });
} }

View File

@ -1,14 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { WordTempItem } from '@vben/common-ui'; import type { ResultItem, WordTempItem } from './typing';
import type { ResultItem } from './typing'; import type { PPTTempItem } from '#/views/ppt/typing';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { message, notification } from 'ant-design-vue'; import { message, notification } from 'ant-design-vue';
import { import {
deleteChatflow, deleteChatflow,
getAppListByType,
getChatflowMessage, getChatflowMessage,
getChatList, getChatList,
getChatParameters, getChatParameters,
@ -17,10 +18,7 @@ import {
import { WordListView, WordWorkView } from './components'; import { WordListView, WordWorkView } from './components';
let temp = reactive<WordTempItem>({ const temp = ref([]);
id: 'baca08c1-e92b-4dc9-a445-3584803f54d4',
name: '海南职创申报书生成',
});
const hitsory = ref([]); const hitsory = ref([]);
const loading = ref(true); const loading = ref(true);
@ -48,26 +46,38 @@ const getLogs = async (appid: string) => {
loading.value = false; loading.value = false;
}; };
const getTemp = async () => {
const res = await getAppListByType(1);
if (Array.isArray(res) && res.length > 0) {
// id name temp
temp.value = res.map(({ id, name }) => ({ id, name }));
}
};
const currentTemp = ref<null | PPTTempItem>(null);
function handleClickMode(item: WordTempItem) { function handleClickMode(item: WordTempItem) {
notification.success({ notification.success({
message: `${item.name}`, message: `${item.name}`,
description: '已选取', description: '已选取',
duration: 3, duration: 3,
}); });
temp = item; currentTemp.value = item;
getParameters(temp.id); getLogs(item.id);
getParameters(item.id);
} }
const itemMessage = ref<ResultItem[]>([]); const itemMessage = ref<ResultItem[]>([]);
async function deleteLog(item: any) { async function deleteLog(item: any) {
const res = await deleteChatflow({ const res = await deleteChatflow({
appId: temp.id, appId: temp.value.id,
conversationId: item, conversationId: item,
userId: '1562', userId: '1562',
}); });
if (res.code === 0) { if (res.code === 0) {
message.success('删除成功'); message.success('删除成功');
getLogs(temp.id); getLogs(temp.value.id);
} else { } else {
message.error('删除失败'); message.error('删除失败');
} }
@ -79,7 +89,7 @@ async function getParameters(id: string) {
} }
async function handleClick(item: string) { async function handleClick(item: string) {
const res = await getChatflowMessage(temp.id, { const res = await getChatflowMessage(currentTemp.value.id, {
userId: '1562', userId: '1562',
firstId: '', firstId: '',
conversationId: item, conversationId: item,
@ -108,8 +118,7 @@ async function handleClick(item: string) {
} }
onMounted(() => { onMounted(() => {
getLogs(temp.id); getTemp();
getParameters(temp.id);
}); });
</script> </script>
@ -118,6 +127,7 @@ onMounted(() => {
<div class="lg:w-1/6"> <div class="lg:w-1/6">
<WordListView <WordListView
title="选择模板" title="选择模板"
:temp="temp"
:items="hitsory" :items="hitsory"
:loading="loading" :loading="loading"
@click-mode="handleClickMode" @click-mode="handleClickMode"
@ -126,7 +136,7 @@ onMounted(() => {
/> />
</div> </div>
<WordWorkView <WordWorkView
:item="temp" :item="currentTemp"
:params-data="params" :params-data="params"
:run-chatflow="sendWord" :run-chatflow="sendWord"
:item-message="itemMessage" :item-message="itemMessage"

View File

@ -44,10 +44,14 @@ interface Props {
itemMessage?: ResultItem; itemMessage?: ResultItem;
item?: WordTempItem; item?: WordTempItem;
paramsData?: object; paramsData?: object;
runChatflow?: (context: WorkflowContext) => Promise<WorkflowResult>; runChatflow?: (
appId: any,
context: WorkflowContext,
) => Promise<WorkflowResult>;
} }
interface PropsHistory { interface PropsHistory {
temp: any;
items?: WordHistoryItem[]; items?: WordHistoryItem[];
title: string; title: string;
loading: boolean; loading: boolean;

View File

@ -15,10 +15,10 @@ export default defineConfig(async () => {
// target: 'http://192.168.3.238:8081/api', // target: 'http://192.168.3.238:8081/api',
ws: true, ws: true,
}, },
'/docx': { '/files': {
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/docx/, ''), rewrite: (path) => path.replace(/^\/files/, ''),
target: 'http://47.112.173.8:6805/static', target: 'http://47.112.173.8:6800/files/',
}, },
'/guangzhou': { '/guangzhou': {
changeOrigin: true, changeOrigin: true,