feat: ai
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715352878351" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1499" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M624.5 786.3c92.9 0 168.2-75.3 168.2-168.2V309c0-92.4-75.3-168.2-168.2-168.2H303.6c-92.4 0-168.2 75.3-168.2 168.2v309.1c0 92.4 75.3 168.2 168.2 168.2h320.9zM178.2 618.1V309c0-69.4 56.1-125.5 125.5-125.5h320.9c69.4 0 125.5 56.1 125.5 125.5v309.1c0 69.4-56.1 125.5-125.5 125.5h-321c-69.4 0-125.4-56.1-125.4-125.5z" p-id="1500" fill="#8a8a8a"></path><path d="M849.8 295.1v361.5c0 102.7-83.6 186.3-186.3 186.3H279.1v42.7h384.4c126.3 0 229.1-102.8 229.1-229.1V295.1h-42.8zM307.9 361.8h312.3c11.8 0 21.4-9.6 21.4-21.4 0-11.8-9.6-21.4-21.4-21.4H307.9c-11.8 0-21.4 9.6-21.4 21.4 0 11.9 9.6 21.4 21.4 21.4zM307.9 484.6h312.3c11.8 0 21.4-9.6 21.4-21.4 0-11.8-9.6-21.4-21.4-21.4H307.9c-11.8 0-21.4 9.6-21.4 21.4 0 11.9 9.6 21.4 21.4 21.4z" p-id="1501" fill="#8a8a8a"></path><path d="M620.2 607.4c11.8 0 21.4-9.6 21.4-21.4 0-11.8-9.6-21.4-21.4-21.4H307.9c-11.8 0-21.4 9.6-21.4 21.4 0 11.8 9.6 21.4 21.4 21.4h312.3z" p-id="1502" fill="#8a8a8a"></path></svg>
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715354120346" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3256" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M907.1 263.7H118.9c-9.1 0-16.4-7.3-16.4-16.4s7.3-16.4 16.4-16.4H907c9.1 0 16.4 7.3 16.4 16.4s-7.3 16.4-16.3 16.4z" fill="#8a8a8a" p-id="3257"></path><path d="M772.5 928.3H257.4c-27.7 0-50.2-22.5-50.2-50.2V247.2c0-9.1 7.3-16.4 16.4-16.4H801c12.1 0 21.9 9.8 21.9 21.9v625.2c0 27.8-22.6 50.4-50.4 50.4zM240 263.7v614.4c0 9.6 7.8 17.4 17.4 17.4h515.2c9.7 0 17.5-7.9 17.5-17.5V263.7H240zM657.4 131.1H368.6c-9.1 0-16.4-7.3-16.4-16.4s7.3-16.4 16.4-16.4h288.7c9.1 0 16.4 7.3 16.4 16.4s-7.3 16.4-16.3 16.4z" fill="#8a8a8a" p-id="3258"></path><path d="M416 754.5c-9.1 0-16.4-7.3-16.4-16.4V517.8c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4V738c0.1 9.1-7.3 16.5-16.4 16.5z" fill="#8a8a8a" p-id="3259"></path><path d="M416 465.2c-9.1 0-16.4-7.3-16.4-16.4v-59.4c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4v59.4c0.1 9.1-7.3 16.4-16.4 16.4zM604.9 754.5c-9.1 0-16.4-7.3-16.4-16.4v-67.2c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4V738c0 9.1-7.3 16.5-16.4 16.5z" fill="#8a8a8a" opacity=".4" p-id="3260"></path><path d="M604.9 619.1c-9.1 0-16.4-7.3-16.4-16.4V389.4c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4v213.3c0 9.1-7.3 16.4-16.4 16.4z" fill="#8a8a8a" p-id="3261"></path></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
@ -365,12 +365,12 @@ export const Dall3Models: ImageModelVO[] = [
|
|||
{
|
||||
key: 'dall-e-3',
|
||||
name: 'DALL·E 3',
|
||||
image: `/static/dall2.jpg`,
|
||||
image: `/static/imgs/ai/dall2.jpg`,
|
||||
},
|
||||
{
|
||||
key: 'dall-e-2',
|
||||
name: 'DALL·E 2',
|
||||
image: `/static/dall3.jpg`,
|
||||
image: `/static/imgs/ai/dall3.jpg`,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -378,12 +378,12 @@ export const Dall3StyleList: ImageModelVO[] = [
|
|||
{
|
||||
key: 'vivid',
|
||||
name: '清晰',
|
||||
image: `/static/qingxi.jpg`,
|
||||
image: `/static/imgs/ai/qingxi.jpg`,
|
||||
},
|
||||
{
|
||||
key: 'natural',
|
||||
name: '自然',
|
||||
image: `/static/ziran.jpg`,
|
||||
image: `/static/imgs/ai/ziran.jpg`,
|
||||
},
|
||||
];
|
||||
export const MidjourneyModels: ImageModelVO[] = [
|
||||
|
|
|
@ -318,7 +318,7 @@ onMounted(async () => {
|
|||
type="primary"
|
||||
@click="createConversation"
|
||||
>
|
||||
<IconifyIcon icon="ep:plus" class="mr-[5px]" />
|
||||
<IconifyIcon icon="lucide:plus" class="mr-[5px]" />
|
||||
新建对话
|
||||
</Button>
|
||||
|
||||
|
@ -330,7 +330,7 @@ onMounted(async () => {
|
|||
@keyup="searchConversation"
|
||||
>
|
||||
<template #prefix>
|
||||
<IconifyIcon icon="ep:search" />
|
||||
<IconifyIcon icon="lucide:search" />
|
||||
</template>
|
||||
</Input>
|
||||
|
||||
|
@ -389,28 +389,28 @@ onMounted(async () => {
|
|||
type="link"
|
||||
@click.stop="handleTop(conversation)"
|
||||
>
|
||||
<span
|
||||
<IconifyIcon
|
||||
v-if="!conversation.pinned"
|
||||
class="icon-[ant-design--arrow-up-outlined]"
|
||||
></span>
|
||||
<span
|
||||
icon="lucide:arrow-up"
|
||||
/>
|
||||
<IconifyIcon
|
||||
v-if="conversation.pinned"
|
||||
class="icon-[ant-design--arrow-down-outlined]"
|
||||
></span>
|
||||
icon="lucide:arrow-down"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
class="btn mr-0 px-[5px]"
|
||||
type="link"
|
||||
@click.stop="updateConversationTitle(conversation)"
|
||||
>
|
||||
<IconifyIcon icon="ep:edit" />
|
||||
<IconifyIcon icon="lucide:edit" />
|
||||
</Button>
|
||||
<Button
|
||||
class="btn mr-0 px-[5px]"
|
||||
type="link"
|
||||
@click.stop="deleteChatConversation(conversation)"
|
||||
>
|
||||
<IconifyIcon icon="ep:delete" />
|
||||
<IconifyIcon icon="lucide:trash" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -430,14 +430,14 @@ onMounted(async () => {
|
|||
class="flex cursor-pointer items-center text-[#606266]"
|
||||
@click="handleRoleRepository"
|
||||
>
|
||||
<IconifyIcon icon="ep:user" />
|
||||
<IconifyIcon icon="lucide:user" />
|
||||
<span class="ml-[5px]">角色仓库</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex cursor-pointer items-center text-[#606266]"
|
||||
@click="handleClearConversation"
|
||||
>
|
||||
<IconifyIcon icon="ep:delete" />
|
||||
<IconifyIcon icon="lucide:trash" />
|
||||
<span class="ml-[5px]">清空未置顶对话</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,7 +60,7 @@ function handleClick(doc: any) {
|
|||
class="mt-[10px] rounded-[8px] bg-[#f5f5f5] p-[10px]"
|
||||
>
|
||||
<div class="text-14px mb-8px flex items-center text-[#666]">
|
||||
<IconifyIcon icon="ep:document" class="mr-[5px]" /> 知识引用
|
||||
<IconifyIcon icon="lucide:file-text" class="mr-[5px]" /> 知识引用
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-[8px]">
|
||||
<div
|
||||
|
|
|
@ -6,6 +6,7 @@ import type { AiChatMessageApi } from '#/api/ai/chat/message';
|
|||
|
||||
import { computed, nextTick, onMounted, ref, toRefs } from 'vue';
|
||||
|
||||
import { IconifyIcon, SvgGptIcon } from '@vben/icons';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
@ -41,9 +42,6 @@ const isScrolling = ref(false); // 用于判断用户是否在滚动
|
|||
const userAvatar = computed(
|
||||
() => userStore.userInfo?.avatar || preferences.app.defaultAvatar,
|
||||
);
|
||||
const roleAvatar = computed(
|
||||
() => props.conversation.roleAvatar ?? '/static/gpt.svg',
|
||||
);
|
||||
|
||||
const { list } = toRefs(props); // 定义 emits
|
||||
|
||||
|
@ -121,7 +119,11 @@ onMounted(async () => {
|
|||
<!-- 左侧消息:system、assistant -->
|
||||
<div v-if="item.type !== 'user'" class="flex flex-row">
|
||||
<div class="avatar">
|
||||
<Avatar :src="roleAvatar" />
|
||||
<Avatar
|
||||
v-if="conversation.roleAvatar"
|
||||
:src="conversation.roleAvatar"
|
||||
/>
|
||||
<SvgGptIcon v-else class="size-8" />
|
||||
</div>
|
||||
<div class="mx-[15px] flex flex-col text-left">
|
||||
<div class="text-left leading-[30px]">
|
||||
|
@ -142,7 +144,7 @@ onMounted(async () => {
|
|||
type="text"
|
||||
@click="copyContent(item.content)"
|
||||
>
|
||||
<img class="h-[20px]" src="/static/copy.svg" />
|
||||
<IconifyIcon icon="lucide:copy" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="item.id > 0"
|
||||
|
@ -150,7 +152,7 @@ onMounted(async () => {
|
|||
type="text"
|
||||
@click="onDelete(item.id)"
|
||||
>
|
||||
<img class="h-[17px]" src="/static/delete.svg" />
|
||||
<IconifyIcon icon="lucide:trash" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -178,28 +180,28 @@ onMounted(async () => {
|
|||
type="text"
|
||||
@click="copyContent(item.content)"
|
||||
>
|
||||
<img class="h-[20px]" src="/static/copy.svg" />
|
||||
<IconifyIcon icon="lucide:copy" />
|
||||
</Button>
|
||||
<Button
|
||||
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||
type="text"
|
||||
@click="onDelete(item.id)"
|
||||
>
|
||||
<img class="h-[17px]" src="/static/delete.svg" />
|
||||
<IconifyIcon icon="lucide:trash" />
|
||||
</Button>
|
||||
<Button
|
||||
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||
type="text"
|
||||
@click="onRefresh(item)"
|
||||
>
|
||||
<span class="icon-[ant-design--redo-outlined]"></span>
|
||||
<IconifyIcon icon="lucide:refresh-cw" />
|
||||
</Button>
|
||||
<Button
|
||||
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||
type="text"
|
||||
@click="onEdit(item)"
|
||||
>
|
||||
<span class="icon-[ant-design--form-outlined]"></span>
|
||||
<IconifyIcon icon="lucide:edit" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -214,7 +216,7 @@ onMounted(async () => {
|
|||
@click="handleGoBottom"
|
||||
>
|
||||
<Button shape="circle">
|
||||
<span class="icon-[ant-design--down-outlined]"></span>
|
||||
<IconifyIcon icon="lucide:chevron-down" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -27,7 +27,7 @@ async function handleCategoryClick(category: string) {
|
|||
<template>
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div
|
||||
class="mr-[10px] flex flex-row"
|
||||
class="mr-2 flex flex-row"
|
||||
v-for="category in categoryList"
|
||||
:key="category"
|
||||
>
|
||||
|
|
|
@ -86,19 +86,19 @@ async function handleTabsScroll() {
|
|||
<div v-if="showMore" class="absolute right-[12px] top-0">
|
||||
<Dropdown>
|
||||
<Button type="text">
|
||||
<span class="icon-[ant-design--more-outlined] text-2xl"></span>
|
||||
<IconifyIcon icon="lucide:ellipsis-vertical" />
|
||||
</Button>
|
||||
<template #overlay>
|
||||
<Menu>
|
||||
<Menu.Item @click="handleMoreClick(['edit', role])">
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ep:edit" color="#787878" />
|
||||
<IconifyIcon icon="lucide:edit" color="#787878" />
|
||||
<span>编辑</span>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item @click="handleMoreClick(['delete', role])">
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ep:delete" color="red" />
|
||||
<IconifyIcon icon="lucide:trash" color="red" />
|
||||
<span class="text-red-500">编辑</span>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useRouter } from 'vue-router';
|
|||
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Button, Input, Layout, TabPane, Tabs } from 'ant-design-vue';
|
||||
import { Button, Input, Layout, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { createChatConversationMy } from '#/api/ai/chat/conversation';
|
||||
import { deleteMy, getCategoryList, getMyPage } from '#/api/ai/model/chatRole';
|
||||
|
@ -194,7 +194,7 @@ onMounted(async () => {
|
|||
@click="handlerAddRole"
|
||||
class="ml-[20px]"
|
||||
>
|
||||
<IconifyIcon icon="ep:user" style="margin-right: 5px" />
|
||||
<IconifyIcon icon="lucide:user" style="margin-right: 5px" />
|
||||
添加角色
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -205,7 +205,7 @@ onMounted(async () => {
|
|||
class="relative h-full p-4"
|
||||
@tab-click="handleTabsClick"
|
||||
>
|
||||
<TabPane
|
||||
<Tabs.TabPane
|
||||
key="my-role"
|
||||
class="flex h-full flex-col overflow-y-auto"
|
||||
tab="我的角色"
|
||||
|
@ -220,9 +220,9 @@ onMounted(async () => {
|
|||
@on-page="handlerCardPage('my')"
|
||||
class="mt-[20px]"
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<TabPane
|
||||
<Tabs.TabPane
|
||||
key="public-role"
|
||||
class="flex h-full flex-col overflow-y-auto"
|
||||
tab="公共角色"
|
||||
|
@ -242,7 +242,7 @@ onMounted(async () => {
|
|||
class="mt-[20px]"
|
||||
loading
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
|
|
|
@ -521,32 +521,21 @@ onMounted(async () => {
|
|||
<Button
|
||||
type="primary"
|
||||
ghost
|
||||
class="mr-[10px] px-[10px]"
|
||||
class="mr-2 px-2"
|
||||
size="small"
|
||||
@click="openChatConversationUpdateForm"
|
||||
>
|
||||
<span v-html="activeConversation?.modelName"></span>
|
||||
<IconifyIcon icon="ep:setting" class="ml-[10px]" />
|
||||
<IconifyIcon icon="lucide:settings" class="ml-2" />
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
class="mr-[10px] px-[10px]"
|
||||
@click="handlerMessageClear"
|
||||
>
|
||||
<IconifyIcon
|
||||
icon="heroicons-outline:archive-box-x-mark"
|
||||
color="#787878"
|
||||
/>
|
||||
<Button size="small" class="mr-2 px-2" @click="handlerMessageClear">
|
||||
<IconifyIcon icon="lucide:trash" color="#787878" />
|
||||
</Button>
|
||||
<Button size="small" class="mr-[10px] px-[10px]">
|
||||
<IconifyIcon icon="ep:download" color="#787878" />
|
||||
<Button size="small" class="mr-2 px-2">
|
||||
<IconifyIcon icon="lucide:download" color="#787878" />
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
class="mr-[10px] px-[10px]"
|
||||
@click="handleGoTopMessage"
|
||||
>
|
||||
<IconifyIcon icon="ep:top" color="#787878" />
|
||||
<Button size="small" class="mr-2 px-2" @click="handleGoTopMessage">
|
||||
<IconifyIcon icon="lucide:arrow-up" color="#787878" />
|
||||
</Button>
|
||||
</div>
|
||||
</Layout.Header>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ref } from 'vue';
|
|||
|
||||
import { DocAlert, Page } from '@vben/common-ui';
|
||||
|
||||
import { Card, TabPane, Tabs } from 'ant-design-vue';
|
||||
import { Card, Tabs } from 'ant-design-vue';
|
||||
|
||||
import ChatConversationList from './modules/ChatConversationList.vue';
|
||||
import ChatMessageList from './modules/ChatMessageList.vue';
|
||||
|
@ -18,12 +18,12 @@ const activeTabName = ref('conversation');
|
|||
</template>
|
||||
<Card>
|
||||
<Tabs v-model:active-key="activeTabName">
|
||||
<TabPane tab="对话列表" key="conversation">
|
||||
<Tabs.TabPane tab="对话列表" key="conversation">
|
||||
<ChatConversationList />
|
||||
</TabPane>
|
||||
<TabPane tab="消息列表" key="message">
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="消息列表" key="message">
|
||||
<ChatMessageList />
|
||||
</TabPane>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</Page>
|
||||
|
|
|
@ -87,9 +87,10 @@ onMounted(async () => {
|
|||
<TableAction :actions="[]" />
|
||||
</template>
|
||||
<template #userId="{ row }">
|
||||
<span>{{
|
||||
userList.find((item) => item.id === row.userId)?.nickname
|
||||
}}</span>
|
||||
<span>
|
||||
{{ userList.find((item) => item.id === row.userId)?.nickname }}
|
||||
</span>
|
||||
<span v-if="row.userId === 0">系统</span>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
|
|
|
@ -6,6 +6,7 @@ import type { AiImageApi } from '#/api/ai/image';
|
|||
import { onMounted, ref, toRefs, watch } from 'vue';
|
||||
|
||||
import { confirm } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Button, Card, Image, message } from 'ant-design-vue';
|
||||
|
||||
|
@ -86,28 +87,28 @@ onMounted(async () => {
|
|||
type="text"
|
||||
@click="handleButtonClick('download', detail)"
|
||||
>
|
||||
<span class="icon-[ant-design--download-outlined]"></span>
|
||||
<IconifyIcon icon="lucide:download" />
|
||||
</Button>
|
||||
<Button
|
||||
class="m-0 p-[10px]"
|
||||
type="text"
|
||||
@click="handleButtonClick('regeneration', detail)"
|
||||
>
|
||||
<span class="icon-[ant-design--redo-outlined]"></span>
|
||||
<IconifyIcon icon="lucide:refresh-cw" />
|
||||
</Button>
|
||||
<Button
|
||||
class="m-0 p-[10px]"
|
||||
type="text"
|
||||
@click="handleButtonClick('delete', detail)"
|
||||
>
|
||||
<span class="icon-[ant-design--delete-outlined]"></span>
|
||||
<IconifyIcon icon="lucide:trash" />
|
||||
</Button>
|
||||
<Button
|
||||
class="m-0 p-[10px]"
|
||||
type="text"
|
||||
@click="handleButtonClick('more', detail)"
|
||||
>
|
||||
<span class="icon-[ant-design--more-outlined]"></span>
|
||||
<IconifyIcon icon="lucide:ellipsis-vertical" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -92,7 +92,9 @@ onMounted(async () => {
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 绘图创作" url="https://doc.iocoder.cn/ai/image/" />
|
||||
</template>
|
||||
<Grid table-title="绘画管理列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction :actions="[]" />
|
||||
|
@ -101,9 +103,9 @@ onMounted(async () => {
|
|||
<Image :src="row.picUrl" class="h-80px w-80px" />
|
||||
</template>
|
||||
<template #userId="{ row }">
|
||||
<span>{{
|
||||
userList.find((item) => item.id === row.userId)?.nickname
|
||||
}}</span>
|
||||
<span>
|
||||
{{ userList.find((item) => item.id === row.userId)?.nickname }}
|
||||
</span>
|
||||
</template>
|
||||
<template #publicStatus="{ row }">
|
||||
<Switch
|
||||
|
|
|
@ -45,7 +45,7 @@ onMounted(async () => {
|
|||
</script>
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="bg-[#fff] p-[20px]">
|
||||
<div class="bg-white p-[20px]">
|
||||
<!-- TODO @fan:Search 可以换成 Icon 组件么? -->
|
||||
<Input.Search
|
||||
v-model="queryParams.prompt"
|
||||
|
@ -55,14 +55,14 @@ onMounted(async () => {
|
|||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<div
|
||||
class="grid gap-[10px] bg-[#fff] shadow-[0_0_10px_rgba(0,0,0,0.1)]"
|
||||
class="grid gap-[10px] bg-white shadow-[0_0_10px_rgba(0,0,0,0.1)]"
|
||||
style="grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))"
|
||||
>
|
||||
<!-- TODO @fan:这个图片的风格,要不和 ImageCard.vue 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ -->
|
||||
<div
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="relative cursor-pointer overflow-hidden bg-[#f0f0f0] transition-transform duration-300 hover:scale-[1.05]"
|
||||
class="relative cursor-pointer overflow-hidden bg-white transition-transform duration-300 hover:scale-[1.05]"
|
||||
>
|
||||
<img
|
||||
:src="item.picUrl"
|
||||
|
|
|
@ -122,10 +122,10 @@ onBeforeUnmount(() => {
|
|||
>
|
||||
<!-- 文件图标和名称 -->
|
||||
<div class="mr-[10px] flex min-w-[200px] items-center">
|
||||
<IconifyIcon icon="ep:document" class="mr-8px text-[#409eff]" />
|
||||
<span class="break-all text-[13px] text-[#303133]">{{
|
||||
file.name
|
||||
}}</span>
|
||||
<IconifyIcon icon="lucide:file-text" class="mr-8px text-[#409eff]" />
|
||||
<span class="break-all text-[13px] text-[#303133]">
|
||||
{{ file.name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 处理进度 -->
|
||||
|
|
|
@ -176,7 +176,10 @@ onMounted(async () => {
|
|||
<template #title>
|
||||
系统会自动将文档内容分割成多个段落,您可以根据需要调整分段方式和内容。
|
||||
</template>
|
||||
<IconifyIcon icon="ep:warning" class="ml-[5px] text-gray-400" />
|
||||
<IconifyIcon
|
||||
icon="lucide:circle-alert"
|
||||
class="ml-[5px] text-gray-400"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -206,7 +209,7 @@ onMounted(async () => {
|
|||
trigger="click"
|
||||
>
|
||||
<div class="flex cursor-pointer items-center">
|
||||
<IconifyIcon icon="ep:document" class="text-danger mr-[5px]" />
|
||||
<IconifyIcon icon="lucide:file-text" class="text-danger mr-[5px]" />
|
||||
<span>{{ currentFile?.name || '请选择文件' }}</span>
|
||||
<span
|
||||
v-if="currentFile?.segments"
|
||||
|
@ -214,7 +217,7 @@ onMounted(async () => {
|
|||
>
|
||||
({{ currentFile.segments.length }}个分片)
|
||||
</span>
|
||||
<IconifyIcon icon="ep:arrow-down" class="ml-[5px]" />
|
||||
<IconifyIcon icon="lucide:chevron-down" class="ml-[5px]" />
|
||||
</div>
|
||||
<template #overlay>
|
||||
<Menu>
|
||||
|
@ -244,7 +247,7 @@ onMounted(async () => {
|
|||
v-if="splitLoading"
|
||||
class="flex items-center justify-center py-[20px]"
|
||||
>
|
||||
<IconifyIcon icon="ep:loading" class="is-loading" />
|
||||
<IconifyIcon icon="lucide:loader" class="is-loading" />
|
||||
<span class="ml-[10px]">正在加载分段内容...</span>
|
||||
</div>
|
||||
<template
|
||||
|
|
|
@ -231,7 +231,10 @@ onMounted(() => {
|
|||
class="flex items-center justify-between rounded-sm border-l-4 border-l-[#409eff] px-[12px] py-[4px] shadow-sm transition-all duration-300 hover:bg-[#ecf5ff]"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ep:document" class="mr-[8px] text-[#409eff]" />
|
||||
<IconifyIcon
|
||||
icon="lucide:file-text"
|
||||
class="mr-[8px] text-[#409eff]"
|
||||
/>
|
||||
<span class="break-all text-[13px] text-[#303133]">{{
|
||||
file.name
|
||||
}}</span>
|
||||
|
@ -243,7 +246,7 @@ onMounted(() => {
|
|||
@click="removeFile(index)"
|
||||
class="ml-2"
|
||||
>
|
||||
<IconifyIcon icon="ep:delete" />
|
||||
<IconifyIcon icon="lucide:trash" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -104,7 +104,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
|
||||
</template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="AI 知识库列表">
|
||||
<template #toolbar-tools>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { onMounted, reactive, ref } from 'vue';
|
|||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
|
@ -183,14 +184,18 @@ onMounted(() => {
|
|||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-13 flex items-center text-gray-500">
|
||||
<span class="ep:document mr-5"></span>
|
||||
<IconifyIcon icon="lucide:file-text" class="mr-5" />
|
||||
<span>{{ segment.documentName || '未知文档' }}</span>
|
||||
</div>
|
||||
<Button size="small" @click="toggleExpand(segment)">
|
||||
{{ segment.expanded ? '收起' : '展开' }}
|
||||
<span
|
||||
class="mr-5"
|
||||
:class="segment.expanded ? 'ep:arrow-up' : 'ep:arrow-down'"
|
||||
:class="
|
||||
segment.expanded
|
||||
? 'lucide:chevron-up'
|
||||
: 'lucide:chevron-down'
|
||||
"
|
||||
></span>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import {
|
||||
MarkdownIt,
|
||||
Markmap,
|
||||
|
@ -126,7 +127,7 @@ defineExpose({
|
|||
<Button type="primary" size="small" class="flex" @click="downloadImage">
|
||||
<template #icon>
|
||||
<div class="flex items-center justify-center">
|
||||
<span class="icon-[ant-design--copy-twotone]"></span>
|
||||
<IconifyIcon icon="lucide:copy" />
|
||||
</div>
|
||||
</template>
|
||||
下载图片
|
||||
|
|
|
@ -90,7 +90,9 @@ onMounted(async () => {
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 思维导图" url="https://doc.iocoder.cn/ai/mindmap/" />
|
||||
</template>
|
||||
<Drawer class="w-[800px]">
|
||||
<Right
|
||||
v-if="previewVisible"
|
||||
|
@ -105,9 +107,9 @@ onMounted(async () => {
|
|||
<TableAction :actions="[]" />
|
||||
</template>
|
||||
<template #userId="{ row }">
|
||||
<span>{{
|
||||
userList.find((item) => item.id === row.userId)?.nickname
|
||||
}}</span>
|
||||
<span>
|
||||
{{ userList.find((item) => item.id === row.userId)?.nickname }}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
|
|
|
@ -83,7 +83,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
|
||||
</template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="API 密钥列表">
|
||||
<template #toolbar-tools>
|
||||
|
|
|
@ -83,7 +83,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 对话聊天" url="https://doc.iocoder.cn/ai/chat/" />
|
||||
</template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="聊天角色列表">
|
||||
<template #toolbar-tools>
|
||||
|
|
|
@ -92,7 +92,9 @@ onMounted(async () => {
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
|
||||
</template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="模型配置列表">
|
||||
<template #toolbar-tools>
|
||||
|
|
|
@ -83,10 +83,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="AI 工具调用(function calling)"
|
||||
url="https://doc.iocoder.cn/ai/tool/"
|
||||
/>
|
||||
</template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="工具列表">
|
||||
<template #toolbar-tools>
|
||||
|
|
|
@ -92,7 +92,9 @@ onMounted(async () => {
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 音乐创作" url="https://doc.iocoder.cn/ai/music/" />
|
||||
</template>
|
||||
<Grid table-title="音乐管理列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction :actions="[]" />
|
||||
|
|
|
@ -75,7 +75,9 @@ onMounted(async () => {
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="AI 写作助手" url="https://doc.iocoder.cn/ai/write/" />
|
||||
</template>
|
||||
<Grid table-title="写作管理列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction :actions="[]" />
|
||||
|
|
|
@ -160,7 +160,9 @@ async function getApprovalDetail() {
|
|||
: [];
|
||||
}
|
||||
}
|
||||
} finally {}
|
||||
} finally {
|
||||
//
|
||||
}
|
||||
}
|
||||
/** 审批相关:选择发起人 */
|
||||
function selectUserConfirm(id: string, userList: any[]) {
|
||||
|
|
|
@ -111,10 +111,12 @@ function onRefresh() {
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="审批接入(业务表单)"
|
||||
url="https://doc.iocoder.cn/bpm/use-business-form/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<Grid table-title="请假列表">
|
||||
<template #toolbar-tools>
|
||||
|
|
|
@ -102,10 +102,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="流程发起、取消、重新发起"
|
||||
url="https://doc.iocoder.cn/bpm/process-instance"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<Grid table-title="流程状态">
|
||||
<!-- 摘要 -->
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { SystemUserApi } from '#/api/system/user';
|
|||
import { computed, onMounted, ref, watchEffect } from 'vue';
|
||||
|
||||
import { DocAlert, Page } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
|
@ -266,7 +267,7 @@ onMounted(async () => {
|
|||
@click="handlerSend"
|
||||
>
|
||||
<template #icon>
|
||||
<span class="i-ant-design:send-outlined mr-1"></span>
|
||||
<IconifyIcon icon="lucide:send-horizontal" />
|
||||
</template>
|
||||
发送消息
|
||||
</Button>
|
||||
|
@ -276,7 +277,10 @@ onMounted(async () => {
|
|||
<Card :bordered="false" class="w-full md:w-1/2">
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<span class="i-ant-design:message-outlined mr-2 text-lg"></span>
|
||||
<IconifyIcon
|
||||
icon="lucide:message-circle-more"
|
||||
class="mr-2 text-lg"
|
||||
/>
|
||||
<span class="text-lg font-medium">消息记录</span>
|
||||
<Tag v-if="messageList.length > 0" class="ml-2">
|
||||
{{ messageList.length }} 条
|
||||
|
@ -294,16 +298,16 @@ onMounted(async () => {
|
|||
<div class="mb-1 flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Badge :color="getMessageBadgeColor(msg.type)" />
|
||||
<span class="ml-1 font-medium text-gray-600">{{
|
||||
getMessageTypeText(msg.type)
|
||||
}}</span>
|
||||
<span class="ml-1 font-medium text-gray-600">
|
||||
{{ getMessageTypeText(msg.type) }}
|
||||
</span>
|
||||
<span v-if="msg.userId" class="ml-2 text-gray-500">
|
||||
用户 ID: {{ msg.userId }}
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-xs text-gray-400">{{
|
||||
formatDate(msg.time)
|
||||
}}</span>
|
||||
<span class="text-xs text-gray-400">
|
||||
{{ formatDate(msg.time) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-2 break-words text-gray-800">
|
||||
{{ msg.text }}
|
||||
|
|
|
@ -91,6 +91,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="支付宝支付接入"
|
||||
url="https://doc.iocoder.cn/pay/alipay-pay-demo/"
|
||||
|
@ -107,6 +108,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
title="微信小程序支付接入"
|
||||
url="https://doc.iocoder.cn/pay/wx-lite-pay-demo/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="示例订单列表">
|
||||
|
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
@ -12,6 +12,9 @@ const SvgBellIcon = createIconifyIcon('svg:bell');
|
|||
const SvgCakeIcon = createIconifyIcon('svg:cake');
|
||||
const SvgAntdvLogoIcon = createIconifyIcon('svg:antdv-logo');
|
||||
|
||||
/** AI */
|
||||
const SvgGptIcon = createIconifyIcon('svg:gpt');
|
||||
|
||||
/** 支付 */
|
||||
const SvgAlipayPcIcon = createIconifyIcon('svg:alipay-pc');
|
||||
const SvgAlipayWapIcon = createIconifyIcon('svg:alipay-wap');
|
||||
|
@ -41,6 +44,7 @@ export {
|
|||
SvgCakeIcon,
|
||||
SvgCardIcon,
|
||||
SvgDownloadIcon,
|
||||
SvgGptIcon,
|
||||
SvgMockIcon,
|
||||
SvgWalletIcon,
|
||||
SvgWxAppIcon,
|
||||
|
|