refactor(@vben/common-ui): 优化三个工作视图的样式和功能

- 为用户和 AI 气泡添加白色背景样式
-修复文件下载时的文件名问题
- 移除或注释掉不必要的代码
- 添加对项目名称和内容的验证
-优化通知消息的显示
This commit is contained in:
Kven 2025-05-18 15:58:47 +08:00
parent 65fa961a7f
commit 4c9cc17366
4 changed files with 143 additions and 178 deletions

View File

@ -74,6 +74,11 @@ const roles: BubbleListProps['roles'] = {
user: {
placement: 'end',
typing: false,
styles: {
content: {
background: '#ffffff',
},
},
avatar: { icon: h(UserOutlined), style: { background: '#87d068' } },
},
ai: {
@ -84,6 +89,9 @@ const roles: BubbleListProps['roles'] = {
marginInlineEnd: 44,
},
styles: {
content: {
background: '#ffffff',
},
footer: {
width: '100%',
},
@ -232,7 +240,7 @@ const startFetching = async () => {
// <a>
const link = document.createElement('a');
link.href = result; //
link.download = filename; //
link.download = filename.value; //
document.body.append(link); // <a>
link.click(); //
link.remove(); // <a>
@ -307,7 +315,7 @@ watch(
// <a>
const link = document.createElement('a');
link.href = msg.content; //
link.download = filename; //
link.download = filename.value; //
document.body.append(link); // <a>
link.click(); //
link.remove(); // <a>

View File

@ -1,40 +1,40 @@
<script setup lang="ts">
import type { ConversationsProps } from 'ant-design-x-vue';
// import type { ConversationsProps } from 'ant-design-x-vue';
//
// import type { SpiderItem } from './typing';
import type { SpiderItem } from './typing';
import { computed, ref } from 'vue';
import { ref } from 'vue';
import { Menu } from 'ant-design-vue';
import { Conversations } from 'ant-design-x-vue';
// import { Conversations } from 'ant-design-x-vue';
interface Props {
items?: SpiderItem[];
spiderList?: SpiderItem[];
title: string;
}
// interface Props {
// items?: SpiderItem[];
// spiderList?: SpiderItem[];
// title: string;
// }
defineOptions({
name: 'SpiderListView',
});
const props = withDefaults(defineProps<Props>(), {
items: () => [],
spiderList: () => [],
});
// const props = withDefaults(defineProps<Props>(), {
// items: () => [],
// spiderList: () => [],
// });
const emit = defineEmits(['click', 'clickMode']);
const selectedKeys = ref([]);
const openKeys = ref([]);
const defaultConversationsItems = computed(() => {
return props.items.map((item) => {
return {
key: item.id,
label: item.id,
};
});
});
const conversationsItems = ref(defaultConversationsItems);
const activeKey = ref(defaultConversationsItems.value[0]?.key);
// const defaultConversationsItems = computed(() => {
// return props.items.map((item) => {
// return {
// key: item.id,
// label: item.id,
// };
// });
// });
// const conversationsItems = ref(defaultConversationsItems);
// const activeKey = ref(defaultConversationsItems.value[0]?.key);
const itemsData = ref([
{
key: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
@ -63,16 +63,16 @@ const handleMenuClick = (item: { key: string }) => {
}
};
const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
activeKey.value = key;
const matchedItem = props.items.find((item) => item.id === key);
if (matchedItem) {
emit('click', matchedItem);
} else {
emit('click', null); //
}
};
// const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
// activeKey.value = key;
//
// const matchedItem = props.items.find((item) => item.id === key);
// if (matchedItem) {
// emit('click', matchedItem);
// } else {
// emit('click', null); //
// }
// };
</script>
<template>
@ -88,16 +88,16 @@ const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
/>
<!-- 🌟 添加会话 -->
<!-- <Button type="link" class="addBtn">会话</Button>-->
<div class="addBtn">获取记录</div>
<!-- <div class="addBtn">获取记录</div>-->
<!-- 🌟 添加会话 -->
<!-- 🌟 会话管理 -->
<Conversations
:items="conversationsItems"
class="conversations"
:active-key="activeKey"
@active-change="onConversationClick"
/>
<!-- <Conversations-->
<!-- :items="conversationsItems"-->
<!-- class="conversations"-->
<!-- :active-key="activeKey"-->
<!-- @active-change="onConversationClick"-->
<!-- />-->
</div>
</template>

View File

@ -344,82 +344,6 @@ const startFetching = async () => {
}
isFetching.value = false;
}
// try {
// notification.info({
// message: '...',
// duration: 3,
// });
// const res = await props.runSpider(
// {
// publish_start_time,
// publish_end_time,
// llm_api_key: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
// },
// );
// if (res.data.outputs.result) {
// //
// fetchResult.value = res.data.outputs.result;
// notification.success({
// message: '',
// duration: 3,
// });
// const { result } = res.data.outputs;
//
// const filename = ref('');
//
// if (result && isDocxURL(result)) {
// filename.value = extractDocxFilename(result);
// }
//
// // url http://47.112.173.8:6802/static/66f3cfd95e364a239d8036390db658ae.docx
// fetchResult.value = `/static/${filename.value}`;
// resultItems.value.push({
// key: resultItems.value.length + 1,
// role: 'ai',
// content: '',
// footer: h(Flex, null, [
// h(
// Button,
// {
// size: 'nomarl',
// 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>
// },
// },
// '',
// ),
// ]),
// });
// fetchStatus.value = 'completed';
// } else {
// fetchResult.value = '';
// message.error('');
// }
// } catch (error) {
// message.error(`${error}`);
// }
};
//
@ -427,6 +351,11 @@ const roles: BubbleListProps['roles'] = {
user: {
placement: 'end',
typing: false,
styles: {
content: {
background: '#ffffff',
},
},
avatar: { icon: h(UserOutlined), style: { background: '#87d068' } },
},
ai: {
@ -437,6 +366,9 @@ const roles: BubbleListProps['roles'] = {
marginInlineEnd: 44,
},
styles: {
content: {
background: '#ffffff',
},
footer: {
width: '100%',
},
@ -462,6 +394,14 @@ const isFetching = ref(false);
const fetchResult = ref('');
const fetchStatus = ref('');
// title resultItems
watch(
() => props.title,
() => {
resultItems.value = [];
},
);
// itemMessage resultItems
watch(
() => props.itemMessage,

View File

@ -14,8 +14,9 @@ import { computed, h, ref, watch } from 'vue';
import { useVbenDrawer } from '@vben-core/popup-ui';
import {
CloudUploadOutlined,
PaperClipOutlined,
EditOutlined,
// CloudUploadOutlined,
// PaperClipOutlined,
UserOutlined,
} from '@ant-design/icons-vue';
// import type { VNode } from 'vue';
@ -27,6 +28,7 @@ import {
FormItem,
Input,
Modal,
notification,
Space,
Textarea,
Typography,
@ -147,17 +149,27 @@ const senderPromptsItems = computed(() => {
const roles: BubbleListProps['roles'] = {
user: {
placement: 'end',
variant: 'shadow',
typing: false,
styles: {
content: {
background: '#ffffff',
},
},
avatar: { icon: h(UserOutlined), style: { background: '#87d068' } },
},
ai: {
placement: 'start',
variant: 'shadow',
typing: false,
style: {
maxWidth: 600,
marginInlineEnd: 44,
},
styles: {
content: {
background: '#ffffff',
},
footer: {
width: '100%',
},
@ -260,10 +272,19 @@ function extractDocxFilename(url: string): null | string {
}
return null;
}
const showModal = () => {
open.value = true;
};
const startFetching = async () => {
if (projectInfo.value.projectName === '') {
open.value = true;
return;
}
if (content.value === '') {
notification.warn({
message: '请输入项目内容',
description: '请在输入框中输入项目内容',
});
open.value = false;
return;
}
open.value = false;
agentRequestLoading.value = true;
fetchStatus.value = 'fetching';
@ -328,7 +349,7 @@ const startFetching = async () => {
// <a>
const link = document.createElement('a');
link.href = answer; //
link.download = filename; //
link.download = filename.value; //
document.body.append(link); // <a>
link.click(); //
link.remove(); // <a>
@ -360,8 +381,8 @@ const onPromptsItemClick: PromptsProps['onItemClick'] = (info) => {
content.value = info.data.description;
};
const handleFileChange: AttachmentsProps['onChange'] = (info) =>
(attachedFiles.value = info.fileList);
// const handleFileChange: AttachmentsProps['onChange'] = (info) =>
// (attachedFiles.value = info.fileList);
// itemMessage resultItems
watch(
@ -410,7 +431,7 @@ watch(
// <a>
const link = document.createElement('a');
link.href = msg.content; //
link.download = filename; //
link.download = filename.value; //
document.body.append(link); // <a>
link.click(); //
link.remove(); // <a>
@ -446,23 +467,19 @@ watch(
placeholder="请输入项目名称"
/>
</FormItem>
<FormItem label="项目背景" name="projectContext" required>
<FormItem label="项目背景" name="projectContext">
<Textarea
v-model:value="projectInfo.projectContext"
placeholder="请输入项目背景"
/>
</FormItem>
<FormItem
label="规避关键词"
name="projectKeyAvoidTechOrKeyword"
required
>
<FormItem label="规避关键词" name="projectKeyAvoidTechOrKeyword">
<Textarea
v-model:value="projectInfo.projectKeyAvoidTechOrKeyword"
placeholder="规避关键词"
/>
</FormItem>
<FormItem label="创新点" name="userInitialInnovationPoint" required>
<FormItem label="创新点" name="userInitialInnovationPoint">
<Textarea
v-model:value="projectInfo.userInitialInnovationPoint"
placeholder="请输入创新点"
@ -510,59 +527,59 @@ watch(
:value="content"
class="sender"
:loading="agentRequestLoading"
@submit="showModal"
@submit="startFetching"
@change="(value) => (content = value)"
>
<template #prefix>
<Badge :dot="attachedFiles.length > 0 && !headerOpen">
<Button type="text" @click="() => (headerOpen = !headerOpen)">
<Button type="text" @click="() => (open = !open)">
<template #icon>
<PaperClipOutlined />
<EditOutlined />
</template>
</Button>
</Badge>
</template>
<template #header>
<Sender.Header
title="Attachments"
:open="headerOpen"
:styles="{ content: { padding: 0 } }"
@open-change="(open) => (headerOpen = open)"
>
<Attachments
:before-upload="() => false"
:items="attachedFiles"
@change="handleFileChange"
>
<template #placeholder="type">
<Flex
v-if="type && type.type === 'inline'"
align="center"
justify="center"
vertical
gap="2"
>
<Typography.Text style="font-size: 30px; line-height: 1">
<CloudUploadOutlined />
</Typography.Text>
<Typography.Title
:level="5"
style="margin: 0; font-size: 14px; line-height: 1.5"
>
Upload files
</Typography.Title>
<Typography.Text type="secondary">
Click or drag files to this area to upload
</Typography.Text>
</Flex>
<Typography.Text v-if="type && type.type === 'drop'">
Drop file here
</Typography.Text>
</template>
</Attachments>
</Sender.Header>
</template>
<!-- <template #header>-->
<!-- <Sender.Header-->
<!-- title="Attachments"-->
<!-- :open="headerOpen"-->
<!-- :styles="{ content: { padding: 0 } }"-->
<!-- @open-change="(open) => (headerOpen = open)"-->
<!-- >-->
<!-- <Attachments-->
<!-- :before-upload="() => false"-->
<!-- :items="attachedFiles"-->
<!-- @change="handleFileChange"-->
<!-- >-->
<!-- <template #placeholder="type">-->
<!-- <Flex-->
<!-- v-if="type && type.type === 'inline'"-->
<!-- align="center"-->
<!-- justify="center"-->
<!-- vertical-->
<!-- gap="2"-->
<!-- >-->
<!-- <Typography.Text style="font-size: 30px; line-height: 1">-->
<!-- <CloudUploadOutlined />-->
<!-- </Typography.Text>-->
<!-- <Typography.Title-->
<!-- :level="5"-->
<!-- style="margin: 0; font-size: 14px; line-height: 1.5"-->
<!-- >-->
<!-- Upload files-->
<!-- </Typography.Title>-->
<!-- <Typography.Text type="secondary">-->
<!-- Click or drag files to this area to upload-->
<!-- </Typography.Text>-->
<!-- </Flex>-->
<!-- <Typography.Text v-if="type && type.type === 'drop'">-->
<!-- Drop file here-->
<!-- </Typography.Text>-->
<!-- </template>-->
<!-- </Attachments>-->
<!-- </Sender.Header>-->
<!-- </template>-->
</Sender>
</div>
</div>