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 |
@@ -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="1716345268026" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5622" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M956.408445 419.226665a250.670939 250.670939 0 0 0-22.425219-209.609236A263.163526 263.163526 0 0 0 652.490412 85.715535 259.784384 259.784384 0 0 0 457.728923 0.008192a261.422756 261.422756 0 0 0-249.44216 178.582564 258.453206 258.453206 0 0 0-172.848261 123.901894c-57.03583 96.868753-44.031251 219.132275 32.153053 302.279661a250.670939 250.670939 0 0 0 22.32282 209.609237 263.163526 263.163526 0 0 0 281.595213 123.901893A259.067596 259.067596 0 0 0 566.271077 1023.990784a260.60357 260.60357 0 0 0 249.339762-178.889759 258.453206 258.453206 0 0 0 172.848261-123.901893c57.445423-96.868753 44.13365-218.82508-32.050655-302.074865zM566.578272 957.124721c-45.362429 0-89.496079-15.666934-124.516283-44.543243 1.638372-0.921584 4.198329-2.150363 6.143895-3.481541l206.537289-117.757998a32.35785 32.35785 0 0 0 16.895713-29.081105V474.82892l87.243317 49.97035c1.023983 0.307195 1.638372 1.228779 1.638372 2.252762v238.075953c0 105.8798-86.936122 191.689541-193.942303 191.996736zM148.588578 781.102113a189.846373 189.846373 0 0 1-23.346803-128.612213c1.535974 1.023983 4.09593 2.559956 6.143895 3.48154L337.922959 773.729439c10.444622 6.143896 23.346803 6.143896 34.098621 0l252.30931-143.664758v99.531108c0 1.023983-0.307195 1.945567-1.331177 2.559956l-208.892449 118.986778a196.297463 196.297463 0 0 1-265.518686-70.04041zM94.112704 335.97688c22.630015-39.013737 58.367008-68.81163 101.16948-84.171369V494.591784c0 11.7758 6.45109 22.93721 16.793315 28.978707l252.30931 143.767156L377.141493 716.796006a3.174346 3.174346 0 0 1-2.867152 0.307195l-208.892448-118.986777A190.870355 190.870355 0 0 1 94.215102 335.874482z m717.607001 164.861198L559.410394 357.070922 646.653711 307.20297a3.174346 3.174346 0 0 1 2.969549-0.307195l208.892449 118.986777a190.358364 190.358364 0 0 1 70.961994 262.139544 194.556693 194.556693 0 0 1-101.16948 84.171369V529.407192a31.538664 31.538664 0 0 0-16.588518-28.671513z m87.03852-129.329002c-1.74077-1.023983-4.300727-2.559956-6.246294-3.48154l-206.639687-117.757999a34.09862 34.09862 0 0 0-33.996222 0L399.566711 393.934295v-99.531108c0-1.023983 0.307195-1.945567 1.331178-2.559956l208.892449-119.089176a195.990268 195.990268 0 0 1 265.518686 70.450003c22.732414 38.706542 31.129071 84.171369 23.346803 128.305018zM352.258716 548.862861l-87.243317-49.560757a2.457558 2.457558 0 0 1-1.638372-2.252762V258.870991c0-105.8798 87.243317-191.996736 194.556692-191.689541a194.556693 194.556693 0 0 1 124.209089 44.543243c-1.638372 0.921584-4.198329 2.252762-6.143896 3.48154l-206.639687 117.757999a31.948257 31.948257 0 0 0-16.793315 29.081105l-0.307194 286.715126z m47.307995-100.759887L512 384.001664l112.535687 63.998912v127.997824l-112.228492 63.998912-112.535687-63.998912-0.307195-127.997824z" p-id="5623" fill="#707070"></path></svg>
|
||||
|
Before Width: | Height: | Size: 3.0 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>
|
||||
<DocAlert title="AI 绘图创作" url="https://doc.iocoder.cn/ai/image/" />
|
||||
<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>
|
||||
<DocAlert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
|
||||
<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>
|
||||
<DocAlert title="AI 思维导图" url="https://doc.iocoder.cn/ai/mindmap/" />
|
||||
<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>
|
||||
<DocAlert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
|
||||
<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>
|
||||
<DocAlert title="AI 对话聊天" url="https://doc.iocoder.cn/ai/chat/" />
|
||||
<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>
|
||||
<DocAlert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
|
||||
<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>
|
||||
<DocAlert
|
||||
title="AI 工具调用(function calling)"
|
||||
url="https://doc.iocoder.cn/ai/tool/"
|
||||
/>
|
||||
<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>
|
||||
<DocAlert title="AI 音乐创作" url="https://doc.iocoder.cn/ai/music/" />
|
||||
<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>
|
||||
<DocAlert title="AI 写作助手" url="https://doc.iocoder.cn/ai/write/" />
|
||||
<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>
|
||||
<DocAlert
|
||||
title="审批接入(业务表单)"
|
||||
url="https://doc.iocoder.cn/bpm/use-business-form/"
|
||||
/>
|
||||
<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>
|
||||
<DocAlert
|
||||
title="流程发起、取消、重新发起"
|
||||
url="https://doc.iocoder.cn/bpm/process-instance"
|
||||
/>
|
||||
<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,22 +91,24 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<DocAlert
|
||||
title="支付宝支付接入"
|
||||
url="https://doc.iocoder.cn/pay/alipay-pay-demo/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="支付宝、微信退款接入"
|
||||
url="https://doc.iocoder.cn/pay/refund-demo/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="微信公众号支付接入"
|
||||
url="https://doc.iocoder.cn/pay/wx-pub-pay-demo/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="微信小程序支付接入"
|
||||
url="https://doc.iocoder.cn/pay/wx-lite-pay-demo/"
|
||||
/>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="支付宝支付接入"
|
||||
url="https://doc.iocoder.cn/pay/alipay-pay-demo/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="支付宝、微信退款接入"
|
||||
url="https://doc.iocoder.cn/pay/refund-demo/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="微信公众号支付接入"
|
||||
url="https://doc.iocoder.cn/pay/wx-pub-pay-demo/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="微信小程序支付接入"
|
||||
url="https://doc.iocoder.cn/pay/wx-lite-pay-demo/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="示例订单列表">
|
||||
|
||||