refactor(web-antd): 修正 Tinyflow 组件中的导入路径
- 将 './ui/typing' 修改为 './ui/typeing'(可能是原代码中的拼写错误) - 将 './ui/typing' 修改为 './ui/index',以符合常见的模块入口文件结构
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Item } from './ui/typing';
|
import type { Item } from './ui/typeing';
|
||||||
|
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
import { Tinyflow as TinyflowNative } from './ui/typing';
|
import { Tinyflow as TinyflowNative } from './ui/index';
|
||||||
|
|
||||||
import './ui/index.css';
|
import './ui/index.css';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,55 @@
|
|||||||
import { Edge, Node as Node_2, useSvelteFlow, Viewport } from '@xyflow/svelte';
|
|
||||||
|
|
||||||
export declare type Item = {
|
export declare type Item = {
|
||||||
children?: Item[];
|
children?: Item[];
|
||||||
label: string;
|
label: string;
|
||||||
value: number | string;
|
value: number | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Position = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Viewport = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
zoom: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Node = {
|
||||||
|
data?: Record<string, any>;
|
||||||
|
draggable?: boolean;
|
||||||
|
height?: number;
|
||||||
|
id: string;
|
||||||
|
position: Position;
|
||||||
|
selected?: boolean;
|
||||||
|
type?: string;
|
||||||
|
width?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Edge = {
|
||||||
|
animated?: boolean;
|
||||||
|
id: string;
|
||||||
|
label?: string;
|
||||||
|
source: string;
|
||||||
|
target: string;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
export type TinyflowData = Partial<{
|
||||||
|
edges: Edge[];
|
||||||
|
nodes: Node[];
|
||||||
|
viewport: Viewport;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export declare type TinyflowOptions = {
|
||||||
|
data?: TinyflowData;
|
||||||
|
element: Element | string;
|
||||||
|
provider?: {
|
||||||
|
internal?: () => Item[] | Promise<Item[]>;
|
||||||
|
knowledge?: () => Item[] | Promise<Item[]>;
|
||||||
|
llm?: () => Item[] | Promise<Item[]>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export declare class Tinyflow {
|
export declare class Tinyflow {
|
||||||
private _init;
|
private _init;
|
||||||
private _setOptions;
|
private _setOptions;
|
||||||
@@ -16,25 +60,11 @@ export declare class Tinyflow {
|
|||||||
destroy(): void;
|
destroy(): void;
|
||||||
getData(): {
|
getData(): {
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
nodes: Node_2[];
|
nodes: Node[];
|
||||||
viewport: Viewport;
|
viewport: Viewport;
|
||||||
};
|
};
|
||||||
getOptions(): TinyflowOptions;
|
getOptions(): TinyflowOptions;
|
||||||
setData(data: TinyflowData): void;
|
setData(data: TinyflowData): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare type TinyflowData = Partial<
|
|
||||||
ReturnType<ReturnType<typeof useSvelteFlow>['toObject']>
|
|
||||||
>;
|
|
||||||
|
|
||||||
export declare type TinyflowOptions = {
|
|
||||||
data?: TinyflowData;
|
|
||||||
element: Element | string;
|
|
||||||
provider?: {
|
|
||||||
internal?: () => Item[] | Promise<Item[]>;
|
|
||||||
knowledge?: () => Item[] | Promise<Item[]>;
|
|
||||||
llm?: () => Item[] | Promise<Item[]>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
98
apps/web-antd/src/utils/download.ts
Normal file
98
apps/web-antd/src/utils/download.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
const download0 = (data: Blob, fileName: string, mineType: string) => {
|
||||||
|
// 创建 blob
|
||||||
|
const blob = new Blob([data], { type: mineType });
|
||||||
|
// 创建 href 超链接,点击进行下载
|
||||||
|
window.URL = window.URL || window.webkitURL;
|
||||||
|
const href = URL.createObjectURL(blob);
|
||||||
|
const downA = document.createElement('a');
|
||||||
|
downA.href = href;
|
||||||
|
downA.download = fileName;
|
||||||
|
downA.click();
|
||||||
|
// 销毁超连接
|
||||||
|
window.URL.revokeObjectURL(href);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const download = {
|
||||||
|
// 下载 Excel 方法
|
||||||
|
excel: (data: Blob, fileName: string) => {
|
||||||
|
download0(data, fileName, 'application/vnd.ms-excel');
|
||||||
|
},
|
||||||
|
// 下载 Word 方法
|
||||||
|
word: (data: Blob, fileName: string) => {
|
||||||
|
download0(data, fileName, 'application/msword');
|
||||||
|
},
|
||||||
|
// 下载 Zip 方法
|
||||||
|
zip: (data: Blob, fileName: string) => {
|
||||||
|
download0(data, fileName, 'application/zip');
|
||||||
|
},
|
||||||
|
// 下载 Html 方法
|
||||||
|
html: (data: Blob, fileName: string) => {
|
||||||
|
download0(data, fileName, 'text/html');
|
||||||
|
},
|
||||||
|
// 下载 Markdown 方法
|
||||||
|
markdown: (data: Blob, fileName: string) => {
|
||||||
|
download0(data, fileName, 'text/markdown');
|
||||||
|
},
|
||||||
|
// 下载 Json 方法
|
||||||
|
json: (data: Blob, fileName: string) => {
|
||||||
|
download0(data, fileName, 'application/json');
|
||||||
|
},
|
||||||
|
// 下载图片(允许跨域)
|
||||||
|
image: ({
|
||||||
|
url,
|
||||||
|
canvasWidth,
|
||||||
|
canvasHeight,
|
||||||
|
drawWithImageSize = true,
|
||||||
|
}: {
|
||||||
|
canvasHeight?: number; // 指定画布高度
|
||||||
|
canvasWidth?: number; // 指定画布宽度
|
||||||
|
drawWithImageSize?: boolean; // 将图片绘制在画布上时带上图片的宽高值, 默认是要带上的
|
||||||
|
url: string;
|
||||||
|
}) => {
|
||||||
|
const image = new Image();
|
||||||
|
// image.setAttribute('crossOrigin', 'anonymous')
|
||||||
|
image.src = url;
|
||||||
|
image.addEventListener('load', () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = canvasWidth || image.width;
|
||||||
|
canvas.height = canvasHeight || image.height;
|
||||||
|
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||||
|
ctx?.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
if (drawWithImageSize) {
|
||||||
|
ctx.drawImage(image, 0, 0, image.width, image.height);
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(image, 0, 0);
|
||||||
|
}
|
||||||
|
const url = canvas.toDataURL('image/png');
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'image.png';
|
||||||
|
a.click();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
base64ToFile: (base64: any, fileName: string) => {
|
||||||
|
// 将base64按照 , 进行分割 将前缀 与后续内容分隔开
|
||||||
|
const data = base64.split(',');
|
||||||
|
// 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
|
||||||
|
const type = data[0].match(/:(.*?);/)[1];
|
||||||
|
// 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
|
||||||
|
const suffix = type.split('/')[1];
|
||||||
|
// 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
|
||||||
|
const bstr = window.atob(data[1]);
|
||||||
|
// 获取解码结果字符串的长度
|
||||||
|
let n = bstr.length;
|
||||||
|
// 根据解码结果字符串的长度创建一个等长的整形数字数组
|
||||||
|
// 但在创建时 所有元素初始值都为 0
|
||||||
|
const u8arr = new Uint8Array(n);
|
||||||
|
// 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
|
||||||
|
while (n--) {
|
||||||
|
// charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将File文件对象返回给方法的调用者
|
||||||
|
return new File([u8arr], `${fileName}.${suffix}`, {
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -303,23 +303,26 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Layout.Sider width="260px" class="conversation-container h-full">
|
<Layout.Sider
|
||||||
|
width="260px"
|
||||||
|
class="conversation-container relative flex h-full flex-col justify-between overflow-hidden bg-[hsl(var(--primary-foreground))!important] p-[10px_10px_0]"
|
||||||
|
>
|
||||||
<Drawer />
|
<Drawer />
|
||||||
<!-- 左顶部:对话 -->
|
<!-- 左顶部:对话 -->
|
||||||
<div class="flex h-full" style="flex-direction: column">
|
<div class="flex h-full flex-col">
|
||||||
<Button
|
<Button
|
||||||
class="w-1/1 btn-new-conversation"
|
class="btn-new-conversation h-[38px] w-full"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="createConversation"
|
@click="createConversation"
|
||||||
>
|
>
|
||||||
<IconifyIcon icon="ep:plus" class="mr-[5px]" />
|
<IconifyIcon icon="ep:plus" class="mr-[5px]" />
|
||||||
新建对话
|
新建对话
|
||||||
</Button>
|
</Button>
|
||||||
<!-- 左顶部:搜索对话 -->
|
|
||||||
<Input
|
<Input
|
||||||
v-model:value="searchName"
|
v-model:value="searchName"
|
||||||
size="large"
|
size="large"
|
||||||
class="search-input mt-[10px]"
|
class="search-input mt-[20px]"
|
||||||
placeholder="搜索历史记录"
|
placeholder="搜索历史记录"
|
||||||
@keyup="searchConversation"
|
@keyup="searchConversation"
|
||||||
>
|
>
|
||||||
@@ -329,50 +332,57 @@ onMounted(async () => {
|
|||||||
</Input>
|
</Input>
|
||||||
|
|
||||||
<!-- 左中间:对话列表 -->
|
<!-- 左中间:对话列表 -->
|
||||||
<div class="conversation-list">
|
<div class="conversation-list mt-[10px] flex-1 overflow-auto">
|
||||||
<!-- 情况一:加载中 -->
|
<!-- 情况一:加载中 -->
|
||||||
<Empty v-if="loading" description="." v-loading="loading" />
|
<Empty v-if="loading" description="." v-loading="loading" />
|
||||||
<!-- 情况二:按照 group 分组,展示聊天会话 list 列表 -->
|
|
||||||
|
<!-- 情况二:按照 group 分组 -->
|
||||||
<div
|
<div
|
||||||
v-for="conversationKey in Object.keys(conversationMap)"
|
v-for="conversationKey in Object.keys(conversationMap)"
|
||||||
:key="conversationKey"
|
:key="conversationKey"
|
||||||
|
class=""
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="conversation-item classify-title"
|
|
||||||
v-if="conversationMap[conversationKey].length > 0"
|
v-if="conversationMap[conversationKey].length > 0"
|
||||||
|
class="conversation-item classify-title pt-[10px]"
|
||||||
>
|
>
|
||||||
<b class="mx-1">
|
<b class="mx-[4px]">
|
||||||
{{ conversationKey }}
|
{{ conversationKey }}
|
||||||
</b>
|
</b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="conversation-item"
|
|
||||||
v-for="conversation in conversationMap[conversationKey]"
|
v-for="conversation in conversationMap[conversationKey]"
|
||||||
:key="conversation.id"
|
:key="conversation.id"
|
||||||
@click="handleConversationClick(conversation.id)"
|
@click="handleConversationClick(conversation.id)"
|
||||||
@mouseover="hoverConversationId = conversation.id"
|
@mouseover="hoverConversationId = conversation.id"
|
||||||
@mouseout="hoverConversationId = null"
|
@mouseout="hoverConversationId = null"
|
||||||
|
class="conversation-item mt-[5px]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="
|
class="conversation flex cursor-pointer flex-row items-center justify-between rounded-[5px] px-[5px] leading-[30px]"
|
||||||
conversation.id === activeConversationId
|
:class="[
|
||||||
? 'conversation active'
|
conversation.id === activeConversationId ? 'bg-[#e6e6e6]' : '',
|
||||||
: 'conversation'
|
]"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<div class="title-wrapper">
|
<div class="title-wrapper flex items-center">
|
||||||
<img
|
<img
|
||||||
class="avatar"
|
class="avatar h-[25px] w-[25px] rounded-[5px]"
|
||||||
:src="conversation.roleAvatar ?? '/static/gpt.svg'"
|
:src="conversation.roleAvatar ?? '/static/gpt.svg'"
|
||||||
/>
|
/>
|
||||||
<span class="title">{{ conversation.title }}</span>
|
<span
|
||||||
|
class="title text-black/77 max-w-[150px] overflow-hidden text-ellipsis whitespace-nowrap px-[10px] py-[2px] text-[14px] font-normal"
|
||||||
|
>
|
||||||
|
{{ conversation.title }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="button-wrapper"
|
|
||||||
v-show="hoverConversationId === conversation.id"
|
v-show="hoverConversationId === conversation.id"
|
||||||
|
class="button-wrapper relative right-[2px] flex items-center text-[#606266]"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
class="btn"
|
class="btn mr-0 px-[5px]"
|
||||||
type="link"
|
type="link"
|
||||||
@click.stop="handleTop(conversation)"
|
@click.stop="handleTop(conversation)"
|
||||||
>
|
>
|
||||||
@@ -386,14 +396,14 @@ onMounted(async () => {
|
|||||||
></span>
|
></span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
class="btn"
|
class="btn mr-0 px-[5px]"
|
||||||
type="link"
|
type="link"
|
||||||
@click.stop="updateConversationTitle(conversation)"
|
@click.stop="updateConversationTitle(conversation)"
|
||||||
>
|
>
|
||||||
<IconifyIcon icon="ep:edit" />
|
<IconifyIcon icon="ep:edit" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
class="btn"
|
class="btn mr-0 px-[5px]"
|
||||||
type="link"
|
type="link"
|
||||||
@click.stop="deleteChatConversation(conversation)"
|
@click.stop="deleteChatConversation(conversation)"
|
||||||
>
|
>
|
||||||
@@ -404,143 +414,29 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 底部占位 -->
|
<!-- 底部占位 -->
|
||||||
<div class="w-100% h-[50px]"></div>
|
<div class="h-[50px] w-full"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 左底部:工具栏 -->
|
<!-- 左底部:工具栏 -->
|
||||||
<div class="tool-box">
|
<div
|
||||||
<div @click="handleRoleRepository">
|
class="tool-box absolute bottom-0 left-0 right-0 flex items-center justify-between bg-[#f4f4f4] px-[20px] leading-[35px] text-[var(--el-text-color)] shadow-[0_0_1px_1px_rgba(228,228,228,0.8)]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex cursor-pointer items-center text-[#606266]"
|
||||||
|
@click="handleRoleRepository"
|
||||||
|
>
|
||||||
<IconifyIcon icon="ep:user" />
|
<IconifyIcon icon="ep:user" />
|
||||||
<span>角色仓库</span>
|
<span class="ml-[5px]">角色仓库</span>
|
||||||
</div>
|
</div>
|
||||||
<div @click="handleClearConversation">
|
<div
|
||||||
|
class="flex cursor-pointer items-center text-[#606266]"
|
||||||
|
@click="handleClearConversation"
|
||||||
|
>
|
||||||
<IconifyIcon icon="ep:delete" />
|
<IconifyIcon icon="ep:delete" />
|
||||||
<span>清空未置顶对话</span>
|
<span class="ml-[5px]">清空未置顶对话</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout.Sider>
|
</Layout.Sider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.conversation-container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 10px 10px 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: hsl(var(--primary-foreground));
|
|
||||||
|
|
||||||
.btn-new-conversation {
|
|
||||||
width: 100%;
|
|
||||||
height: 38px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-list {
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.classify-title {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-item {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 5px;
|
|
||||||
line-height: 30px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
max-width: 150px;
|
|
||||||
padding: 2px 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: rgb(0 0 0 / 77%);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-items: center;
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对话编辑、删除
|
|
||||||
.button-wrapper {
|
|
||||||
right: 2px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
place-items: center center;
|
|
||||||
color: #606266;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 0 5px 0 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 角色仓库、清空未设置对话
|
|
||||||
.tool-box {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
//width: 100%;
|
|
||||||
padding: 0 20px;
|
|
||||||
line-height: 35px;
|
|
||||||
color: var(--el-text-color);
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
box-shadow: 0 0 1px 1px rgb(228 228 228 / 80%);
|
|
||||||
|
|
||||||
> div {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
color: #606266;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
> span {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -113,66 +113,92 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div ref="messageContainer" class="relative h-full overflow-y-auto">
|
<div ref="messageContainer" class="relative h-full overflow-y-auto">
|
||||||
<div class="chat-list" v-for="(item, index) in list" :key="index">
|
<div
|
||||||
<!-- 靠左 message:system、assistant 类型 -->
|
v-for="(item, index) in list"
|
||||||
<div class="left-message message-item" v-if="item.type !== 'user'">
|
:key="index"
|
||||||
|
class="mt-[50px] flex flex-col overflow-y-hidden px-[20px]"
|
||||||
|
>
|
||||||
|
<!-- 左侧消息:system、assistant -->
|
||||||
|
<div v-if="item.type !== 'user'" class="flex flex-row">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<Avatar :src="roleAvatar" />
|
<Avatar :src="roleAvatar" />
|
||||||
</div>
|
</div>
|
||||||
<div class="message">
|
<div class="mx-[15px] flex flex-col text-left">
|
||||||
<div>
|
<div class="text-left leading-[30px]">
|
||||||
<div class="time">{{ formatDate(item.createTime) }}</div>
|
{{ formatDate(item.createTime) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="left-text-container">
|
<div
|
||||||
<MarkdownView class="left-text" :content="item.content" />
|
class="relative flex flex-col break-words rounded-[10px] bg-[#e4e4e4cc] p-[10px] pb-[5px] pt-[10px] shadow-[0_0_0_1px_rgba(228,228,228,0.8)]"
|
||||||
|
>
|
||||||
|
<MarkdownView
|
||||||
|
class="text-[0.95rem] text-[#393939]"
|
||||||
|
:content="item.content"
|
||||||
|
/>
|
||||||
<MessageKnowledge v-if="item.segments" :segments="item.segments" />
|
<MessageKnowledge v-if="item.segments" :segments="item.segments" />
|
||||||
</div>
|
</div>
|
||||||
<div class="left-btns">
|
<div class="mt-[8px] flex flex-row">
|
||||||
<Button
|
<Button
|
||||||
class="btn-cus"
|
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||||
type="text"
|
type="text"
|
||||||
@click="copyContent(item.content)"
|
@click="copyContent(item.content)"
|
||||||
>
|
>
|
||||||
<img class="btn-image" src="/static/copy.svg" />
|
<img class="h-[20px]" src="/static/copy.svg" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
v-if="item.id > 0"
|
v-if="item.id > 0"
|
||||||
class="btn-cus"
|
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||||
type="text"
|
type="text"
|
||||||
@click="onDelete(item.id)"
|
@click="onDelete(item.id)"
|
||||||
>
|
>
|
||||||
<img class="btn-image h-[17px]" src="/static/delete.svg" />
|
<img class="h-[17px]" src="/static/delete.svg" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 靠右 message:user 类型 -->
|
|
||||||
<div class="right-message message-item" v-if="item.type === 'user'">
|
<!-- 右侧消息:user -->
|
||||||
|
<div v-else class="flex flex-row-reverse justify-start">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<Avatar :src="userAvatar" />
|
<Avatar :src="userAvatar" />
|
||||||
</div>
|
</div>
|
||||||
<div class="message">
|
<div class="mx-[15px] flex flex-col text-left">
|
||||||
<div>
|
<div class="text-left leading-[30px]">
|
||||||
<div class="time">{{ formatDate(item.createTime) }}</div>
|
{{ formatDate(item.createTime) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="right-text-container">
|
<div class="flex flex-row-reverse">
|
||||||
<div class="right-text">{{ item.content }}</div>
|
<div
|
||||||
|
class="inline w-auto whitespace-pre-wrap break-words rounded-[10px] bg-[#267fff] p-[10px] text-[0.95rem] text-white shadow-[0_0_0_1px_#267fff]"
|
||||||
|
>
|
||||||
|
{{ item.content }}
|
||||||
</div>
|
</div>
|
||||||
<div class="right-btns">
|
</div>
|
||||||
|
<div class="mt-[8px] flex flex-row-reverse">
|
||||||
<Button
|
<Button
|
||||||
class="btn-cus"
|
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||||
type="text"
|
type="text"
|
||||||
@click="copyContent(item.content)"
|
@click="copyContent(item.content)"
|
||||||
>
|
>
|
||||||
<img class="btn-image" src="/static/copy.svg" />
|
<img class="h-[20px]" src="/static/copy.svg" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="btn-cus" type="text" @click="onDelete(item.id)">
|
<Button
|
||||||
<img class="btn-image h-[17px]" src="/static/delete.svg" />
|
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" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="btn-cus" type="text" @click="onRefresh(item)">
|
<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>
|
<span class="icon-[ant-design--redo-outlined]"></span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="btn-cus" type="text" @click="onEdit(item)">
|
<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>
|
<span class="icon-[ant-design--form-outlined]"></span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,117 +206,15 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 回到底部 -->
|
|
||||||
<div v-if="isScrolling" class="to-bottom" @click="handleGoBottom">
|
<!-- 回到底部按钮 -->
|
||||||
|
<div
|
||||||
|
v-if="isScrolling"
|
||||||
|
class="absolute bottom-0 right-1/2 z-[1000]"
|
||||||
|
@click="handleGoBottom"
|
||||||
|
>
|
||||||
<Button shape="circle">
|
<Button shape="circle">
|
||||||
<span class="icon-[ant-design--down-outlined]"></span>
|
<span class="icon-[ant-design--down-outlined]"></span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
// 中间
|
|
||||||
.chat-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0 20px;
|
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
.message-item {
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-message {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-message {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0 15px;
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
.time {
|
|
||||||
line-height: 30px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-text-container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 10px 10px 5px;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
background-color: rgb(228 228 228 / 80%);
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 0 0 1px rgb(228 228 228 / 80%);
|
|
||||||
|
|
||||||
.left-text {
|
|
||||||
font-size: 0.95rem;
|
|
||||||
color: #393939;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-text-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
|
|
||||||
.right-text {
|
|
||||||
display: inline;
|
|
||||||
width: auto;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
color: #fff;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
background-color: #267fff;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 0 0 1px #267fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-btns {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-btns {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 复制、删除按钮
|
|
||||||
.btn-cus {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 5px;
|
|
||||||
background-color: transparent;
|
|
||||||
|
|
||||||
.btn-image {
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cus:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回到底部
|
|
||||||
.to-bottom {
|
|
||||||
position: absolute;
|
|
||||||
right: 50%;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -16,16 +16,21 @@ const handlerPromptClick = async (prompt: any) => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="chat-empty">
|
<div class="relative flex h-full w-full flex-row justify-center">
|
||||||
|
<!-- center-container -->
|
||||||
|
<div class="flex flex-col justify-center">
|
||||||
<!-- title -->
|
<!-- title -->
|
||||||
<div class="center-container">
|
<div class="text-center text-[28px] font-bold">芋道 AI</div>
|
||||||
<div class="title">芋道 AI</div>
|
|
||||||
<div class="role-list">
|
<!-- role-list -->
|
||||||
|
<div
|
||||||
|
class="mt-[20px] flex w-[460px] flex-wrap items-center justify-center"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="role-item"
|
|
||||||
v-for="prompt in promptList"
|
v-for="prompt in promptList"
|
||||||
:key="prompt.prompt"
|
:key="prompt.prompt"
|
||||||
@click="handlerPromptClick(prompt)"
|
@click="handlerPromptClick(prompt)"
|
||||||
|
class="m-[10px] flex w-[180px] cursor-pointer justify-center rounded-[10px] border border-[#e4e4e4] leading-[50px] hover:bg-[rgba(243,243,243,0.73)]"
|
||||||
>
|
>
|
||||||
{{ prompt.prompt }}
|
{{ prompt.prompt }}
|
||||||
</div>
|
</div>
|
||||||
@@ -33,49 +38,3 @@ const handlerPromptClick = async (prompt: any) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
.chat-empty {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.center-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.role-list {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 460px;
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
.role-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 180px;
|
|
||||||
margin: 10px;
|
|
||||||
line-height: 50px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid #e4e4e4;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.role-item:hover {
|
|
||||||
background-color: rgb(243 243 243 / 73%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -3,13 +3,7 @@ import { Skeleton } from 'ant-design-vue';
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="message-loading">
|
<div class="p-[30px]">
|
||||||
<Skeleton active />
|
<Skeleton active />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.message-loading {
|
|
||||||
padding: 30px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -10,39 +10,14 @@ const handlerNewChat = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="new-chat">
|
<div class="flex h-full w-full flex-row justify-center">
|
||||||
<div class="box-center">
|
<div class="flex flex-col justify-center">
|
||||||
<div class="tip">点击下方按钮,开始你的对话吧</div>
|
<div class="text-center text-[14px] text-[#858585]">
|
||||||
<div class="btns">
|
点击下方按钮,开始你的对话吧
|
||||||
|
</div>
|
||||||
|
<div class="mt-[20px] flex flex-row justify-center">
|
||||||
<Button type="primary" round @click="handlerNewChat">新建对话</Button>
|
<Button type="primary" round @click="handlerNewChat">新建对话</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
.new-chat {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.box-center {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.tip {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #858585;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -25,8 +25,12 @@ const handleCategoryClick = async (category: string) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="category-list">
|
<div class="flex flex-wrap items-center">
|
||||||
<div class="category" v-for="category in categoryList" :key="category">
|
<div
|
||||||
|
class="mr-[10px] flex flex-row"
|
||||||
|
v-for="category in categoryList"
|
||||||
|
:key="category"
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
shape="round"
|
shape="round"
|
||||||
@@ -38,17 +42,3 @@ const handleCategoryClick = async (category: string) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.category-list {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.category {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -60,10 +60,18 @@ const handleTabsScroll = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="card-list" ref="tabsRef" @scroll="handleTabsScroll">
|
<div
|
||||||
<div class="card-item" v-for="role in roleList" :key="role.id">
|
class="relative flex h-full flex-wrap content-start items-start overflow-auto px-[25px] pb-[140px]"
|
||||||
|
ref="tabsRef"
|
||||||
|
@scroll="handleTabsScroll"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mb-[20px] mr-[20px] inline-block"
|
||||||
|
v-for="role in roleList"
|
||||||
|
:key="role.id"
|
||||||
|
>
|
||||||
<Card
|
<Card
|
||||||
class="card"
|
class="relative rounded-[10px]"
|
||||||
:body-style="{
|
:body-style="{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -75,7 +83,7 @@ const handleTabsScroll = async () => {
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- 更多操作 -->
|
<!-- 更多操作 -->
|
||||||
<div class="more-container" v-if="showMore">
|
<div v-if="showMore" class="absolute right-[12px] top-0">
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<Button type="text">
|
<Button type="text">
|
||||||
<span class="icon-[ant-design--more-outlined] text-2xl"></span>
|
<span class="icon-[ant-design--more-outlined] text-2xl"></span>
|
||||||
@@ -91,23 +99,32 @@ const handleTabsScroll = async () => {
|
|||||||
<Menu.Item @click="handleMoreClick(['delete', role])">
|
<Menu.Item @click="handleMoreClick(['delete', role])">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<IconifyIcon icon="ep:delete" color="red" />
|
<IconifyIcon icon="ep:delete" color="red" />
|
||||||
<span style="color: red">编辑</span>
|
<span class="text-red-500">编辑</span>
|
||||||
</div>
|
</div>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
</template>
|
</template>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 角色信息 -->
|
<!-- 角色信息 -->
|
||||||
<div>
|
<div>
|
||||||
<img class="avatar" :src="role.avatar" />
|
<img
|
||||||
|
:src="role.avatar"
|
||||||
|
class="h-[40px] w-[40px] overflow-hidden rounded-[10px]"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-container">
|
|
||||||
<div class="content-container">
|
<div class="ml-[10px] w-full">
|
||||||
<div class="title">{{ role.name }}</div>
|
<div class="h-[85px]">
|
||||||
<div class="description">{{ role.description }}</div>
|
<div class="max-w-[140px] text-[18px] font-bold text-[#3e3e3e]">
|
||||||
|
{{ role.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-container">
|
<div class="mt-[10px] text-[14px] text-[#6a6a6a]">
|
||||||
|
{{ role.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-[2px] flex flex-row-reverse">
|
||||||
<Button type="primary" size="small" @click="handleUseClick(role)">
|
<Button type="primary" size="small" @click="handleUseClick(role)">
|
||||||
使用
|
使用
|
||||||
</Button>
|
</Button>
|
||||||
@@ -117,67 +134,3 @@ const handleTabsScroll = async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
// 卡片列表
|
|
||||||
.card-list {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
place-content: flex-start start;
|
|
||||||
align-items: start;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 25px;
|
|
||||||
padding-bottom: 140px;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.card {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
|
|
||||||
.more-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-container {
|
|
||||||
width: 100%;
|
|
||||||
margin-left: 10px;
|
|
||||||
//height: 100px;
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
height: 85px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
max-width: 140px;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3e3e3e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #6a6a6a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -171,15 +171,18 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Drawer>
|
<Drawer>
|
||||||
<Layout class="role-container">
|
<Layout
|
||||||
|
class="absolute inset-0 flex h-full w-full flex-col overflow-hidden bg-white"
|
||||||
|
>
|
||||||
<FormModal @success="handlerAddRoleSuccess" />
|
<FormModal @success="handlerAddRoleSuccess" />
|
||||||
<Layout.Content class="role-main">
|
|
||||||
<div class="search-container">
|
<Layout.Content class="relative m-0 flex-1 overflow-hidden p-0">
|
||||||
<!-- 搜索按钮 -->
|
<div class="absolute right-0 top-[-5px] z-[100] mr-[20px] mt-[20px]">
|
||||||
|
<!-- 搜索输入框 -->
|
||||||
<Input.Search
|
<Input.Search
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
v-model:value="search"
|
v-model:value="search"
|
||||||
class="search-input"
|
class="w-[240px]"
|
||||||
placeholder="请输入搜索的内容"
|
placeholder="请输入搜索的内容"
|
||||||
@search="getActiveTabsRole"
|
@search="getActiveTabsRole"
|
||||||
/>
|
/>
|
||||||
@@ -193,12 +196,18 @@ onMounted(async () => {
|
|||||||
添加角色
|
添加角色
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 标签页内容 -->
|
||||||
<Tabs
|
<Tabs
|
||||||
v-model:value="activeTab"
|
v-model:value="activeTab"
|
||||||
class="tabs p-4"
|
class="relative h-full p-4"
|
||||||
@tab-click="handleTabsClick"
|
@tab-click="handleTabsClick"
|
||||||
>
|
>
|
||||||
<TabPane key="my-role" class="role-pane" tab="我的角色">
|
<TabPane
|
||||||
|
key="my-role"
|
||||||
|
class="flex h-full flex-col overflow-y-auto"
|
||||||
|
tab="我的角色"
|
||||||
|
>
|
||||||
<RoleList
|
<RoleList
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:role-list="myRoleList"
|
:role-list="myRoleList"
|
||||||
@@ -210,12 +219,17 @@ onMounted(async () => {
|
|||||||
class="mt-[20px]"
|
class="mt-[20px]"
|
||||||
/>
|
/>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane key="public-role" class="role-pane" tab="公共角色">
|
|
||||||
|
<TabPane
|
||||||
|
key="public-role"
|
||||||
|
class="flex h-full flex-col overflow-y-auto"
|
||||||
|
tab="公共角色"
|
||||||
|
>
|
||||||
<RoleCategoryList
|
<RoleCategoryList
|
||||||
class="role-category-list"
|
|
||||||
:category-list="categoryList"
|
:category-list="categoryList"
|
||||||
:active="activeCategory"
|
:active="activeCategory"
|
||||||
@on-category-click="handlerCategoryClick"
|
@on-category-click="handlerCategoryClick"
|
||||||
|
class="mx-[27px]"
|
||||||
/>
|
/>
|
||||||
<RoleList
|
<RoleList
|
||||||
:role-list="publicRoleList"
|
:role-list="publicRoleList"
|
||||||
@@ -232,56 +246,3 @@ onMounted(async () => {
|
|||||||
</Layout>
|
</Layout>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
// 跟容器
|
|
||||||
.role-container {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
.role-main {
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
position: absolute;
|
|
||||||
top: -5px;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
margin: 20px 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
width: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.role-category-list {
|
|
||||||
margin: 0 27px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.role-pane {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -494,7 +494,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<Layout class="ai-layout">
|
<Layout class="absolute left-0 top-0 h-full w-full flex-1">
|
||||||
<!-- 左侧:对话列表 -->
|
<!-- 左侧:对话列表 -->
|
||||||
<ConversationList
|
<ConversationList
|
||||||
:active-id="activeConversationId"
|
:active-id="activeConversationId"
|
||||||
@@ -504,19 +504,24 @@ onMounted(async () => {
|
|||||||
@on-conversation-clear="handleConversationClear"
|
@on-conversation-clear="handleConversationClear"
|
||||||
@on-conversation-delete="handlerConversationDelete"
|
@on-conversation-delete="handlerConversationDelete"
|
||||||
/>
|
/>
|
||||||
<Layout class="detail-container">
|
|
||||||
<Layout.Header class="header">
|
<!-- 右侧:详情部分 -->
|
||||||
<div class="title">
|
<Layout class="bg-white">
|
||||||
|
<Layout.Header
|
||||||
|
class="flex items-center justify-between bg-[#fbfbfb!important] shadow-none"
|
||||||
|
>
|
||||||
|
<div class="text-[18px] font-bold">
|
||||||
{{ activeConversation?.title ? activeConversation?.title : '对话' }}
|
{{ activeConversation?.title ? activeConversation?.title : '对话' }}
|
||||||
<span v-if="activeMessageList.length > 0">
|
<span v-if="activeMessageList.length > 0">
|
||||||
({{ activeMessageList.length }})
|
({{ activeMessageList.length }})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="btns" v-if="activeConversation">
|
|
||||||
|
<div class="flex w-[300px] justify-end" v-if="activeConversation">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
ghost
|
ghost
|
||||||
class="mr-[10px]"
|
class="mr-[10px] px-[10px]"
|
||||||
size="small"
|
size="small"
|
||||||
@click="openChatConversationUpdateForm"
|
@click="openChatConversationUpdateForm"
|
||||||
>
|
>
|
||||||
@@ -525,7 +530,7 @@ onMounted(async () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
class="btn mr-[10px]"
|
class="mr-[10px] px-[10px]"
|
||||||
@click="handlerMessageClear"
|
@click="handlerMessageClear"
|
||||||
>
|
>
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
@@ -533,29 +538,26 @@ onMounted(async () => {
|
|||||||
color="#787878"
|
color="#787878"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="small" class="btn mr-[10px]">
|
<Button size="small" class="mr-[10px] px-[10px]">
|
||||||
<IconifyIcon icon="ep:download" color="#787878" />
|
<IconifyIcon icon="ep:download" color="#787878" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
class="btn mr-[10px]"
|
class="mr-[10px] px-[10px]"
|
||||||
@click="handleGoTopMessage"
|
@click="handleGoTopMessage"
|
||||||
>
|
>
|
||||||
<IconifyIcon icon="ep:top" color="#787878" />
|
<IconifyIcon icon="ep:top" color="#787878" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
<Layout.Content class="main-container">
|
|
||||||
<div>
|
<Layout.Content class="relative m-0 h-full w-full p-0">
|
||||||
<div class="message-container">
|
<div class="absolute inset-0 m-0 overflow-y-hidden p-0">
|
||||||
<!-- 情况一:消息加载中 -->
|
|
||||||
<MessageLoading v-if="activeMessageListLoading" />
|
<MessageLoading v-if="activeMessageListLoading" />
|
||||||
<!-- 情况二:无聊天对话时 -->
|
|
||||||
<MessageNewConversation
|
<MessageNewConversation
|
||||||
v-if="!activeConversation"
|
v-if="!activeConversation"
|
||||||
@on-new-conversation="handleConversationCreate"
|
@on-new-conversation="handleConversationCreate"
|
||||||
/>
|
/>
|
||||||
<!-- 情况三:消息列表为空 -->
|
|
||||||
<MessageListEmpty
|
<MessageListEmpty
|
||||||
v-if="
|
v-if="
|
||||||
!activeMessageListLoading &&
|
!activeMessageListLoading &&
|
||||||
@@ -564,7 +566,6 @@ onMounted(async () => {
|
|||||||
"
|
"
|
||||||
@on-prompt="doSendMessage"
|
@on-prompt="doSendMessage"
|
||||||
/>
|
/>
|
||||||
<!-- 情况四:消息列表不为空 -->
|
|
||||||
<MessageList
|
<MessageList
|
||||||
v-if="!activeMessageListLoading && messageList.length > 0"
|
v-if="!activeMessageListLoading && messageList.length > 0"
|
||||||
ref="messageRef"
|
ref="messageRef"
|
||||||
@@ -575,12 +576,14 @@ onMounted(async () => {
|
|||||||
@on-refresh="handleMessageRefresh"
|
@on-refresh="handleMessageRefresh"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
<Layout.Footer class="footer-container">
|
|
||||||
<form class="prompt-from">
|
<Layout.Footer class="m-0 flex flex-col bg-[white!important] p-0">
|
||||||
|
<form
|
||||||
|
class="m-[10px_20px_20px] flex flex-col rounded-[10px] border border-[#e3e3e3] p-[9px_10px]"
|
||||||
|
>
|
||||||
<textarea
|
<textarea
|
||||||
class="prompt-input"
|
class="box-border h-[80px] resize-none overflow-auto border-none p-[0_2px] focus:outline-none"
|
||||||
v-model="prompt"
|
v-model="prompt"
|
||||||
@keydown="handleSendByKeydown"
|
@keydown="handleSendByKeydown"
|
||||||
@input="handlePromptInput"
|
@input="handlePromptInput"
|
||||||
@@ -588,10 +591,10 @@ onMounted(async () => {
|
|||||||
@compositionend="onCompositionend"
|
@compositionend="onCompositionend"
|
||||||
placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)"
|
placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)"
|
||||||
></textarea>
|
></textarea>
|
||||||
<div class="prompt-btns">
|
<div class="flex justify-between pb-0 pt-[5px]">
|
||||||
<div>
|
<div class="flex items-center">
|
||||||
<Switch v-model:checked="enableContext" />
|
<Switch v-model:checked="enableContext" />
|
||||||
<span class="ml-5px text-14px text-#8f8f8f">上下文</span>
|
<span class="ml-[5px] text-[14px] text-[#8f8f8f]">上下文</span>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -616,202 +619,3 @@ onMounted(async () => {
|
|||||||
<FormModal @success="handleConversationUpdateSuccess" />
|
<FormModal @success="handleConversationUpdateSuccess" />
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.ai-layout {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 10px 10px 0;
|
|
||||||
|
|
||||||
.btn-new-conversation {
|
|
||||||
padding: 18px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-list {
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
.conversation {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 5px;
|
|
||||||
margin-top: 10px;
|
|
||||||
line-height: 30px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
max-width: 220px;
|
|
||||||
padding: 5px 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
font-size: 14px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-items: center;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对话编辑、删除
|
|
||||||
.button-wrapper {
|
|
||||||
right: 2px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-items: center;
|
|
||||||
color: #606266;
|
|
||||||
|
|
||||||
.el-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 角色仓库、清空未设置对话
|
|
||||||
.tool-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
line-height: 35px;
|
|
||||||
color: var(--el-text-color);
|
|
||||||
|
|
||||||
> div {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
color: #606266;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
> span {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 头部
|
|
||||||
.detail-container {
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
background: #fbfbfb;
|
|
||||||
box-shadow: 0 0 0 0 #dcdfe6;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-end;
|
|
||||||
width: 300px;
|
|
||||||
//justify-content: space-between;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// main 容器
|
|
||||||
.main-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.message-container {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 底部
|
|
||||||
.footer-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: auto;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
background-color: white;
|
|
||||||
|
|
||||||
.prompt-from {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: auto;
|
|
||||||
padding: 9px 10px;
|
|
||||||
margin: 10px 20px 20px;
|
|
||||||
border: 1px solid #e3e3e3;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-input {
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 80px;
|
|
||||||
padding: 0 2px;
|
|
||||||
overflow: auto;
|
|
||||||
resize: none;
|
|
||||||
//box-shadow: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-input:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prompt-btns {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-top: 5px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -65,8 +65,12 @@ onMounted(async () => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Card body-class="" class="image-card">
|
<Card
|
||||||
<div class="image-operation">
|
body-class=""
|
||||||
|
class="relative flex h-auto w-[320px] flex-col rounded-[10px]"
|
||||||
|
>
|
||||||
|
<!-- 图片操作区 -->
|
||||||
|
<div class="flex flex-row justify-between">
|
||||||
<div>
|
<div>
|
||||||
<Button v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS">
|
<Button v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS">
|
||||||
生成中
|
生成中
|
||||||
@@ -78,31 +82,30 @@ onMounted(async () => {
|
|||||||
异常
|
异常
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 操作区 -->
|
<div class="flex">
|
||||||
<div>
|
|
||||||
<Button
|
<Button
|
||||||
class="btn"
|
class="m-0 p-[10px]"
|
||||||
type="text"
|
type="text"
|
||||||
@click="handleButtonClick('download', detail)"
|
@click="handleButtonClick('download', detail)"
|
||||||
>
|
>
|
||||||
<span class="icon-[ant-design--download-outlined]"></span>
|
<span class="icon-[ant-design--download-outlined]"></span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
class="btn"
|
class="m-0 p-[10px]"
|
||||||
type="text"
|
type="text"
|
||||||
@click="handleButtonClick('regeneration', detail)"
|
@click="handleButtonClick('regeneration', detail)"
|
||||||
>
|
>
|
||||||
<span class="icon-[ant-design--redo-outlined]"></span>
|
<span class="icon-[ant-design--redo-outlined]"></span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
class="btn"
|
class="m-0 p-[10px]"
|
||||||
type="text"
|
type="text"
|
||||||
@click="handleButtonClick('delete', detail)"
|
@click="handleButtonClick('delete', detail)"
|
||||||
>
|
>
|
||||||
<span class="icon-[ant-design--delete-outlined]"></span>
|
<span class="icon-[ant-design--delete-outlined]"></span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
class="btn"
|
class="m-0 p-[10px]"
|
||||||
type="text"
|
type="text"
|
||||||
@click="handleButtonClick('more', detail)"
|
@click="handleButtonClick('more', detail)"
|
||||||
>
|
>
|
||||||
@@ -110,14 +113,17 @@ onMounted(async () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-wrapper" ref="cardImageRef">
|
|
||||||
<Image class="image" :src="detail?.picUrl" />
|
<!-- 图片展示区域 -->
|
||||||
|
<div class="mt-[20px] h-[280px] flex-1 overflow-hidden" ref="cardImageRef">
|
||||||
|
<Image class="w-full rounded-[10px]" :src="detail?.picUrl" />
|
||||||
<div v-if="detail?.status === AiImageStatusEnum.FAIL">
|
<div v-if="detail?.status === AiImageStatusEnum.FAIL">
|
||||||
{{ detail?.errorMessage }}
|
{{ detail?.errorMessage }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Midjourney 专属操作 -->
|
|
||||||
<div class="image-mj-btns">
|
<!-- Midjourney 专属操作按钮 -->
|
||||||
|
<div class="mt-[5px] flex w-full flex-wrap justify-start">
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
v-for="(button, index) in detail?.buttons"
|
v-for="(button, index) in detail?.buttons"
|
||||||
@@ -130,46 +136,3 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.image-card {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 320px;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 10px;
|
|
||||||
|
|
||||||
.image-operation {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
//border: 1px solid red;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
height: 280px;
|
|
||||||
margin-top: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.image {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-mj-btns {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
justify-content: flex-start;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AiImageApi } from '#/api/ai/image';
|
import type { AiImageApi } from '#/api/ai/image';
|
||||||
import type { ImageModelVO } from '#/utils/constants';
|
|
||||||
|
|
||||||
import { ref, toRefs, watch } from 'vue';
|
import { ref, toRefs, watch } from 'vue';
|
||||||
|
|
||||||
@@ -43,15 +42,16 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="item">
|
<div class="mb-5 w-full overflow-hidden break-words">
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
<Image class="image" :src="detail?.picUrl" />
|
<Image class="rounded-[10px]" :src="detail?.picUrl" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 时间 -->
|
<!-- 时间 -->
|
||||||
<div class="item">
|
<div class="mb-5 w-full overflow-hidden break-words">
|
||||||
<div class="tip">时间</div>
|
<div class="tip text-lg font-bold">时间</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
<div>
|
<div>
|
||||||
提交时间:{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
|
提交时间:{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -60,177 +60,150 @@ watch(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 模型 -->
|
<!-- 模型 -->
|
||||||
<div class="item">
|
<div class="mb-5 w-full overflow-hidden break-words">
|
||||||
<div class="tip">模型</div>
|
<div class="tip text-lg font-bold">模型</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
{{ detail.model }}({{ detail.height }}x{{ detail.width }})
|
{{ detail.model }}({{ detail.height }}x{{ detail.width }})
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提示词 -->
|
<!-- 提示词 -->
|
||||||
<div class="item">
|
<div class="mb-5 w-full overflow-hidden break-words">
|
||||||
<div class="tip">提示词</div>
|
<div class="tip text-lg font-bold">提示词</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
{{ detail.prompt }}
|
{{ detail.prompt }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 地址 -->
|
|
||||||
<div class="item">
|
<!-- 图片地址 -->
|
||||||
<div class="tip">图片地址</div>
|
<div class="mb-5 w-full overflow-hidden break-words">
|
||||||
<div class="body">
|
<div class="tip text-lg font-bold">图片地址</div>
|
||||||
|
<div class="body mt-2 text-gray-600">
|
||||||
{{ detail.picUrl }}
|
{{ detail.picUrl }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- StableDiffusion 专属区域 -->
|
|
||||||
|
<!-- StableDiffusion 专属 -->
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||||
detail?.options?.sampler
|
detail?.options?.sampler
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">采样方法</div>
|
<div class="tip text-lg font-bold">采样方法</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
{{
|
{{
|
||||||
StableDiffusionSamplers.find(
|
StableDiffusionSamplers.find(
|
||||||
(item: ImageModelVO) => item.key === detail?.options?.sampler,
|
(item) => item.key === detail?.options?.sampler,
|
||||||
)?.name
|
)?.name
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||||
detail?.options?.clipGuidancePreset
|
detail?.options?.clipGuidancePreset
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">CLIP</div>
|
<div class="tip text-lg font-bold">CLIP</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
{{
|
{{
|
||||||
StableDiffusionClipGuidancePresets.find(
|
StableDiffusionClipGuidancePresets.find(
|
||||||
(item: ImageModelVO) =>
|
(item) => item.key === detail?.options?.clipGuidancePreset,
|
||||||
item.key === detail?.options?.clipGuidancePreset,
|
|
||||||
)?.name
|
)?.name
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||||
detail?.options?.stylePreset
|
detail?.options?.stylePreset
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">风格</div>
|
<div class="tip text-lg font-bold">风格</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
{{
|
{{
|
||||||
StableDiffusionStylePresets.find(
|
StableDiffusionStylePresets.find(
|
||||||
(item: ImageModelVO) => item.key === detail?.options?.stylePreset,
|
(item) => item.key === detail?.options?.stylePreset,
|
||||||
)?.name
|
)?.name
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||||
detail?.options?.steps
|
detail?.options?.steps
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">迭代步数</div>
|
<div class="tip text-lg font-bold">迭代步数</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">{{ detail?.options?.steps }}</div>
|
||||||
{{ detail?.options?.steps }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||||
detail?.options?.scale
|
detail?.options?.scale
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">引导系数</div>
|
<div class="tip text-lg font-bold">引导系数</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">{{ detail?.options?.scale }}</div>
|
||||||
{{ detail?.options?.scale }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||||
detail?.options?.seed
|
detail?.options?.seed
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">随机因子</div>
|
<div class="tip text-lg font-bold">随机因子</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">{{ detail?.options?.seed }}</div>
|
||||||
{{ detail?.options?.seed }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<!-- Dall3 专属区域 -->
|
<!-- Dall3 专属 -->
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="detail.platform === AiPlatformEnum.OPENAI && detail?.options?.style"
|
v-if="detail.platform === AiPlatformEnum.OPENAI && detail?.options?.style"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">风格选择</div>
|
<div class="tip text-lg font-bold">风格选择</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
{{
|
{{
|
||||||
Dall3StyleList.find(
|
Dall3StyleList.find((item) => item.key === detail?.options?.style)?.name
|
||||||
(item: ImageModelVO) => item.key === detail?.options?.style,
|
|
||||||
)?.name
|
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Midjourney 专属区域 -->
|
|
||||||
|
<!-- Midjourney 专属 -->
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.version
|
detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.version
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">模型版本</div>
|
<div class="tip text-lg font-bold">模型版本</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">{{ detail?.options?.version }}</div>
|
||||||
{{ detail?.options?.version }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="item"
|
|
||||||
v-if="
|
v-if="
|
||||||
detail.platform === AiPlatformEnum.MIDJOURNEY &&
|
detail.platform === AiPlatformEnum.MIDJOURNEY &&
|
||||||
detail?.options?.referImageUrl
|
detail?.options?.referImageUrl
|
||||||
"
|
"
|
||||||
|
class="mb-5 w-full overflow-hidden break-words"
|
||||||
>
|
>
|
||||||
<div class="tip">参考图</div>
|
<div class="tip text-lg font-bold">参考图</div>
|
||||||
<div class="body">
|
<div class="body mt-2 text-gray-600">
|
||||||
<Image :src="detail.options.referImageUrl" />
|
<Image :src="detail.options.referImageUrl" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
.item {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
word-wrap: break-word;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
|
||||||
margin-top: 10px;
|
|
||||||
color: #616161;
|
|
||||||
|
|
||||||
.taskImage {
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -166,38 +166,46 @@ onUnmounted(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Drawer class="w-[600px]">
|
<Drawer class="w-[600px]">
|
||||||
<ImageDetail :id="showImageDetailId" />
|
<ImageDetail :id="showImageDetailId" />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Card
|
<Card
|
||||||
class="dr-task"
|
class="dr-task flex h-full w-full flex-col"
|
||||||
:body-style="{
|
:body-style="{
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
绘画任务
|
绘画任务
|
||||||
<!-- TODO @fan:看看,怎么优化下这个样子哈。 -->
|
|
||||||
<Button @click="handleViewPublic">绘画作品</Button>
|
<Button @click="handleViewPublic">绘画作品</Button>
|
||||||
</template>
|
</template>
|
||||||
<div class="task-image-list" ref="imageListRef">
|
|
||||||
|
<div
|
||||||
|
class="task-image-list flex flex-1 flex-wrap content-start overflow-y-auto p-5 pb-[140px] pt-5"
|
||||||
|
ref="imageListRef"
|
||||||
|
>
|
||||||
<ImageCard
|
<ImageCard
|
||||||
v-for="image in imageList"
|
v-for="image in imageList"
|
||||||
:key="image.id"
|
:key="image.id"
|
||||||
:detail="image"
|
:detail="image"
|
||||||
@on-btn-click="handleImageButtonClick"
|
@on-btn-click="handleImageButtonClick"
|
||||||
@on-mj-btn-click="handleImageMidjourneyButtonClick"
|
@on-mj-btn-click="handleImageMidjourneyButtonClick"
|
||||||
|
class="mb-5 mr-5"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="task-image-pagination">
|
|
||||||
|
<div
|
||||||
|
class="task-image-pagination sticky bottom-0 z-50 flex h-[60px] items-center justify-center bg-white shadow-[0_-2px_8px_rgba(0,0,0,0.1)]"
|
||||||
|
>
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="pageTotal"
|
:total="pageTotal"
|
||||||
:show-total="(total: number) => `共 ${total} 条`"
|
:show-total="(total) => `共 ${total} 条`"
|
||||||
show-quick-jumper
|
show-quick-jumper
|
||||||
show-size-changer
|
show-size-changer
|
||||||
v-model:current="queryParams.pageNo"
|
v-model:current="queryParams.pageNo"
|
||||||
@@ -208,43 +216,3 @@ onUnmounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss">
|
|
||||||
.dr-task {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-image-list {
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box; /* 确保内边距不会增加高度 */
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
align-content: flex-start;
|
|
||||||
height: 100%;
|
|
||||||
padding: 20px 20px 140px;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div:last-of-type {
|
|
||||||
//margin-bottom: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-image-pagination {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 60px;
|
|
||||||
z-index: 999;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
line-height: 90px;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -117,19 +117,20 @@ defineExpose({ settingValues });
|
|||||||
v-model:value="prompt"
|
v-model:value="prompt"
|
||||||
:maxlength="1024"
|
:maxlength="1024"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
class="w-100% mt-[15px]"
|
class="mt-[15px] w-full"
|
||||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||||
show-count
|
show-count
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="hot-words">
|
|
||||||
|
<div class="hot-words mt-[30px] flex flex-col">
|
||||||
<div>
|
<div>
|
||||||
<b>随机热词</b>
|
<b>随机热词</b>
|
||||||
</div>
|
</div>
|
||||||
<Space wrap class="word-list">
|
<Space wrap class="word-list mt-[15px] flex flex-wrap justify-start">
|
||||||
<Button
|
<Button
|
||||||
shape="round"
|
shape="round"
|
||||||
class="btn"
|
class="btn m-0"
|
||||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||||
v-for="hotWord in ImageHotWords"
|
v-for="hotWord in ImageHotWords"
|
||||||
:key="hotWord"
|
:key="hotWord"
|
||||||
@@ -139,16 +140,17 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
|
<div class="group-item mt-[30px]">
|
||||||
<div>
|
<div>
|
||||||
<b>平台</b>
|
<b>平台</b>
|
||||||
</div>
|
</div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="group-item-body mt-[15px] w-full">
|
||||||
<Select
|
<Select
|
||||||
v-model:value="otherPlatform"
|
v-model:value="otherPlatform"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
size="large"
|
size="large"
|
||||||
class="!w-[330px]"
|
class="!important w-[330px]"
|
||||||
@change="handlerPlatformChange"
|
@change="handlerPlatformChange"
|
||||||
>
|
>
|
||||||
<Select.Option
|
<Select.Option
|
||||||
@@ -161,16 +163,17 @@ defineExpose({ settingValues });
|
|||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
|
<div class="group-item mt-[30px]">
|
||||||
<div>
|
<div>
|
||||||
<b>模型</b>
|
<b>模型</b>
|
||||||
</div>
|
</div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="group-item-body mt-[15px] w-full">
|
||||||
<Select
|
<Select
|
||||||
v-model:value="modelId"
|
v-model:value="modelId"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
size="large"
|
size="large"
|
||||||
class="!w-[330px]"
|
class="!important w-[330px]"
|
||||||
>
|
>
|
||||||
<Select.Option
|
<Select.Option
|
||||||
v-for="item in platformModels"
|
v-for="item in platformModels"
|
||||||
@@ -182,11 +185,12 @@ defineExpose({ settingValues });
|
|||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
|
<div class="group-item mt-[30px]">
|
||||||
<div>
|
<div>
|
||||||
<b>图片尺寸</b>
|
<b>图片尺寸</b>
|
||||||
</div>
|
</div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="group-item-body mt-[15px] flex flex-wrap gap-x-[20px]">
|
||||||
<InputNumber
|
<InputNumber
|
||||||
v-model:value="width"
|
v-model:value="width"
|
||||||
class="mt-[10px] w-[170px]"
|
class="mt-[10px] w-[170px]"
|
||||||
@@ -199,7 +203,8 @@ defineExpose({ settingValues });
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="btns">
|
|
||||||
|
<div class="btns mt-[50px] flex justify-center">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -212,37 +217,3 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
.hot-words {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.word-list {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
justify-content: start;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模型
|
|
||||||
.group-item {
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.group-item-body {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -151,19 +151,18 @@ defineExpose({ settingValues });
|
|||||||
v-model:value="prompt"
|
v-model:value="prompt"
|
||||||
:maxlength="1024"
|
:maxlength="1024"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
class="w-100% mt-[15px]"
|
class="mt-[15px] w-full"
|
||||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||||
show-count
|
show-count
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="hot-words">
|
|
||||||
<div>
|
<div class="hot-words mt-[30px] flex flex-col">
|
||||||
<b>随机热词</b>
|
<div><b>随机热词</b></div>
|
||||||
</div>
|
<Space wrap class="word-list mt-[15px] flex flex-wrap justify-start">
|
||||||
<Space wrap class="word-list">
|
|
||||||
<Button
|
<Button
|
||||||
shape="round"
|
shape="round"
|
||||||
class="btn"
|
class="btn m-0"
|
||||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||||
v-for="hotWord in ImageHotWords"
|
v-for="hotWord in ImageHotWords"
|
||||||
:key="hotWord"
|
:key="hotWord"
|
||||||
@@ -173,15 +172,17 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="model">
|
|
||||||
<div>
|
<div class="model mt-[30px]">
|
||||||
<b>模型选择</b>
|
<div><b>模型选择</b></div>
|
||||||
</div>
|
<Space wrap class="model-list mt-[15px] flex flex-wrap gap-[10px]">
|
||||||
<Space wrap class="model-list">
|
|
||||||
<div
|
<div
|
||||||
:class="
|
class="modal-item flex w-[110px] cursor-pointer flex-col items-center overflow-hidden rounded-[5px] border-[3px]"
|
||||||
selectModel === model.key ? 'modal-item selectModel' : 'modal-item'
|
:class="[
|
||||||
"
|
selectModel === model.key
|
||||||
|
? 'border-[#1293ff!important]'
|
||||||
|
: 'border-transparent',
|
||||||
|
]"
|
||||||
v-for="model in Dall3Models"
|
v-for="model in Dall3Models"
|
||||||
:key="model.key"
|
:key="model.key"
|
||||||
>
|
>
|
||||||
@@ -191,21 +192,21 @@ defineExpose({ settingValues });
|
|||||||
fit="contain"
|
fit="contain"
|
||||||
@click="handleModelClick(model)"
|
@click="handleModelClick(model)"
|
||||||
/>
|
/>
|
||||||
<div class="model-font">{{ model.name }}</div>
|
<div class="model-font text-[14px] font-bold text-[#3e3e3e]">
|
||||||
|
{{ model.name }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-style">
|
|
||||||
<div>
|
<div class="image-style mt-[30px]">
|
||||||
<b>风格选择</b>
|
<div><b>风格选择</b></div>
|
||||||
</div>
|
<Space wrap class="image-style-list mt-[15px] flex flex-wrap gap-[10px]">
|
||||||
<Space wrap class="image-style-list">
|
|
||||||
<div
|
<div
|
||||||
:class="
|
class="image-style-item flex w-[110px] cursor-pointer flex-col items-center overflow-hidden rounded-[5px] border-[3px]"
|
||||||
style === imageStyle.key
|
:class="[
|
||||||
? 'image-style-item selectImageStyle'
|
style === imageStyle.key ? 'border-[#1293ff]' : 'border-transparent',
|
||||||
: 'image-style-item'
|
]"
|
||||||
"
|
|
||||||
v-for="imageStyle in Dall3StyleList"
|
v-for="imageStyle in Dall3StyleList"
|
||||||
:key="imageStyle.key"
|
:key="imageStyle.key"
|
||||||
>
|
>
|
||||||
@@ -215,35 +216,41 @@ defineExpose({ settingValues });
|
|||||||
fit="contain"
|
fit="contain"
|
||||||
@click="handleStyleClick(imageStyle)"
|
@click="handleStyleClick(imageStyle)"
|
||||||
/>
|
/>
|
||||||
<div class="style-font">{{ imageStyle.name }}</div>
|
<div class="style-font text-[14px] font-bold text-[#3e3e3e]">
|
||||||
|
{{ imageStyle.name }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-size">
|
|
||||||
<div>
|
<div class="image-size mt-[30px] w-full">
|
||||||
<b>画面比例</b>
|
<div><b>画面比例</b></div>
|
||||||
</div>
|
<Space
|
||||||
<Space wrap class="size-list">
|
wrap
|
||||||
|
class="size-list mt-[20px] flex w-full flex-row justify-between"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="size-item"
|
class="size-item flex cursor-pointer flex-col items-center"
|
||||||
v-for="imageSize in Dall3SizeList"
|
v-for="imageSize in Dall3SizeList"
|
||||||
:key="imageSize.key"
|
:key="imageSize.key"
|
||||||
@click="handleSizeClick(imageSize)"
|
@click="handleSizeClick(imageSize)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="
|
class="size-wrapper flex h-[50px] w-[50px] flex-col items-center justify-center rounded-[7px] border bg-white p-[4px]"
|
||||||
selectSize === imageSize.key
|
:class="[
|
||||||
? 'size-wrapper selectImageSize'
|
selectSize === imageSize.key ? 'border-[#1293ff]' : 'border-white',
|
||||||
: 'size-wrapper'
|
]"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<div :style="imageSize.style"></div>
|
<div :style="imageSize.style"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="size-font">{{ imageSize.name }}</div>
|
<div class="size-font text-[14px] font-bold text-[#3e3e3e]">
|
||||||
|
{{ imageSize.name }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="btns">
|
|
||||||
|
<div class="btns mt-[50px] flex justify-center">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -256,134 +263,3 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
// 热词
|
|
||||||
.hot-words {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.word-list {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
justify-content: start;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模型
|
|
||||||
.model {
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.model-list {
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.modal-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 110px;
|
|
||||||
//outline: 1px solid blue;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 3px solid transparent;
|
|
||||||
|
|
||||||
.model-font {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3e3e3e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectModel {
|
|
||||||
border: 3px solid #1293ff;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 样式 style
|
|
||||||
.image-style {
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.image-style-list {
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.image-style-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 110px;
|
|
||||||
//outline: 1px solid blue;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 3px solid transparent;
|
|
||||||
|
|
||||||
.style-font {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3e3e3e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectImageStyle {
|
|
||||||
border: 3px solid #1293ff;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尺寸
|
|
||||||
.image-size {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.size-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
.size-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.size-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
padding: 4px;
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.size-font {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3e3e3e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectImageSize {
|
|
||||||
border: 1px solid #1293ff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -150,19 +150,18 @@ defineExpose({ settingValues });
|
|||||||
v-model:value="prompt"
|
v-model:value="prompt"
|
||||||
:maxlength="1024"
|
:maxlength="1024"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
class="w-100% mt-[15px]"
|
class="mt-[15px] w-full"
|
||||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||||
show-count
|
show-count
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="hot-words">
|
|
||||||
<div>
|
<div class="mt-8 flex flex-col">
|
||||||
<b>随机热词</b>
|
<div><b>随机热词</b></div>
|
||||||
</div>
|
<Space wrap class="mt-4 flex flex-wrap gap-2">
|
||||||
<Space wrap class="word-list">
|
|
||||||
<Button
|
<Button
|
||||||
shape="round"
|
shape="round"
|
||||||
class="btn"
|
class="m-0"
|
||||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||||
v-for="hotWord in ImageHotWords"
|
v-for="hotWord in ImageHotWords"
|
||||||
:key="hotWord"
|
:key="hotWord"
|
||||||
@@ -172,41 +171,41 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-size">
|
|
||||||
<div>
|
<div class="mt-8 w-full">
|
||||||
<b>尺寸</b>
|
<div><b>尺寸</b></div>
|
||||||
</div>
|
<Space wrap class="mt-5 flex w-full flex-row justify-between">
|
||||||
<Space wrap class="size-list">
|
|
||||||
<div
|
<div
|
||||||
class="size-item"
|
class="flex cursor-pointer flex-col items-center"
|
||||||
v-for="imageSize in MidjourneySizeList"
|
v-for="imageSize in MidjourneySizeList"
|
||||||
:key="imageSize.key"
|
:key="imageSize.key"
|
||||||
@click="handleSizeClick(imageSize)"
|
@click="handleSizeClick(imageSize)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="
|
class="flex h-[50px] w-[50px] items-center justify-center rounded-[7px] border bg-white p-1"
|
||||||
selectSize === imageSize.key
|
:class="[
|
||||||
? 'size-wrapper selectImageSize'
|
selectSize === imageSize.key ? 'border-[#1293ff]' : 'border-white',
|
||||||
: 'size-wrapper'
|
]"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<div :style="imageSize.style"></div>
|
<div :style="imageSize.style"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="size-font">{{ imageSize.key }}</div>
|
<div class="text-sm font-bold text-[#3e3e3e]">{{ imageSize.key }}</div>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="model">
|
|
||||||
<div>
|
<div class="mt-8">
|
||||||
<b>模型</b>
|
<div><b>模型</b></div>
|
||||||
</div>
|
<Space wrap class="mt-4 flex flex-wrap gap-4">
|
||||||
<Space wrap class="model-list">
|
|
||||||
<div
|
<div
|
||||||
:class="
|
|
||||||
selectModel === model.key ? 'modal-item selectModel' : 'modal-item'
|
|
||||||
"
|
|
||||||
v-for="model in MidjourneyModels"
|
v-for="model in MidjourneyModels"
|
||||||
:key="model.key"
|
:key="model.key"
|
||||||
|
class="flex w-[150px] cursor-pointer flex-col items-center overflow-hidden border-[3px]"
|
||||||
|
:class="[
|
||||||
|
selectModel === model.key
|
||||||
|
? 'rounded border-[#1293ff]'
|
||||||
|
: 'border-transparent',
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
:preview="false"
|
:preview="false"
|
||||||
@@ -214,18 +213,17 @@ defineExpose({ settingValues });
|
|||||||
fit="contain"
|
fit="contain"
|
||||||
@click="handleModelClick(model)"
|
@click="handleModelClick(model)"
|
||||||
/>
|
/>
|
||||||
<div class="model-font">{{ model.name }}</div>
|
<div class="text-sm font-bold text-[#3e3e3e]">{{ model.name }}</div>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="version">
|
|
||||||
<div>
|
<div class="mt-5">
|
||||||
<b>版本</b>
|
<div><b>版本</b></div>
|
||||||
</div>
|
<Space wrap class="mt-5 w-full">
|
||||||
<Space wrap class="version-list">
|
|
||||||
<Select
|
<Select
|
||||||
v-model:value="selectVersion"
|
v-model:value="selectVersion"
|
||||||
class="version-select !w-[330px]"
|
class="!w-[330px]"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请选择版本"
|
placeholder="请选择版本"
|
||||||
>
|
>
|
||||||
@@ -239,15 +237,15 @@ defineExpose({ settingValues });
|
|||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="model">
|
|
||||||
<div>
|
<div class="mt-8">
|
||||||
<b>参考图</b>
|
<div><b>参考图</b></div>
|
||||||
</div>
|
<Space wrap class="mt-4">
|
||||||
<Space wrap class="model-list">
|
|
||||||
<ImageUpload v-model:value="referImageUrl" :show-description="false" />
|
<ImageUpload v-model:value="referImageUrl" :show-description="false" />
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="btns">
|
|
||||||
|
<div class="mt-[50px] flex justify-center">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -259,113 +257,3 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
// 热词
|
|
||||||
.hot-words {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.word-list {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
justify-content: start;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// version
|
|
||||||
.version {
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
.version-list {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模型
|
|
||||||
.model {
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.model-list {
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.modal-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 150px;
|
|
||||||
//outline: 1px solid blue;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 3px solid transparent;
|
|
||||||
|
|
||||||
.model-font {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3e3e3e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectModel {
|
|
||||||
border: 3px solid #1293ff;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尺寸
|
|
||||||
.image-size {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.size-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
.size-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.size-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
padding: 4px;
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.size-font {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3e3e3e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectImageSize {
|
|
||||||
border: 1px solid #1293ff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { alert, confirm } from '@vben/common-ui';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Input,
|
|
||||||
InputNumber,
|
InputNumber,
|
||||||
message,
|
message,
|
||||||
Select,
|
Select,
|
||||||
@@ -139,19 +138,19 @@ defineExpose({ settingValues });
|
|||||||
v-model:value="prompt"
|
v-model:value="prompt"
|
||||||
:maxlength="1024"
|
:maxlength="1024"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
class="w-100% mt-[15px]"
|
class="mt-[15px] w-full"
|
||||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||||
show-count
|
show-count
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="hot-words">
|
|
||||||
<div>
|
<!-- 热词区域 -->
|
||||||
<b>随机热词</b>
|
<div class="mt-[30px] flex flex-col">
|
||||||
</div>
|
<div><b>随机热词</b></div>
|
||||||
<Space wrap class="word-list">
|
<Space wrap class="mt-[15px] flex flex-wrap justify-start">
|
||||||
<Button
|
<Button
|
||||||
shape="round"
|
shape="round"
|
||||||
class="btn"
|
class="m-0"
|
||||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||||
v-for="hotWord in ImageHotEnglishWords"
|
v-for="hotWord in ImageHotEnglishWords"
|
||||||
:key="hotWord"
|
:key="hotWord"
|
||||||
@@ -161,11 +160,11 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
<div>
|
<!-- 参数项:采样方法 -->
|
||||||
<b>采样方法</b>
|
<div class="mt-[30px]">
|
||||||
</div>
|
<div><b>采样方法</b></div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="mt-[15px] w-full">
|
||||||
<Select
|
<Select
|
||||||
v-model:value="sampler"
|
v-model:value="sampler"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
@@ -182,11 +181,11 @@ defineExpose({ settingValues });
|
|||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
<div>
|
<!-- CLIP -->
|
||||||
<b>CLIP</b>
|
<div class="mt-[30px]">
|
||||||
</div>
|
<div><b>CLIP</b></div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="mt-[15px] w-full">
|
||||||
<Select
|
<Select
|
||||||
v-model:value="clipGuidancePreset"
|
v-model:value="clipGuidancePreset"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
@@ -203,11 +202,11 @@ defineExpose({ settingValues });
|
|||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
<div>
|
<!-- 风格 -->
|
||||||
<b>风格</b>
|
<div class="mt-[30px]">
|
||||||
</div>
|
<div><b>风格</b></div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="mt-[15px] w-full">
|
||||||
<Select
|
<Select
|
||||||
v-model:value="stylePreset"
|
v-model:value="stylePreset"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
@@ -225,35 +224,43 @@ defineExpose({ settingValues });
|
|||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
<div>
|
<!-- 图片尺寸 -->
|
||||||
<b>图片尺寸</b>
|
<div class="mt-[30px]">
|
||||||
</div>
|
<div><b>图片尺寸</b></div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="mt-[15px] w-full">
|
||||||
<Input v-model="width" class="w-[170px]" placeholder="图片宽度" />
|
<InputNumber
|
||||||
<Input v-model="height" class="w-[170px]" placeholder="图片高度" />
|
v-model:value="width"
|
||||||
|
class="w-[170px]"
|
||||||
|
placeholder="图片宽度"
|
||||||
|
/>
|
||||||
|
<InputNumber
|
||||||
|
v-model:value="height"
|
||||||
|
class="w-[170px]"
|
||||||
|
placeholder="图片高度"
|
||||||
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
<div>
|
<!-- 迭代步数 -->
|
||||||
<b>迭代步数</b>
|
<div class="mt-[30px]">
|
||||||
</div>
|
<div><b>迭代步数</b></div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="mt-[15px] w-full">
|
||||||
<InputNumber
|
<InputNumber
|
||||||
v-model="steps"
|
v-model:value="steps"
|
||||||
size="large"
|
size="large"
|
||||||
class="!w-[330px]"
|
class="!w-[330px]"
|
||||||
placeholder="Please input"
|
placeholder="Please input"
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
<div>
|
<!-- 引导系数 -->
|
||||||
<b>引导系数</b>
|
<div class="mt-[30px]">
|
||||||
</div>
|
<div><b>引导系数</b></div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="mt-[15px] w-full">
|
||||||
<InputNumber
|
<InputNumber
|
||||||
v-model="scale"
|
v-model:value="scale"
|
||||||
type="number"
|
type="number"
|
||||||
size="large"
|
size="large"
|
||||||
class="!w-[330px]"
|
class="!w-[330px]"
|
||||||
@@ -261,11 +268,11 @@ defineExpose({ settingValues });
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-item">
|
|
||||||
<div>
|
<!-- 随机因子 -->
|
||||||
<b>随机因子</b>
|
<div class="mt-[30px]">
|
||||||
</div>
|
<div><b>随机因子</b></div>
|
||||||
<Space wrap class="group-item-body">
|
<Space wrap class="mt-[15px] w-full">
|
||||||
<InputNumber
|
<InputNumber
|
||||||
v-model:value="seed"
|
v-model:value="seed"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -274,7 +281,9 @@ defineExpose({ settingValues });
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<div class="btns">
|
|
||||||
|
<!-- 生成按钮 -->
|
||||||
|
<div class="mt-[50px] flex justify-center">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -287,38 +296,3 @@ defineExpose({ settingValues });
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
// 热词
|
|
||||||
.hot-words {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.word-list {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
justify-content: start;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模型
|
|
||||||
.group-item {
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
.group-item-body {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -90,15 +90,16 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<div class="ai-image">
|
<div class="ai-image absolute inset-0 flex h-full w-full flex-row">
|
||||||
<div class="left">
|
<div class="left flex w-[390px] flex-col p-5">
|
||||||
<div class="segmented flex justify-center">
|
<div class="segmented flex justify-center">
|
||||||
<Segmented
|
<Segmented
|
||||||
v-model:value="selectPlatform"
|
v-model:value="selectPlatform"
|
||||||
:options="platformOptions"
|
:options="platformOptions"
|
||||||
|
class="bg-[#ececec]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-switch-container">
|
<div class="modal-switch-container mt-[30px] h-full overflow-y-auto">
|
||||||
<Common
|
<Common
|
||||||
v-if="selectPlatform === 'common'"
|
v-if="selectPlatform === 'common'"
|
||||||
ref="commonRef"
|
ref="commonRef"
|
||||||
@@ -125,47 +126,9 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main flex-1 bg-white">
|
||||||
<ImageList ref="imageListRef" @on-regeneration="handleRegeneration" />
|
<ImageList ref="imageListRef" @on-regeneration="handleRegeneration" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.ai-image {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.left {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 390px;
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.segmented .ant-segmented {
|
|
||||||
background-color: #ececec;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-switch-container {
|
|
||||||
height: 100%;
|
|
||||||
margin-top: 30px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex: 1;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
width: 350px;
|
|
||||||
background-color: #f7f8fa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -45,72 +45,44 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<div class="square-container">
|
<div class="bg-[#fff] p-[20px]">
|
||||||
<!-- TODO @fan:style 建议换成 unocss -->
|
|
||||||
<!-- TODO @fan:Search 可以换成 Icon 组件么? -->
|
<!-- TODO @fan:Search 可以换成 Icon 组件么? -->
|
||||||
<Input.Search
|
<Input.Search
|
||||||
v-model="queryParams.prompt"
|
v-model="queryParams.prompt"
|
||||||
style="width: 100%; margin-bottom: 20px"
|
class="mb-[20px] w-full"
|
||||||
size="large"
|
size="large"
|
||||||
placeholder="请输入要搜索的内容"
|
placeholder="请输入要搜索的内容"
|
||||||
@keyup.enter="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
/>
|
/>
|
||||||
<div class="gallery">
|
<div
|
||||||
|
class="grid gap-[10px] bg-[#fff] shadow-[0_0_10px_rgba(0,0,0,0.1)]"
|
||||||
|
style="grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))"
|
||||||
|
>
|
||||||
<!-- TODO @fan:这个图片的风格,要不和 ImageCard.vue 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ -->
|
<!-- TODO @fan:这个图片的风格,要不和 ImageCard.vue 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ -->
|
||||||
<div v-for="item in list" :key="item.id" class="gallery-item">
|
<div
|
||||||
<img :src="item.picUrl" class="img" />
|
v-for="item in list"
|
||||||
|
:key="item.id"
|
||||||
|
class="relative cursor-pointer overflow-hidden bg-[#f0f0f0] transition-transform duration-300 hover:scale-[1.05]"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="item.picUrl"
|
||||||
|
class="block h-auto w-full transition-transform duration-300 hover:scale-[1.1]"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO @fan:缺少翻页 -->
|
<!-- TODO @fan:缺少翻页 -->
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="total"
|
:total="total"
|
||||||
:show-total="(total: number) => `共 ${total} 条`"
|
:show-total="(total) => `共 ${total} 条`"
|
||||||
show-quick-jumper
|
show-quick-jumper
|
||||||
show-size-changer
|
show-size-changer
|
||||||
v-model:current="queryParams.pageNo"
|
v-model:current="queryParams.pageNo"
|
||||||
v-model:page-size="queryParams.pageSize"
|
v-model:page-size="queryParams.pageSize"
|
||||||
@change="debounceGetList"
|
@change="debounceGetList"
|
||||||
@show-size-change="debounceGetList"
|
@show-size-change="debounceGetList"
|
||||||
|
class="mt-[20px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
|
||||||
.square-container {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
.gallery {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
||||||
gap: 10px;
|
|
||||||
//max-width: 1000px;
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 0 10px rgb(0 0 0 / 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
background: #f0f0f0;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item img {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item:hover img {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex w-[350px] flex-col bg-[#f5f7f9] p-5">
|
<div class="flex w-[350px] flex-col bg-[#f5f7f9] p-5">
|
||||||
<h3 class="title w-full text-center leading-[28px]">思维导图创作中心</h3>
|
<h3
|
||||||
|
class="h-[1.75rem] w-full text-center text-[1.25rem] leading-[28px] text-[hsl(var(--primary))]"
|
||||||
|
>
|
||||||
|
思维导图创作中心
|
||||||
|
</h3>
|
||||||
<div class="flex-grow overflow-y-auto">
|
<div class="flex-grow overflow-y-auto">
|
||||||
<div>
|
<div>
|
||||||
<b>您的需求?</b>
|
<b>您的需求?</b>
|
||||||
@@ -67,11 +71,3 @@ defineExpose({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.title {
|
|
||||||
height: 1.75rem;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
color: hsl(var(--primary));
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Card class="my-card h-full flex-grow">
|
<Card class="my-card flex h-full flex-grow flex-col">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="m-0 flex shrink-0 items-center justify-between px-7">
|
<div class="m-0 flex shrink-0 items-center justify-between px-7">
|
||||||
<h3>思维导图预览</h3>
|
<h3>思维导图预览</h3>
|
||||||
@@ -176,9 +176,6 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.my-card {
|
.my-card {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
:deep(.ant-card-body) {
|
:deep(.ant-card-body) {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useVbenDrawer } from '@vben/common-ui';
|
|||||||
import { Button, Input, Select } from 'ant-design-vue';
|
import { Button, Input, Select } from 'ant-design-vue';
|
||||||
|
|
||||||
import { testWorkflow } from '#/api/ai/workflow';
|
import { testWorkflow } from '#/api/ai/workflow';
|
||||||
import Tinyflow from '#/components/Tinyflow/Tinyflow.vue';
|
import Tinyflow from '#/components/tinyflow/tinyflow.vue';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
provider: any;
|
provider: any;
|
||||||
@@ -25,7 +25,7 @@ const error = ref(null);
|
|||||||
const [Drawer, drawerApi] = useVbenDrawer({
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
footer: false,
|
footer: false,
|
||||||
closeOnClickModal: false,
|
closeOnClickModal: false,
|
||||||
modal: false
|
modal: false,
|
||||||
});
|
});
|
||||||
/** 展示工作流测试抽屉 */
|
/** 展示工作流测试抽屉 */
|
||||||
const testWorkflowModel = () => {
|
const testWorkflowModel = () => {
|
||||||
@@ -180,7 +180,7 @@ defineExpose({ validate });
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative" style="width: 100%; height: 700px">
|
<div class="relative h-[700px] w-[100%]">
|
||||||
<Tinyflow
|
<Tinyflow
|
||||||
v-if="workflowData"
|
v-if="workflowData"
|
||||||
ref="tinyflowRef"
|
ref="tinyflowRef"
|
||||||
@@ -198,12 +198,19 @@ defineExpose({ validate });
|
|||||||
测试
|
测试
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Drawer title="工作流测试">
|
<Drawer title="工作流测试">
|
||||||
<fieldset>
|
<fieldset
|
||||||
<legend class="ml-2"><h3>运行参数配置</h3></legend>
|
class="min-inline-size-auto m-0 rounded-[6px] border border-[#dcdfe6] p-[12px_16px]"
|
||||||
<div class="p-2">
|
>
|
||||||
|
<legend
|
||||||
|
class="ml-[8px] px-[10px] text-[16px] font-semibold text-[#303133]"
|
||||||
|
>
|
||||||
|
<h3>运行参数配置</h3>
|
||||||
|
</legend>
|
||||||
|
<div class="p-[8px]">
|
||||||
<div
|
<div
|
||||||
class="mb-1 flex justify-around"
|
class="mb-[4px] flex items-center justify-around"
|
||||||
v-for="(param, index) in params4Test"
|
v-for="(param, index) in params4Test"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
@@ -218,7 +225,7 @@ defineExpose({ validate });
|
|||||||
</Select.Option>
|
</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Input
|
<Input
|
||||||
class="w-[200px]"
|
class="mx-[8px] w-[200px]"
|
||||||
v-model:value="param.value"
|
v-model:value="param.value"
|
||||||
placeholder="参数值"
|
placeholder="参数值"
|
||||||
/>
|
/>
|
||||||
@@ -228,26 +235,35 @@ defineExpose({ validate });
|
|||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Button type="primary" plain @click="addParam">添加参数</Button>
|
<Button type="primary" plain class="mt-[8px]" @click="addParam">
|
||||||
|
添加参数
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="mt-2" style="background-color: #f8f9fa">
|
|
||||||
<legend class="ml-2"><h3>运行结果</h3></legend>
|
<fieldset
|
||||||
<div class="p-2">
|
class="min-inline-size-auto m-0 mt-[8px] rounded-[6px] border border-[#dcdfe6] bg-[#f8f9fa] p-[12px_16px]"
|
||||||
<div v-if="loading"><el-text type="primary">执行中...</el-text></div>
|
>
|
||||||
<div v-else-if="error">
|
<legend
|
||||||
<el-text type="danger">{{ error }}</el-text>
|
class="ml-[8px] px-[10px] text-[16px] font-semibold text-[#303133]"
|
||||||
</div>
|
>
|
||||||
<pre v-else-if="testResult" class="result-content">
|
<h3>运行结果</h3>
|
||||||
{{ JSON.stringify(testResult, null, 2) }}
|
</legend>
|
||||||
|
<div class="p-[8px]">
|
||||||
|
<div v-if="loading" class="text-primary">执行中...</div>
|
||||||
|
<div v-else-if="error" class="text-danger">{{ error }}</div>
|
||||||
|
<pre
|
||||||
|
v-else-if="testResult"
|
||||||
|
class="max-h-[300px] overflow-auto whitespace-pre-wrap rounded-[4px] bg-white p-[12px] font-mono text-[14px] leading-[1.5]"
|
||||||
|
>{{ JSON.stringify(testResult, null, 2) }}
|
||||||
</pre>
|
</pre>
|
||||||
<div v-else style="color: #909399">点击运行查看结果</div>
|
<div v-else class="text-[#909399]">点击运行查看结果</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
class="mt-2"
|
|
||||||
size="large"
|
size="large"
|
||||||
style="width: 100%; color: white; background-color: #67c23a"
|
class="mt-[8px] w-[100%] bg-[#67c23a] text-white"
|
||||||
@click="goRun"
|
@click="goRun"
|
||||||
>
|
>
|
||||||
运行流程
|
运行流程
|
||||||
@@ -255,32 +271,3 @@ defineExpose({ validate });
|
|||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.result-content {
|
|
||||||
max-height: 300px;
|
|
||||||
padding: 12px;
|
|
||||||
overflow: auto;
|
|
||||||
font-family: Monaco, Consolas, monospace;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
background: white;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
min-inline-size: auto;
|
|
||||||
padding: 12px 16px;
|
|
||||||
margin: 0;
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend {
|
|
||||||
padding: 0 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ watch(copied, (val) => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Card class="my-card h-full">
|
<Card class="my-card flex h-full flex-col">
|
||||||
<template #title>
|
<template #title>
|
||||||
<h3 class="m-0 flex shrink-0 items-center justify-between px-7">
|
<h3 class="m-0 flex shrink-0 items-center justify-between px-7">
|
||||||
<span>预览</span>
|
<span>预览</span>
|
||||||
@@ -123,9 +123,6 @@ watch(copied, (val) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.my-card {
|
.my-card {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
:deep(.ant-card-body) {
|
:deep(.ant-card-body) {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|||||||
@@ -29,4 +29,3 @@ const emits = defineEmits<{
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped></style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user