refactor(web-antd): 修正 Tinyflow 组件中的导入路径
- 将 './ui/typing' 修改为 './ui/typeing'(可能是原代码中的拼写错误) - 将 './ui/typing' 修改为 './ui/index',以符合常见的模块入口文件结构pull/145/head
parent
0f701dd49b
commit
31a98ba9f8
|
|
@ -1,9 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import type { Item } from './ui/typing';
|
||||
import type { Item } from './ui/typeing';
|
||||
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
import { Tinyflow as TinyflowNative } from './ui/typing';
|
||||
import { Tinyflow as TinyflowNative } from './ui/index';
|
||||
|
||||
import './ui/index.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,55 @@
|
|||
import { Edge, Node as Node_2, useSvelteFlow, Viewport } from '@xyflow/svelte';
|
||||
|
||||
export declare type Item = {
|
||||
children?: Item[];
|
||||
label: 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 {
|
||||
private _init;
|
||||
private _setOptions;
|
||||
|
|
@ -16,25 +60,11 @@ export declare class Tinyflow {
|
|||
destroy(): void;
|
||||
getData(): {
|
||||
edges: Edge[];
|
||||
nodes: Node_2[];
|
||||
nodes: Node[];
|
||||
viewport: Viewport;
|
||||
};
|
||||
getOptions(): TinyflowOptions;
|
||||
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 {};
|
||||
|
|
@ -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>
|
||||
|
||||
<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 />
|
||||
<!-- 左顶部:对话 -->
|
||||
<div class="flex h-full" style="flex-direction: column">
|
||||
<div class="flex h-full flex-col">
|
||||
<Button
|
||||
class="w-1/1 btn-new-conversation"
|
||||
class="btn-new-conversation h-[38px] w-full"
|
||||
type="primary"
|
||||
@click="createConversation"
|
||||
>
|
||||
<IconifyIcon icon="ep:plus" class="mr-[5px]" />
|
||||
新建对话
|
||||
</Button>
|
||||
<!-- 左顶部:搜索对话 -->
|
||||
|
||||
<Input
|
||||
v-model:value="searchName"
|
||||
size="large"
|
||||
class="search-input mt-[10px]"
|
||||
class="search-input mt-[20px]"
|
||||
placeholder="搜索历史记录"
|
||||
@keyup="searchConversation"
|
||||
>
|
||||
|
|
@ -329,50 +332,57 @@ onMounted(async () => {
|
|||
</Input>
|
||||
|
||||
<!-- 左中间:对话列表 -->
|
||||
<div class="conversation-list">
|
||||
<div class="conversation-list mt-[10px] flex-1 overflow-auto">
|
||||
<!-- 情况一:加载中 -->
|
||||
<Empty v-if="loading" description="." v-loading="loading" />
|
||||
<!-- 情况二:按照 group 分组,展示聊天会话 list 列表 -->
|
||||
|
||||
<!-- 情况二:按照 group 分组 -->
|
||||
<div
|
||||
v-for="conversationKey in Object.keys(conversationMap)"
|
||||
:key="conversationKey"
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="conversation-item classify-title"
|
||||
v-if="conversationMap[conversationKey].length > 0"
|
||||
class="conversation-item classify-title pt-[10px]"
|
||||
>
|
||||
<b class="mx-1">
|
||||
<b class="mx-[4px]">
|
||||
{{ conversationKey }}
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="conversation-item"
|
||||
v-for="conversation in conversationMap[conversationKey]"
|
||||
:key="conversation.id"
|
||||
@click="handleConversationClick(conversation.id)"
|
||||
@mouseover="hoverConversationId = conversation.id"
|
||||
@mouseout="hoverConversationId = null"
|
||||
class="conversation-item mt-[5px]"
|
||||
>
|
||||
<div
|
||||
:class="
|
||||
conversation.id === activeConversationId
|
||||
? 'conversation active'
|
||||
: 'conversation'
|
||||
"
|
||||
class="conversation flex cursor-pointer flex-row items-center justify-between rounded-[5px] px-[5px] leading-[30px]"
|
||||
:class="[
|
||||
conversation.id === activeConversationId ? 'bg-[#e6e6e6]' : '',
|
||||
]"
|
||||
>
|
||||
<div class="title-wrapper">
|
||||
<div class="title-wrapper flex items-center">
|
||||
<img
|
||||
class="avatar"
|
||||
class="avatar h-[25px] w-[25px] rounded-[5px]"
|
||||
: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
|
||||
class="button-wrapper"
|
||||
v-show="hoverConversationId === conversation.id"
|
||||
class="button-wrapper relative right-[2px] flex items-center text-[#606266]"
|
||||
>
|
||||
<Button
|
||||
class="btn"
|
||||
class="btn mr-0 px-[5px]"
|
||||
type="link"
|
||||
@click.stop="handleTop(conversation)"
|
||||
>
|
||||
|
|
@ -386,14 +396,14 @@ onMounted(async () => {
|
|||
></span>
|
||||
</Button>
|
||||
<Button
|
||||
class="btn"
|
||||
class="btn mr-0 px-[5px]"
|
||||
type="link"
|
||||
@click.stop="updateConversationTitle(conversation)"
|
||||
>
|
||||
<IconifyIcon icon="ep:edit" />
|
||||
</Button>
|
||||
<Button
|
||||
class="btn"
|
||||
class="btn mr-0 px-[5px]"
|
||||
type="link"
|
||||
@click.stop="deleteChatConversation(conversation)"
|
||||
>
|
||||
|
|
@ -404,143 +414,29 @@ onMounted(async () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部占位 -->
|
||||
<div class="w-100% h-[50px]"></div>
|
||||
|
||||
<!-- 底部占位 -->
|
||||
<div class="h-[50px] w-full"></div>
|
||||
</div>
|
||||
|
||||
<!-- 左底部:工具栏 -->
|
||||
<div class="tool-box">
|
||||
<div @click="handleRoleRepository">
|
||||
<div
|
||||
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" />
|
||||
<span>角色仓库</span>
|
||||
<span class="ml-[5px]">角色仓库</span>
|
||||
</div>
|
||||
<div @click="handleClearConversation">
|
||||
<div
|
||||
class="flex cursor-pointer items-center text-[#606266]"
|
||||
@click="handleClearConversation"
|
||||
>
|
||||
<IconifyIcon icon="ep:delete" />
|
||||
<span>清空未置顶对话</span>
|
||||
<span class="ml-[5px]">清空未置顶对话</span>
|
||||
</div>
|
||||
</div>
|
||||
</Layout.Sider>
|
||||
</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>
|
||||
<template>
|
||||
<div ref="messageContainer" class="relative h-full overflow-y-auto">
|
||||
<div class="chat-list" v-for="(item, index) in list" :key="index">
|
||||
<!-- 靠左 message:system、assistant 类型 -->
|
||||
<div class="left-message message-item" v-if="item.type !== 'user'">
|
||||
<div
|
||||
v-for="(item, index) in list"
|
||||
: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">
|
||||
<Avatar :src="roleAvatar" />
|
||||
</div>
|
||||
<div class="message">
|
||||
<div>
|
||||
<div class="time">{{ formatDate(item.createTime) }}</div>
|
||||
<div class="mx-[15px] flex flex-col text-left">
|
||||
<div class="text-left leading-[30px]">
|
||||
{{ formatDate(item.createTime) }}
|
||||
</div>
|
||||
<div class="left-text-container">
|
||||
<MarkdownView class="left-text" :content="item.content" />
|
||||
<div
|
||||
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" />
|
||||
</div>
|
||||
<div class="left-btns">
|
||||
<div class="mt-[8px] flex flex-row">
|
||||
<Button
|
||||
class="btn-cus"
|
||||
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||
type="text"
|
||||
@click="copyContent(item.content)"
|
||||
>
|
||||
<img class="btn-image" src="/static/copy.svg" />
|
||||
<img class="h-[20px]" src="/static/copy.svg" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="item.id > 0"
|
||||
class="btn-cus"
|
||||
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||
type="text"
|
||||
@click="onDelete(item.id)"
|
||||
>
|
||||
<img class="btn-image h-[17px]" src="/static/delete.svg" />
|
||||
<img class="h-[17px]" src="/static/delete.svg" />
|
||||
</Button>
|
||||
</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">
|
||||
<Avatar :src="userAvatar" />
|
||||
</div>
|
||||
<div class="message">
|
||||
<div>
|
||||
<div class="time">{{ formatDate(item.createTime) }}</div>
|
||||
<div class="mx-[15px] flex flex-col text-left">
|
||||
<div class="text-left leading-[30px]">
|
||||
{{ formatDate(item.createTime) }}
|
||||
</div>
|
||||
<div class="right-text-container">
|
||||
<div class="right-text">{{ item.content }}</div>
|
||||
<div class="flex flex-row-reverse">
|
||||
<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 class="mt-[8px] flex flex-row-reverse">
|
||||
<Button
|
||||
class="btn-cus"
|
||||
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||
type="text"
|
||||
@click="copyContent(item.content)"
|
||||
>
|
||||
<img class="btn-image" src="/static/copy.svg" />
|
||||
<img class="h-[20px]" src="/static/copy.svg" />
|
||||
</Button>
|
||||
<Button class="btn-cus" type="text" @click="onDelete(item.id)">
|
||||
<img class="btn-image h-[17px]" src="/static/delete.svg" />
|
||||
<Button
|
||||
class="flex items-center bg-transparent px-[5px] hover:bg-[#f6f6f6]"
|
||||
type="text"
|
||||
@click="onDelete(item.id)"
|
||||
>
|
||||
<img class="h-[17px]" src="/static/delete.svg" />
|
||||
</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>
|
||||
</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>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -180,117 +206,15 @@ onMounted(async () => {
|
|||
</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">
|
||||
<span class="icon-[ant-design--down-outlined]"></span>
|
||||
</Button>
|
||||
</div>
|
||||
</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>
|
||||
<template>
|
||||
<div class="chat-empty">
|
||||
<!-- title -->
|
||||
<div class="center-container">
|
||||
<div class="title">芋道 AI</div>
|
||||
<div class="role-list">
|
||||
<div class="relative flex h-full w-full flex-row justify-center">
|
||||
<!-- center-container -->
|
||||
<div class="flex flex-col justify-center">
|
||||
<!-- title -->
|
||||
<div class="text-center text-[28px] font-bold">芋道 AI</div>
|
||||
|
||||
<!-- role-list -->
|
||||
<div
|
||||
class="mt-[20px] flex w-[460px] flex-wrap items-center justify-center"
|
||||
>
|
||||
<div
|
||||
class="role-item"
|
||||
v-for="prompt in promptList"
|
||||
:key="prompt.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 }}
|
||||
</div>
|
||||
|
|
@ -33,49 +38,3 @@ const handlerPromptClick = async (prompt: any) => {
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<template>
|
||||
<div class="message-loading">
|
||||
<div class="p-[30px]">
|
||||
<Skeleton active />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.message-loading {
|
||||
padding: 30px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -10,39 +10,14 @@ const handlerNewChat = () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="new-chat">
|
||||
<div class="box-center">
|
||||
<div class="tip">点击下方按钮,开始你的对话吧</div>
|
||||
<div class="btns">
|
||||
<div class="flex h-full w-full flex-row justify-center">
|
||||
<div class="flex flex-col justify-center">
|
||||
<div class="text-center text-[14px] text-[#858585]">
|
||||
点击下方按钮,开始你的对话吧
|
||||
</div>
|
||||
<div class="mt-[20px] flex flex-row justify-center">
|
||||
<Button type="primary" round @click="handlerNewChat">新建对话</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<template>
|
||||
<div class="category-list">
|
||||
<div class="category" v-for="category in categoryList" :key="category">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div
|
||||
class="mr-[10px] flex flex-row"
|
||||
v-for="category in categoryList"
|
||||
:key="category"
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
shape="round"
|
||||
|
|
@ -38,17 +42,3 @@ const handleCategoryClick = async (category: string) => {
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<template>
|
||||
<div class="card-list" ref="tabsRef" @scroll="handleTabsScroll">
|
||||
<div class="card-item" v-for="role in roleList" :key="role.id">
|
||||
<div
|
||||
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
|
||||
class="card"
|
||||
class="relative rounded-[10px]"
|
||||
:body-style="{
|
||||
position: 'relative',
|
||||
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>
|
||||
<Button type="text">
|
||||
<span class="icon-[ant-design--more-outlined] text-2xl"></span>
|
||||
|
|
@ -91,23 +99,32 @@ const handleTabsScroll = async () => {
|
|||
<Menu.Item @click="handleMoreClick(['delete', role])">
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ep:delete" color="red" />
|
||||
<span style="color: red">编辑</span>
|
||||
<span class="text-red-500">编辑</span>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
<!-- 角色信息 -->
|
||||
<div>
|
||||
<img class="avatar" :src="role.avatar" />
|
||||
<img
|
||||
:src="role.avatar"
|
||||
class="h-[40px] w-[40px] overflow-hidden rounded-[10px]"
|
||||
/>
|
||||
</div>
|
||||
<div class="right-container">
|
||||
<div class="content-container">
|
||||
<div class="title">{{ role.name }}</div>
|
||||
<div class="description">{{ role.description }}</div>
|
||||
|
||||
<div class="ml-[10px] w-full">
|
||||
<div class="h-[85px]">
|
||||
<div class="max-w-[140px] text-[18px] font-bold text-[#3e3e3e]">
|
||||
{{ role.name }}
|
||||
</div>
|
||||
<div class="mt-[10px] text-[14px] text-[#6a6a6a]">
|
||||
{{ role.description }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-container">
|
||||
<div class="mt-[2px] flex flex-row-reverse">
|
||||
<Button type="primary" size="small" @click="handleUseClick(role)">
|
||||
使用
|
||||
</Button>
|
||||
|
|
@ -117,67 +134,3 @@ const handleTabsScroll = async () => {
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<Drawer>
|
||||
<Layout class="role-container">
|
||||
<Layout
|
||||
class="absolute inset-0 flex h-full w-full flex-col overflow-hidden bg-white"
|
||||
>
|
||||
<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
|
||||
:loading="loading"
|
||||
v-model:value="search"
|
||||
class="search-input"
|
||||
class="w-[240px]"
|
||||
placeholder="请输入搜索的内容"
|
||||
@search="getActiveTabsRole"
|
||||
/>
|
||||
|
|
@ -193,12 +196,18 @@ onMounted(async () => {
|
|||
添加角色
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- 标签页内容 -->
|
||||
<Tabs
|
||||
v-model:value="activeTab"
|
||||
class="tabs p-4"
|
||||
class="relative h-full p-4"
|
||||
@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
|
||||
:loading="loading"
|
||||
:role-list="myRoleList"
|
||||
|
|
@ -210,12 +219,17 @@ onMounted(async () => {
|
|||
class="mt-[20px]"
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane key="public-role" class="role-pane" tab="公共角色">
|
||||
|
||||
<TabPane
|
||||
key="public-role"
|
||||
class="flex h-full flex-col overflow-y-auto"
|
||||
tab="公共角色"
|
||||
>
|
||||
<RoleCategoryList
|
||||
class="role-category-list"
|
||||
:category-list="categoryList"
|
||||
:active="activeCategory"
|
||||
@on-category-click="handlerCategoryClick"
|
||||
class="mx-[27px]"
|
||||
/>
|
||||
<RoleList
|
||||
:role-list="publicRoleList"
|
||||
|
|
@ -232,56 +246,3 @@ onMounted(async () => {
|
|||
</Layout>
|
||||
</Drawer>
|
||||
</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>
|
||||
<Page auto-content-height>
|
||||
<Layout class="ai-layout">
|
||||
<Layout class="absolute left-0 top-0 h-full w-full flex-1">
|
||||
<!-- 左侧:对话列表 -->
|
||||
<ConversationList
|
||||
:active-id="activeConversationId"
|
||||
|
|
@ -504,19 +504,24 @@ onMounted(async () => {
|
|||
@on-conversation-clear="handleConversationClear"
|
||||
@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 : '对话' }}
|
||||
<span v-if="activeMessageList.length > 0">
|
||||
({{ activeMessageList.length }})
|
||||
</span>
|
||||
</div>
|
||||
<div class="btns" v-if="activeConversation">
|
||||
|
||||
<div class="flex w-[300px] justify-end" v-if="activeConversation">
|
||||
<Button
|
||||
type="primary"
|
||||
ghost
|
||||
class="mr-[10px]"
|
||||
class="mr-[10px] px-[10px]"
|
||||
size="small"
|
||||
@click="openChatConversationUpdateForm"
|
||||
>
|
||||
|
|
@ -525,7 +530,7 @@ onMounted(async () => {
|
|||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
class="btn mr-[10px]"
|
||||
class="mr-[10px] px-[10px]"
|
||||
@click="handlerMessageClear"
|
||||
>
|
||||
<IconifyIcon
|
||||
|
|
@ -533,54 +538,52 @@ onMounted(async () => {
|
|||
color="#787878"
|
||||
/>
|
||||
</Button>
|
||||
<Button size="small" class="btn mr-[10px]">
|
||||
<Button size="small" class="mr-[10px] px-[10px]">
|
||||
<IconifyIcon icon="ep:download" color="#787878" />
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
class="btn mr-[10px]"
|
||||
class="mr-[10px] px-[10px]"
|
||||
@click="handleGoTopMessage"
|
||||
>
|
||||
<IconifyIcon icon="ep:top" color="#787878" />
|
||||
</Button>
|
||||
</div>
|
||||
</Layout.Header>
|
||||
<Layout.Content class="main-container">
|
||||
<div>
|
||||
<div class="message-container">
|
||||
<!-- 情况一:消息加载中 -->
|
||||
<MessageLoading v-if="activeMessageListLoading" />
|
||||
<!-- 情况二:无聊天对话时 -->
|
||||
<MessageNewConversation
|
||||
v-if="!activeConversation"
|
||||
@on-new-conversation="handleConversationCreate"
|
||||
/>
|
||||
<!-- 情况三:消息列表为空 -->
|
||||
<MessageListEmpty
|
||||
v-if="
|
||||
!activeMessageListLoading &&
|
||||
messageList.length === 0 &&
|
||||
activeConversation
|
||||
"
|
||||
@on-prompt="doSendMessage"
|
||||
/>
|
||||
<!-- 情况四:消息列表不为空 -->
|
||||
<MessageList
|
||||
v-if="!activeMessageListLoading && messageList.length > 0"
|
||||
ref="messageRef"
|
||||
:conversation="activeConversation"
|
||||
:list="messageList"
|
||||
@on-delete-success="handleMessageDelete"
|
||||
@on-edit="handleMessageEdit"
|
||||
@on-refresh="handleMessageRefresh"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Layout.Content class="relative m-0 h-full w-full p-0">
|
||||
<div class="absolute inset-0 m-0 overflow-y-hidden p-0">
|
||||
<MessageLoading v-if="activeMessageListLoading" />
|
||||
<MessageNewConversation
|
||||
v-if="!activeConversation"
|
||||
@on-new-conversation="handleConversationCreate"
|
||||
/>
|
||||
<MessageListEmpty
|
||||
v-if="
|
||||
!activeMessageListLoading &&
|
||||
messageList.length === 0 &&
|
||||
activeConversation
|
||||
"
|
||||
@on-prompt="doSendMessage"
|
||||
/>
|
||||
<MessageList
|
||||
v-if="!activeMessageListLoading && messageList.length > 0"
|
||||
ref="messageRef"
|
||||
:conversation="activeConversation"
|
||||
:list="messageList"
|
||||
@on-delete-success="handleMessageDelete"
|
||||
@on-edit="handleMessageEdit"
|
||||
@on-refresh="handleMessageRefresh"
|
||||
/>
|
||||
</div>
|
||||
</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
|
||||
class="prompt-input"
|
||||
class="box-border h-[80px] resize-none overflow-auto border-none p-[0_2px] focus:outline-none"
|
||||
v-model="prompt"
|
||||
@keydown="handleSendByKeydown"
|
||||
@input="handlePromptInput"
|
||||
|
|
@ -588,10 +591,10 @@ onMounted(async () => {
|
|||
@compositionend="onCompositionend"
|
||||
placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)"
|
||||
></textarea>
|
||||
<div class="prompt-btns">
|
||||
<div>
|
||||
<div class="flex justify-between pb-0 pt-[5px]">
|
||||
<div class="flex items-center">
|
||||
<Switch v-model:checked="enableContext" />
|
||||
<span class="ml-5px text-14px text-#8f8f8f">上下文</span>
|
||||
<span class="ml-[5px] text-[14px] text-[#8f8f8f]">上下文</span>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
@ -616,202 +619,3 @@ onMounted(async () => {
|
|||
<FormModal @success="handleConversationUpdateSuccess" />
|
||||
</Page>
|
||||
</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>
|
||||
<template>
|
||||
<Card body-class="" class="image-card">
|
||||
<div class="image-operation">
|
||||
<Card
|
||||
body-class=""
|
||||
class="relative flex h-auto w-[320px] flex-col rounded-[10px]"
|
||||
>
|
||||
<!-- 图片操作区 -->
|
||||
<div class="flex flex-row justify-between">
|
||||
<div>
|
||||
<Button v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS">
|
||||
生成中
|
||||
|
|
@ -78,31 +82,30 @@ onMounted(async () => {
|
|||
异常
|
||||
</Button>
|
||||
</div>
|
||||
<!-- 操作区 -->
|
||||
<div>
|
||||
<div class="flex">
|
||||
<Button
|
||||
class="btn"
|
||||
class="m-0 p-[10px]"
|
||||
type="text"
|
||||
@click="handleButtonClick('download', detail)"
|
||||
>
|
||||
<span class="icon-[ant-design--download-outlined]"></span>
|
||||
</Button>
|
||||
<Button
|
||||
class="btn"
|
||||
class="m-0 p-[10px]"
|
||||
type="text"
|
||||
@click="handleButtonClick('regeneration', detail)"
|
||||
>
|
||||
<span class="icon-[ant-design--redo-outlined]"></span>
|
||||
</Button>
|
||||
<Button
|
||||
class="btn"
|
||||
class="m-0 p-[10px]"
|
||||
type="text"
|
||||
@click="handleButtonClick('delete', detail)"
|
||||
>
|
||||
<span class="icon-[ant-design--delete-outlined]"></span>
|
||||
</Button>
|
||||
<Button
|
||||
class="btn"
|
||||
class="m-0 p-[10px]"
|
||||
type="text"
|
||||
@click="handleButtonClick('more', detail)"
|
||||
>
|
||||
|
|
@ -110,14 +113,17 @@ onMounted(async () => {
|
|||
</Button>
|
||||
</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">
|
||||
{{ detail?.errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Midjourney 专属操作 -->
|
||||
<div class="image-mj-btns">
|
||||
|
||||
<!-- Midjourney 专属操作按钮 -->
|
||||
<div class="mt-[5px] flex w-full flex-wrap justify-start">
|
||||
<Button
|
||||
size="small"
|
||||
v-for="(button, index) in detail?.buttons"
|
||||
|
|
@ -130,46 +136,3 @@ onMounted(async () => {
|
|||
</div>
|
||||
</Card>
|
||||
</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">
|
||||
import type { AiImageApi } from '#/api/ai/image';
|
||||
import type { ImageModelVO } from '#/utils/constants';
|
||||
|
||||
import { ref, toRefs, watch } from 'vue';
|
||||
|
||||
|
|
@ -43,15 +42,16 @@ watch(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="item">
|
||||
<div class="body">
|
||||
<Image class="image" :src="detail?.picUrl" />
|
||||
<div class="mb-5 w-full overflow-hidden break-words">
|
||||
<div class="body mt-2 text-gray-600">
|
||||
<Image class="rounded-[10px]" :src="detail?.picUrl" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 时间 -->
|
||||
<div class="item">
|
||||
<div class="tip">时间</div>
|
||||
<div class="body">
|
||||
<div class="mb-5 w-full overflow-hidden break-words">
|
||||
<div class="tip text-lg font-bold">时间</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
<div>
|
||||
提交时间:{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
|
||||
</div>
|
||||
|
|
@ -60,177 +60,150 @@ watch(
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模型 -->
|
||||
<div class="item">
|
||||
<div class="tip">模型</div>
|
||||
<div class="body">
|
||||
<div class="mb-5 w-full overflow-hidden break-words">
|
||||
<div class="tip text-lg font-bold">模型</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
{{ detail.model }}({{ detail.height }}x{{ detail.width }})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示词 -->
|
||||
<div class="item">
|
||||
<div class="tip">提示词</div>
|
||||
<div class="body">
|
||||
<div class="mb-5 w-full overflow-hidden break-words">
|
||||
<div class="tip text-lg font-bold">提示词</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
{{ detail.prompt }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 地址 -->
|
||||
<div class="item">
|
||||
<div class="tip">图片地址</div>
|
||||
<div class="body">
|
||||
|
||||
<!-- 图片地址 -->
|
||||
<div class="mb-5 w-full overflow-hidden break-words">
|
||||
<div class="tip text-lg font-bold">图片地址</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
{{ detail.picUrl }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- StableDiffusion 专属区域 -->
|
||||
|
||||
<!-- StableDiffusion 专属 -->
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||
detail?.options?.sampler
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">采样方法</div>
|
||||
<div class="body">
|
||||
<div class="tip text-lg font-bold">采样方法</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
{{
|
||||
StableDiffusionSamplers.find(
|
||||
(item: ImageModelVO) => item.key === detail?.options?.sampler,
|
||||
(item) => item.key === detail?.options?.sampler,
|
||||
)?.name
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||
detail?.options?.clipGuidancePreset
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">CLIP</div>
|
||||
<div class="body">
|
||||
<div class="tip text-lg font-bold">CLIP</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
{{
|
||||
StableDiffusionClipGuidancePresets.find(
|
||||
(item: ImageModelVO) =>
|
||||
item.key === detail?.options?.clipGuidancePreset,
|
||||
(item) => item.key === detail?.options?.clipGuidancePreset,
|
||||
)?.name
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||
detail?.options?.stylePreset
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">风格</div>
|
||||
<div class="body">
|
||||
<div class="tip text-lg font-bold">风格</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
{{
|
||||
StableDiffusionStylePresets.find(
|
||||
(item: ImageModelVO) => item.key === detail?.options?.stylePreset,
|
||||
(item) => item.key === detail?.options?.stylePreset,
|
||||
)?.name
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||
detail?.options?.steps
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">迭代步数</div>
|
||||
<div class="body">
|
||||
{{ detail?.options?.steps }}
|
||||
</div>
|
||||
<div class="tip text-lg font-bold">迭代步数</div>
|
||||
<div class="body mt-2 text-gray-600">{{ detail?.options?.steps }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||
detail?.options?.scale
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">引导系数</div>
|
||||
<div class="body">
|
||||
{{ detail?.options?.scale }}
|
||||
</div>
|
||||
<div class="tip text-lg font-bold">引导系数</div>
|
||||
<div class="body mt-2 text-gray-600">{{ detail?.options?.scale }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.STABLE_DIFFUSION &&
|
||||
detail?.options?.seed
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">随机因子</div>
|
||||
<div class="body">
|
||||
{{ detail?.options?.seed }}
|
||||
</div>
|
||||
<div class="tip text-lg font-bold">随机因子</div>
|
||||
<div class="body mt-2 text-gray-600">{{ detail?.options?.seed }}</div>
|
||||
</div>
|
||||
<!-- Dall3 专属区域 -->
|
||||
|
||||
<!-- Dall3 专属 -->
|
||||
<div
|
||||
class="item"
|
||||
v-if="detail.platform === AiPlatformEnum.OPENAI && detail?.options?.style"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">风格选择</div>
|
||||
<div class="body">
|
||||
<div class="tip text-lg font-bold">风格选择</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
{{
|
||||
Dall3StyleList.find(
|
||||
(item: ImageModelVO) => item.key === detail?.options?.style,
|
||||
)?.name
|
||||
Dall3StyleList.find((item) => item.key === detail?.options?.style)?.name
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Midjourney 专属区域 -->
|
||||
|
||||
<!-- Midjourney 专属 -->
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.version
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">模型版本</div>
|
||||
<div class="body">
|
||||
{{ detail?.options?.version }}
|
||||
</div>
|
||||
<div class="tip text-lg font-bold">模型版本</div>
|
||||
<div class="body mt-2 text-gray-600">{{ detail?.options?.version }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="item"
|
||||
v-if="
|
||||
detail.platform === AiPlatformEnum.MIDJOURNEY &&
|
||||
detail?.options?.referImageUrl
|
||||
"
|
||||
class="mb-5 w-full overflow-hidden break-words"
|
||||
>
|
||||
<div class="tip">参考图</div>
|
||||
<div class="body">
|
||||
<div class="tip text-lg font-bold">参考图</div>
|
||||
<div class="body mt-2 text-gray-600">
|
||||
<Image :src="detail.options.referImageUrl" />
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<template>
|
||||
<Drawer class="w-[600px]">
|
||||
<ImageDetail :id="showImageDetailId" />
|
||||
</Drawer>
|
||||
<Card
|
||||
class="dr-task"
|
||||
class="dr-task flex h-full w-full flex-col"
|
||||
:body-style="{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
绘画任务
|
||||
<!-- TODO @fan:看看,怎么优化下这个样子哈。 -->
|
||||
<Button @click="handleViewPublic">绘画作品</Button>
|
||||
</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
|
||||
v-for="image in imageList"
|
||||
:key="image.id"
|
||||
:detail="image"
|
||||
@on-btn-click="handleImageButtonClick"
|
||||
@on-mj-btn-click="handleImageMidjourneyButtonClick"
|
||||
class="mb-5 mr-5"
|
||||
/>
|
||||
</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
|
||||
:total="pageTotal"
|
||||
:show-total="(total: number) => `共 ${total} 条`"
|
||||
:show-total="(total) => `共 ${total} 条`"
|
||||
show-quick-jumper
|
||||
show-size-changer
|
||||
v-model:current="queryParams.pageNo"
|
||||
|
|
@ -208,43 +216,3 @@ onUnmounted(async () => {
|
|||
</div>
|
||||
</Card>
|
||||
</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"
|
||||
:maxlength="1024"
|
||||
:rows="5"
|
||||
class="w-100% mt-[15px]"
|
||||
class="mt-[15px] w-full"
|
||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||
show-count
|
||||
/>
|
||||
</div>
|
||||
<div class="hot-words">
|
||||
|
||||
<div class="hot-words mt-[30px] flex flex-col">
|
||||
<div>
|
||||
<b>随机热词</b>
|
||||
</div>
|
||||
<Space wrap class="word-list">
|
||||
<Space wrap class="word-list mt-[15px] flex flex-wrap justify-start">
|
||||
<Button
|
||||
shape="round"
|
||||
class="btn"
|
||||
class="btn m-0"
|
||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||
v-for="hotWord in ImageHotWords"
|
||||
:key="hotWord"
|
||||
|
|
@ -139,16 +140,17 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
|
||||
<div class="group-item mt-[30px]">
|
||||
<div>
|
||||
<b>平台</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
<Space wrap class="group-item-body mt-[15px] w-full">
|
||||
<Select
|
||||
v-model:value="otherPlatform"
|
||||
placeholder="Select"
|
||||
size="large"
|
||||
class="!w-[330px]"
|
||||
class="!important w-[330px]"
|
||||
@change="handlerPlatformChange"
|
||||
>
|
||||
<Select.Option
|
||||
|
|
@ -161,16 +163,17 @@ defineExpose({ settingValues });
|
|||
</Select>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
|
||||
<div class="group-item mt-[30px]">
|
||||
<div>
|
||||
<b>模型</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
<Space wrap class="group-item-body mt-[15px] w-full">
|
||||
<Select
|
||||
v-model:value="modelId"
|
||||
placeholder="Select"
|
||||
size="large"
|
||||
class="!w-[330px]"
|
||||
class="!important w-[330px]"
|
||||
>
|
||||
<Select.Option
|
||||
v-for="item in platformModels"
|
||||
|
|
@ -182,11 +185,12 @@ defineExpose({ settingValues });
|
|||
</Select>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
|
||||
<div class="group-item mt-[30px]">
|
||||
<div>
|
||||
<b>图片尺寸</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
<Space wrap class="group-item-body mt-[15px] flex flex-wrap gap-x-[20px]">
|
||||
<InputNumber
|
||||
v-model:value="width"
|
||||
class="mt-[10px] w-[170px]"
|
||||
|
|
@ -199,7 +203,8 @@ defineExpose({ settingValues });
|
|||
/>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="btns">
|
||||
|
||||
<div class="btns mt-[50px] flex justify-center">
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
|
|
@ -212,37 +217,3 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</div>
|
||||
</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"
|
||||
:maxlength="1024"
|
||||
:rows="5"
|
||||
class="w-100% mt-[15px]"
|
||||
class="mt-[15px] w-full"
|
||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||
show-count
|
||||
/>
|
||||
</div>
|
||||
<div class="hot-words">
|
||||
<div>
|
||||
<b>随机热词</b>
|
||||
</div>
|
||||
<Space wrap class="word-list">
|
||||
|
||||
<div class="hot-words mt-[30px] flex flex-col">
|
||||
<div><b>随机热词</b></div>
|
||||
<Space wrap class="word-list mt-[15px] flex flex-wrap justify-start">
|
||||
<Button
|
||||
shape="round"
|
||||
class="btn"
|
||||
class="btn m-0"
|
||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||
v-for="hotWord in ImageHotWords"
|
||||
:key="hotWord"
|
||||
|
|
@ -173,15 +172,17 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="model">
|
||||
<div>
|
||||
<b>模型选择</b>
|
||||
</div>
|
||||
<Space wrap class="model-list">
|
||||
|
||||
<div class="model mt-[30px]">
|
||||
<div><b>模型选择</b></div>
|
||||
<Space wrap class="model-list mt-[15px] flex flex-wrap gap-[10px]">
|
||||
<div
|
||||
:class="
|
||||
selectModel === model.key ? 'modal-item selectModel' : 'modal-item'
|
||||
"
|
||||
class="modal-item flex w-[110px] cursor-pointer flex-col items-center overflow-hidden rounded-[5px] border-[3px]"
|
||||
:class="[
|
||||
selectModel === model.key
|
||||
? 'border-[#1293ff!important]'
|
||||
: 'border-transparent',
|
||||
]"
|
||||
v-for="model in Dall3Models"
|
||||
:key="model.key"
|
||||
>
|
||||
|
|
@ -191,21 +192,21 @@ defineExpose({ settingValues });
|
|||
fit="contain"
|
||||
@click="handleModelClick(model)"
|
||||
/>
|
||||
<div class="model-font">{{ model.name }}</div>
|
||||
<div class="model-font text-[14px] font-bold text-[#3e3e3e]">
|
||||
{{ model.name }}
|
||||
</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="image-style">
|
||||
<div>
|
||||
<b>风格选择</b>
|
||||
</div>
|
||||
<Space wrap class="image-style-list">
|
||||
|
||||
<div class="image-style mt-[30px]">
|
||||
<div><b>风格选择</b></div>
|
||||
<Space wrap class="image-style-list mt-[15px] flex flex-wrap gap-[10px]">
|
||||
<div
|
||||
:class="
|
||||
style === imageStyle.key
|
||||
? 'image-style-item selectImageStyle'
|
||||
: 'image-style-item'
|
||||
"
|
||||
class="image-style-item flex w-[110px] cursor-pointer flex-col items-center overflow-hidden rounded-[5px] border-[3px]"
|
||||
:class="[
|
||||
style === imageStyle.key ? 'border-[#1293ff]' : 'border-transparent',
|
||||
]"
|
||||
v-for="imageStyle in Dall3StyleList"
|
||||
:key="imageStyle.key"
|
||||
>
|
||||
|
|
@ -215,35 +216,41 @@ defineExpose({ settingValues });
|
|||
fit="contain"
|
||||
@click="handleStyleClick(imageStyle)"
|
||||
/>
|
||||
<div class="style-font">{{ imageStyle.name }}</div>
|
||||
<div class="style-font text-[14px] font-bold text-[#3e3e3e]">
|
||||
{{ imageStyle.name }}
|
||||
</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="image-size">
|
||||
<div>
|
||||
<b>画面比例</b>
|
||||
</div>
|
||||
<Space wrap class="size-list">
|
||||
|
||||
<div class="image-size mt-[30px] w-full">
|
||||
<div><b>画面比例</b></div>
|
||||
<Space
|
||||
wrap
|
||||
class="size-list mt-[20px] flex w-full flex-row justify-between"
|
||||
>
|
||||
<div
|
||||
class="size-item"
|
||||
class="size-item flex cursor-pointer flex-col items-center"
|
||||
v-for="imageSize in Dall3SizeList"
|
||||
:key="imageSize.key"
|
||||
@click="handleSizeClick(imageSize)"
|
||||
>
|
||||
<div
|
||||
:class="
|
||||
selectSize === imageSize.key
|
||||
? 'size-wrapper selectImageSize'
|
||||
: 'size-wrapper'
|
||||
"
|
||||
class="size-wrapper flex h-[50px] w-[50px] flex-col items-center justify-center rounded-[7px] border bg-white p-[4px]"
|
||||
:class="[
|
||||
selectSize === imageSize.key ? 'border-[#1293ff]' : 'border-white',
|
||||
]"
|
||||
>
|
||||
<div :style="imageSize.style"></div>
|
||||
</div>
|
||||
<div class="size-font">{{ imageSize.name }}</div>
|
||||
<div class="size-font text-[14px] font-bold text-[#3e3e3e]">
|
||||
{{ imageSize.name }}
|
||||
</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="btns">
|
||||
|
||||
<div class="btns mt-[50px] flex justify-center">
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
|
|
@ -256,134 +263,3 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</div>
|
||||
</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"
|
||||
:maxlength="1024"
|
||||
:rows="5"
|
||||
class="w-100% mt-[15px]"
|
||||
class="mt-[15px] w-full"
|
||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||
show-count
|
||||
/>
|
||||
</div>
|
||||
<div class="hot-words">
|
||||
<div>
|
||||
<b>随机热词</b>
|
||||
</div>
|
||||
<Space wrap class="word-list">
|
||||
|
||||
<div class="mt-8 flex flex-col">
|
||||
<div><b>随机热词</b></div>
|
||||
<Space wrap class="mt-4 flex flex-wrap gap-2">
|
||||
<Button
|
||||
shape="round"
|
||||
class="btn"
|
||||
class="m-0"
|
||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||
v-for="hotWord in ImageHotWords"
|
||||
:key="hotWord"
|
||||
|
|
@ -172,41 +171,41 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="image-size">
|
||||
<div>
|
||||
<b>尺寸</b>
|
||||
</div>
|
||||
<Space wrap class="size-list">
|
||||
|
||||
<div class="mt-8 w-full">
|
||||
<div><b>尺寸</b></div>
|
||||
<Space wrap class="mt-5 flex w-full flex-row justify-between">
|
||||
<div
|
||||
class="size-item"
|
||||
class="flex cursor-pointer flex-col items-center"
|
||||
v-for="imageSize in MidjourneySizeList"
|
||||
:key="imageSize.key"
|
||||
@click="handleSizeClick(imageSize)"
|
||||
>
|
||||
<div
|
||||
:class="
|
||||
selectSize === imageSize.key
|
||||
? 'size-wrapper selectImageSize'
|
||||
: 'size-wrapper'
|
||||
"
|
||||
class="flex h-[50px] w-[50px] items-center justify-center rounded-[7px] border bg-white p-1"
|
||||
:class="[
|
||||
selectSize === imageSize.key ? 'border-[#1293ff]' : 'border-white',
|
||||
]"
|
||||
>
|
||||
<div :style="imageSize.style"></div>
|
||||
</div>
|
||||
<div class="size-font">{{ imageSize.key }}</div>
|
||||
<div class="text-sm font-bold text-[#3e3e3e]">{{ imageSize.key }}</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="model">
|
||||
<div>
|
||||
<b>模型</b>
|
||||
</div>
|
||||
<Space wrap class="model-list">
|
||||
|
||||
<div class="mt-8">
|
||||
<div><b>模型</b></div>
|
||||
<Space wrap class="mt-4 flex flex-wrap gap-4">
|
||||
<div
|
||||
:class="
|
||||
selectModel === model.key ? 'modal-item selectModel' : 'modal-item'
|
||||
"
|
||||
v-for="model in MidjourneyModels"
|
||||
: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
|
||||
:preview="false"
|
||||
|
|
@ -214,18 +213,17 @@ defineExpose({ settingValues });
|
|||
fit="contain"
|
||||
@click="handleModelClick(model)"
|
||||
/>
|
||||
<div class="model-font">{{ model.name }}</div>
|
||||
<div class="text-sm font-bold text-[#3e3e3e]">{{ model.name }}</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="version">
|
||||
<div>
|
||||
<b>版本</b>
|
||||
</div>
|
||||
<Space wrap class="version-list">
|
||||
|
||||
<div class="mt-5">
|
||||
<div><b>版本</b></div>
|
||||
<Space wrap class="mt-5 w-full">
|
||||
<Select
|
||||
v-model:value="selectVersion"
|
||||
class="version-select !w-[330px]"
|
||||
class="!w-[330px]"
|
||||
clearable
|
||||
placeholder="请选择版本"
|
||||
>
|
||||
|
|
@ -239,15 +237,15 @@ defineExpose({ settingValues });
|
|||
</Select>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="model">
|
||||
<div>
|
||||
<b>参考图</b>
|
||||
</div>
|
||||
<Space wrap class="model-list">
|
||||
|
||||
<div class="mt-8">
|
||||
<div><b>参考图</b></div>
|
||||
<Space wrap class="mt-4">
|
||||
<ImageUpload v-model:value="referImageUrl" :show-description="false" />
|
||||
</Space>
|
||||
</div>
|
||||
<div class="btns">
|
||||
|
||||
<div class="mt-[50px] flex justify-center">
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
|
|
@ -259,113 +257,3 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</div>
|
||||
</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 {
|
||||
Button,
|
||||
Input,
|
||||
InputNumber,
|
||||
message,
|
||||
Select,
|
||||
|
|
@ -139,19 +138,19 @@ defineExpose({ settingValues });
|
|||
v-model:value="prompt"
|
||||
:maxlength="1024"
|
||||
:rows="5"
|
||||
class="w-100% mt-[15px]"
|
||||
class="mt-[15px] w-full"
|
||||
placeholder="例如:童话里的小屋应该是什么样子?"
|
||||
show-count
|
||||
/>
|
||||
</div>
|
||||
<div class="hot-words">
|
||||
<div>
|
||||
<b>随机热词</b>
|
||||
</div>
|
||||
<Space wrap class="word-list">
|
||||
|
||||
<!-- 热词区域 -->
|
||||
<div class="mt-[30px] flex flex-col">
|
||||
<div><b>随机热词</b></div>
|
||||
<Space wrap class="mt-[15px] flex flex-wrap justify-start">
|
||||
<Button
|
||||
shape="round"
|
||||
class="btn"
|
||||
class="m-0"
|
||||
:type="selectHotWord === hotWord ? 'primary' : 'default'"
|
||||
v-for="hotWord in ImageHotEnglishWords"
|
||||
:key="hotWord"
|
||||
|
|
@ -161,11 +160,11 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
<div>
|
||||
<b>采样方法</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
|
||||
<!-- 参数项:采样方法 -->
|
||||
<div class="mt-[30px]">
|
||||
<div><b>采样方法</b></div>
|
||||
<Space wrap class="mt-[15px] w-full">
|
||||
<Select
|
||||
v-model:value="sampler"
|
||||
placeholder="Select"
|
||||
|
|
@ -182,11 +181,11 @@ defineExpose({ settingValues });
|
|||
</Select>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
<div>
|
||||
<b>CLIP</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
|
||||
<!-- CLIP -->
|
||||
<div class="mt-[30px]">
|
||||
<div><b>CLIP</b></div>
|
||||
<Space wrap class="mt-[15px] w-full">
|
||||
<Select
|
||||
v-model:value="clipGuidancePreset"
|
||||
placeholder="Select"
|
||||
|
|
@ -203,11 +202,11 @@ defineExpose({ settingValues });
|
|||
</Select>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
<div>
|
||||
<b>风格</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
|
||||
<!-- 风格 -->
|
||||
<div class="mt-[30px]">
|
||||
<div><b>风格</b></div>
|
||||
<Space wrap class="mt-[15px] w-full">
|
||||
<Select
|
||||
v-model:value="stylePreset"
|
||||
placeholder="Select"
|
||||
|
|
@ -225,35 +224,43 @@ defineExpose({ settingValues });
|
|||
</Select>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
<div>
|
||||
<b>图片尺寸</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
<Input v-model="width" class="w-[170px]" placeholder="图片宽度" />
|
||||
<Input v-model="height" class="w-[170px]" placeholder="图片高度" />
|
||||
|
||||
<!-- 图片尺寸 -->
|
||||
<div class="mt-[30px]">
|
||||
<div><b>图片尺寸</b></div>
|
||||
<Space wrap class="mt-[15px] w-full">
|
||||
<InputNumber
|
||||
v-model:value="width"
|
||||
class="w-[170px]"
|
||||
placeholder="图片宽度"
|
||||
/>
|
||||
<InputNumber
|
||||
v-model:value="height"
|
||||
class="w-[170px]"
|
||||
placeholder="图片高度"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
<div>
|
||||
<b>迭代步数</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
|
||||
<!-- 迭代步数 -->
|
||||
<div class="mt-[30px]">
|
||||
<div><b>迭代步数</b></div>
|
||||
<Space wrap class="mt-[15px] w-full">
|
||||
<InputNumber
|
||||
v-model="steps"
|
||||
v-model:value="steps"
|
||||
size="large"
|
||||
class="!w-[330px]"
|
||||
placeholder="Please input"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
<div>
|
||||
<b>引导系数</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
|
||||
<!-- 引导系数 -->
|
||||
<div class="mt-[30px]">
|
||||
<div><b>引导系数</b></div>
|
||||
<Space wrap class="mt-[15px] w-full">
|
||||
<InputNumber
|
||||
v-model="scale"
|
||||
v-model:value="scale"
|
||||
type="number"
|
||||
size="large"
|
||||
class="!w-[330px]"
|
||||
|
|
@ -261,11 +268,11 @@ defineExpose({ settingValues });
|
|||
/>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="group-item">
|
||||
<div>
|
||||
<b>随机因子</b>
|
||||
</div>
|
||||
<Space wrap class="group-item-body">
|
||||
|
||||
<!-- 随机因子 -->
|
||||
<div class="mt-[30px]">
|
||||
<div><b>随机因子</b></div>
|
||||
<Space wrap class="mt-[15px] w-full">
|
||||
<InputNumber
|
||||
v-model:value="seed"
|
||||
size="large"
|
||||
|
|
@ -274,7 +281,9 @@ defineExpose({ settingValues });
|
|||
/>
|
||||
</Space>
|
||||
</div>
|
||||
<div class="btns">
|
||||
|
||||
<!-- 生成按钮 -->
|
||||
<div class="mt-[50px] flex justify-center">
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
|
|
@ -287,38 +296,3 @@ defineExpose({ settingValues });
|
|||
</Button>
|
||||
</div>
|
||||
</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>
|
||||
<Page auto-content-height>
|
||||
<div class="ai-image">
|
||||
<div class="left">
|
||||
<div class="ai-image absolute inset-0 flex h-full w-full flex-row">
|
||||
<div class="left flex w-[390px] flex-col p-5">
|
||||
<div class="segmented flex justify-center">
|
||||
<Segmented
|
||||
v-model:value="selectPlatform"
|
||||
:options="platformOptions"
|
||||
class="bg-[#ececec]"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-switch-container">
|
||||
<div class="modal-switch-container mt-[30px] h-full overflow-y-auto">
|
||||
<Common
|
||||
v-if="selectPlatform === 'common'"
|
||||
ref="commonRef"
|
||||
|
|
@ -125,47 +126,9 @@ onMounted(async () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="main flex-1 bg-white">
|
||||
<ImageList ref="imageListRef" @on-regeneration="handleRegeneration" />
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</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>
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="square-container">
|
||||
<!-- TODO @fan:style 建议换成 unocss -->
|
||||
<div class="bg-[#fff] p-[20px]">
|
||||
<!-- TODO @fan:Search 可以换成 Icon 组件么? -->
|
||||
<Input.Search
|
||||
v-model="queryParams.prompt"
|
||||
style="width: 100%; margin-bottom: 20px"
|
||||
class="mb-[20px] w-full"
|
||||
size="large"
|
||||
placeholder="请输入要搜索的内容"
|
||||
@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 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ -->
|
||||
<div v-for="item in list" :key="item.id" class="gallery-item">
|
||||
<img :src="item.picUrl" class="img" />
|
||||
<div
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="relative cursor-pointer overflow-hidden bg-[#f0f0f0] transition-transform duration-300 hover:scale-[1.05]"
|
||||
>
|
||||
<img
|
||||
:src="item.picUrl"
|
||||
class="block h-auto w-full transition-transform duration-300 hover:scale-[1.1]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO @fan:缺少翻页 -->
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
:show-total="(total: number) => `共 ${total} 条`"
|
||||
:show-total="(total) => `共 ${total} 条`"
|
||||
show-quick-jumper
|
||||
show-size-changer
|
||||
v-model:current="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
@change="debounceGetList"
|
||||
@show-size-change="debounceGetList"
|
||||
class="mt-[20px]"
|
||||
/>
|
||||
</div>
|
||||
</Page>
|
||||
</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>
|
||||
<template>
|
||||
<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>
|
||||
<b>您的需求?</b>
|
||||
|
|
@ -67,11 +71,3 @@ defineExpose({
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
height: 1.75rem;
|
||||
font-size: 1.25rem;
|
||||
color: hsl(var(--primary));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ defineExpose({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="my-card h-full flex-grow">
|
||||
<Card class="my-card flex h-full flex-grow flex-col">
|
||||
<template #title>
|
||||
<div class="m-0 flex shrink-0 items-center justify-between px-7">
|
||||
<h3>思维导图预览</h3>
|
||||
|
|
@ -176,9 +176,6 @@ defineExpose({
|
|||
}
|
||||
|
||||
.my-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
box-sizing: border-box;
|
||||
flex-grow: 1;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useVbenDrawer } from '@vben/common-ui';
|
|||
import { Button, Input, Select } from 'ant-design-vue';
|
||||
|
||||
import { testWorkflow } from '#/api/ai/workflow';
|
||||
import Tinyflow from '#/components/Tinyflow/Tinyflow.vue';
|
||||
import Tinyflow from '#/components/tinyflow/tinyflow.vue';
|
||||
|
||||
defineProps<{
|
||||
provider: any;
|
||||
|
|
@ -25,7 +25,7 @@ const error = ref(null);
|
|||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
footer: false,
|
||||
closeOnClickModal: false,
|
||||
modal: false
|
||||
modal: false,
|
||||
});
|
||||
/** 展示工作流测试抽屉 */
|
||||
const testWorkflowModel = () => {
|
||||
|
|
@ -180,7 +180,7 @@ defineExpose({ validate });
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative" style="width: 100%; height: 700px">
|
||||
<div class="relative h-[700px] w-[100%]">
|
||||
<Tinyflow
|
||||
v-if="workflowData"
|
||||
ref="tinyflowRef"
|
||||
|
|
@ -198,12 +198,19 @@ defineExpose({ validate });
|
|||
测试
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Drawer title="工作流测试">
|
||||
<fieldset>
|
||||
<legend class="ml-2"><h3>运行参数配置</h3></legend>
|
||||
<div class="p-2">
|
||||
<fieldset
|
||||
class="min-inline-size-auto m-0 rounded-[6px] border border-[#dcdfe6] p-[12px_16px]"
|
||||
>
|
||||
<legend
|
||||
class="ml-[8px] px-[10px] text-[16px] font-semibold text-[#303133]"
|
||||
>
|
||||
<h3>运行参数配置</h3>
|
||||
</legend>
|
||||
<div class="p-[8px]">
|
||||
<div
|
||||
class="mb-1 flex justify-around"
|
||||
class="mb-[4px] flex items-center justify-around"
|
||||
v-for="(param, index) in params4Test"
|
||||
:key="index"
|
||||
>
|
||||
|
|
@ -218,7 +225,7 @@ defineExpose({ validate });
|
|||
</Select.Option>
|
||||
</Select>
|
||||
<Input
|
||||
class="w-[200px]"
|
||||
class="mx-[8px] w-[200px]"
|
||||
v-model:value="param.value"
|
||||
placeholder="参数值"
|
||||
/>
|
||||
|
|
@ -228,26 +235,35 @@ defineExpose({ validate });
|
|||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<Button type="primary" plain @click="addParam">添加参数</Button>
|
||||
<Button type="primary" plain class="mt-[8px]" @click="addParam">
|
||||
添加参数
|
||||
</Button>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="mt-2" style="background-color: #f8f9fa">
|
||||
<legend class="ml-2"><h3>运行结果</h3></legend>
|
||||
<div class="p-2">
|
||||
<div v-if="loading"><el-text type="primary">执行中...</el-text></div>
|
||||
<div v-else-if="error">
|
||||
<el-text type="danger">{{ error }}</el-text>
|
||||
</div>
|
||||
<pre v-else-if="testResult" class="result-content">
|
||||
{{ JSON.stringify(testResult, null, 2) }}
|
||||
|
||||
<fieldset
|
||||
class="min-inline-size-auto m-0 mt-[8px] rounded-[6px] border border-[#dcdfe6] bg-[#f8f9fa] p-[12px_16px]"
|
||||
>
|
||||
<legend
|
||||
class="ml-[8px] px-[10px] text-[16px] font-semibold text-[#303133]"
|
||||
>
|
||||
<h3>运行结果</h3>
|
||||
</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>
|
||||
<div v-else style="color: #909399">点击运行查看结果</div>
|
||||
<div v-else class="text-[#909399]">点击运行查看结果</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<Button
|
||||
class="mt-2"
|
||||
size="large"
|
||||
style="width: 100%; color: white; background-color: #67c23a"
|
||||
class="mt-[8px] w-[100%] bg-[#67c23a] text-white"
|
||||
@click="goRun"
|
||||
>
|
||||
运行流程
|
||||
|
|
@ -255,32 +271,3 @@ defineExpose({ validate });
|
|||
</Drawer>
|
||||
</div>
|
||||
</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>
|
||||
<template>
|
||||
<Card class="my-card h-full">
|
||||
<Card class="my-card flex h-full flex-col">
|
||||
<template #title>
|
||||
<h3 class="m-0 flex shrink-0 items-center justify-between px-7">
|
||||
<span>预览</span>
|
||||
|
|
@ -123,9 +123,6 @@ watch(copied, (val) => {
|
|||
}
|
||||
|
||||
.my-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
box-sizing: border-box;
|
||||
flex-grow: 1;
|
||||
|
|
|
|||
|
|
@ -29,4 +29,3 @@ const emits = defineEmits<{
|
|||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue