feat: ai
							parent
							
								
									d09b993bc8
								
							
						
					
					
						commit
						014785a1ad
					
				| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type { Item } from './ui/typeing';
 | 
					import type { Item } from './ui/typing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { onMounted, onUnmounted, ref } from 'vue';
 | 
					import { onMounted, onUnmounted, ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					export { default as Tinyflow } from './tinyflow.vue';
 | 
				
			||||||
 | 
					export * from './ui/typing';
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,21 @@
 | 
				
			||||||
export declare type Item = {
 | 
					export interface Item {
 | 
				
			||||||
  children?: Item[];
 | 
					  children?: Item[];
 | 
				
			||||||
  label: string;
 | 
					  label: string;
 | 
				
			||||||
  value: number | string;
 | 
					  value: number | string;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Position = {
 | 
					export interface Position {
 | 
				
			||||||
  x: number;
 | 
					  x: number;
 | 
				
			||||||
  y: number;
 | 
					  y: number;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Viewport = {
 | 
					export interface Viewport {
 | 
				
			||||||
  x: number;
 | 
					  x: number;
 | 
				
			||||||
  y: number;
 | 
					  y: number;
 | 
				
			||||||
  zoom: number;
 | 
					  zoom: number;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Node = {
 | 
					export interface Node {
 | 
				
			||||||
  data?: Record<string, any>;
 | 
					  data?: Record<string, any>;
 | 
				
			||||||
  draggable?: boolean;
 | 
					  draggable?: boolean;
 | 
				
			||||||
  height?: number;
 | 
					  height?: number;
 | 
				
			||||||
| 
						 | 
					@ -24,23 +24,23 @@ export type Node = {
 | 
				
			||||||
  selected?: boolean;
 | 
					  selected?: boolean;
 | 
				
			||||||
  type?: string;
 | 
					  type?: string;
 | 
				
			||||||
  width?: number;
 | 
					  width?: number;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Edge = {
 | 
					export interface Edge {
 | 
				
			||||||
  animated?: boolean;
 | 
					  animated?: boolean;
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
  label?: string;
 | 
					  label?: string;
 | 
				
			||||||
  source: string;
 | 
					  source: string;
 | 
				
			||||||
  target: string;
 | 
					  target: string;
 | 
				
			||||||
  type?: string;
 | 
					  type?: string;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
export type TinyflowData = Partial<{
 | 
					export type TinyflowData = Partial<{
 | 
				
			||||||
  edges: Edge[];
 | 
					  edges: Edge[];
 | 
				
			||||||
  nodes: Node[];
 | 
					  nodes: Node[];
 | 
				
			||||||
  viewport: Viewport;
 | 
					  viewport: Viewport;
 | 
				
			||||||
}>;
 | 
					}>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export declare type TinyflowOptions = {
 | 
					export interface TinyflowOptions {
 | 
				
			||||||
  data?: TinyflowData;
 | 
					  data?: TinyflowData;
 | 
				
			||||||
  element: Element | string;
 | 
					  element: Element | string;
 | 
				
			||||||
  provider?: {
 | 
					  provider?: {
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,7 @@ export declare type TinyflowOptions = {
 | 
				
			||||||
    knowledge?: () => Item[] | Promise<Item[]>;
 | 
					    knowledge?: () => Item[] | Promise<Item[]>;
 | 
				
			||||||
    llm?: () => Item[] | Promise<Item[]>;
 | 
					    llm?: () => Item[] | Promise<Item[]>;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export declare class Tinyflow {
 | 
					export declare class Tinyflow {
 | 
				
			||||||
  private _init;
 | 
					  private _init;
 | 
				
			||||||
| 
						 | 
					@ -66,5 +66,3 @@ export declare class Tinyflow {
 | 
				
			||||||
  getOptions(): TinyflowOptions;
 | 
					  getOptions(): TinyflowOptions;
 | 
				
			||||||
  setData(data: TinyflowData): void;
 | 
					  setData(data: TinyflowData): void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export {};
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					export { default as MarkdownView } from './markdown-view.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export * from './typing';
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import type { MarkdownViewProps } from './typing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { computed, onMounted, ref } from 'vue';
 | 
					import { computed, onMounted, ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { MarkdownIt } from '@vben/plugins/markmap';
 | 
					import { MarkdownIt } from '@vben/plugins/markmap';
 | 
				
			||||||
| 
						 | 
					@ -10,15 +12,10 @@ import hljs from 'highlight.js';
 | 
				
			||||||
import 'highlight.js/styles/vs2015.min.css';
 | 
					import 'highlight.js/styles/vs2015.min.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 定义组件属性
 | 
					// 定义组件属性
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps<MarkdownViewProps>();
 | 
				
			||||||
  content: {
 | 
					 | 
				
			||||||
    type: String,
 | 
					 | 
				
			||||||
    required: true,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { copy } = useClipboard(); // 初始化 copy 到粘贴板
 | 
					const { copy } = useClipboard(); // 初始化 copy 到粘贴板
 | 
				
			||||||
const contentRef = ref();
 | 
					const contentRef = ref<HTMLElement | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const md = new MarkdownIt({
 | 
					const md = new MarkdownIt({
 | 
				
			||||||
  highlight(str, lang) {
 | 
					  highlight(str, lang) {
 | 
				
			||||||
| 
						 | 
					@ -40,7 +37,7 @@ const renderedMarkdown = computed(() => {
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
  // 添加 copy 监听
 | 
					  // 添加 copy 监听
 | 
				
			||||||
  contentRef.value.addEventListener('click', (e: any) => {
 | 
					  contentRef.value?.addEventListener('click', (e: any) => {
 | 
				
			||||||
    if (e.target.id === 'copy') {
 | 
					    if (e.target.id === 'copy') {
 | 
				
			||||||
      copy(e.target?.dataset?.copy);
 | 
					      copy(e.target?.dataset?.copy);
 | 
				
			||||||
      message.success('复制成功!');
 | 
					      message.success('复制成功!');
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					export type MarkdownViewProps = {
 | 
				
			||||||
 | 
					  content: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,6 @@ const props = defineProps({
 | 
				
			||||||
    default: null,
 | 
					    default: null,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
/** 新建对话 */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 定义钩子
 | 
					// 定义钩子
 | 
				
			||||||
const emits = defineEmits([
 | 
					const emits = defineEmits([
 | 
				
			||||||
| 
						 | 
					@ -37,9 +36,11 @@ const emits = defineEmits([
 | 
				
			||||||
  'onConversationClear',
 | 
					  'onConversationClear',
 | 
				
			||||||
  'onConversationDelete',
 | 
					  'onConversationDelete',
 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const [Drawer, drawerApi] = useVbenDrawer({
 | 
					const [Drawer, drawerApi] = useVbenDrawer({
 | 
				
			||||||
  connectedComponent: RoleRepository,
 | 
					  connectedComponent: RoleRepository,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 定义属性
 | 
					// 定义属性
 | 
				
			||||||
const searchName = ref<string>(''); // 对话搜索
 | 
					const searchName = ref<string>(''); // 对话搜索
 | 
				
			||||||
const activeConversationId = ref<null | number>(null); // 选中的对话,默认为 null
 | 
					const activeConversationId = ref<null | number>(null); // 选中的对话,默认为 null
 | 
				
			||||||
| 
						 | 
					@ -50,7 +51,7 @@ const loading = ref<boolean>(false); // 加载中
 | 
				
			||||||
const loadingTime = ref<any>();
 | 
					const loadingTime = ref<any>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 搜索对话 */
 | 
					/** 搜索对话 */
 | 
				
			||||||
const searchConversation = async () => {
 | 
					async function searchConversation() {
 | 
				
			||||||
  // 恢复数据
 | 
					  // 恢复数据
 | 
				
			||||||
  if (searchName.value.trim().length === 0) {
 | 
					  if (searchName.value.trim().length === 0) {
 | 
				
			||||||
    conversationMap.value = await getConversationGroupByCreateTime(
 | 
					    conversationMap.value = await getConversationGroupByCreateTime(
 | 
				
			||||||
| 
						 | 
					@ -64,25 +65,25 @@ const searchConversation = async () => {
 | 
				
			||||||
    conversationMap.value =
 | 
					    conversationMap.value =
 | 
				
			||||||
      await getConversationGroupByCreateTime(filterValues);
 | 
					      await getConversationGroupByCreateTime(filterValues);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 点击对话 */
 | 
					/** 点击对话 */
 | 
				
			||||||
const handleConversationClick = async (id: number) => {
 | 
					async function handleConversationClick(id: number) {
 | 
				
			||||||
  // 过滤出选中的对话
 | 
					  // 过滤出选中的对话
 | 
				
			||||||
  const filterConversation = conversationList.value.find((item) => {
 | 
					  const filterConversation = conversationList.value.find((item) => {
 | 
				
			||||||
    return item.id === id;
 | 
					    return item.id === id;
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  // 回调 onConversationClick
 | 
					  // 回调 onConversationClick
 | 
				
			||||||
  // noinspection JSVoidFunctionReturnValueUsed
 | 
					  // noinspection JSVoidFunctionReturnValueUsed
 | 
				
			||||||
  const success = emits('onConversationClick', filterConversation);
 | 
					  const success = emits('onConversationClick', filterConversation) as any;
 | 
				
			||||||
  // 切换对话
 | 
					  // 切换对话
 | 
				
			||||||
  if (success) {
 | 
					  if (success) {
 | 
				
			||||||
    activeConversationId.value = id;
 | 
					    activeConversationId.value = id;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取对话列表 */
 | 
					/** 获取对话列表 */
 | 
				
			||||||
const getChatConversationList = async () => {
 | 
					async function getChatConversationList() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 加载中
 | 
					    // 加载中
 | 
				
			||||||
    loadingTime.value = setTimeout(() => {
 | 
					    loadingTime.value = setTimeout(() => {
 | 
				
			||||||
| 
						 | 
					@ -114,12 +115,12 @@ const getChatConversationList = async () => {
 | 
				
			||||||
    // 加载完成
 | 
					    // 加载完成
 | 
				
			||||||
    loading.value = false;
 | 
					    loading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 按照 creteTime 创建时间,进行分组 */
 | 
					/** 按照 creteTime 创建时间,进行分组 */
 | 
				
			||||||
const getConversationGroupByCreateTime = async (
 | 
					async function getConversationGroupByCreateTime(
 | 
				
			||||||
  list: AiChatConversationApi.ChatConversationVO[],
 | 
					  list: AiChatConversationApi.ChatConversationVO[],
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前)
 | 
					  // 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前)
 | 
				
			||||||
  // noinspection NonAsciiCharacters
 | 
					  // noinspection NonAsciiCharacters
 | 
				
			||||||
  const groupMap: any = {
 | 
					  const groupMap: any = {
 | 
				
			||||||
| 
						 | 
					@ -159,8 +160,9 @@ const getConversationGroupByCreateTime = async (
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return groupMap;
 | 
					  return groupMap;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
const createConversation = async () => {
 | 
					
 | 
				
			||||||
 | 
					async function createConversation() {
 | 
				
			||||||
  // 1. 新建对话
 | 
					  // 1. 新建对话
 | 
				
			||||||
  const conversationId = await createChatConversationMy(
 | 
					  const conversationId = await createChatConversationMy(
 | 
				
			||||||
    {} as unknown as AiChatConversationApi.ChatConversationVO,
 | 
					    {} as unknown as AiChatConversationApi.ChatConversationVO,
 | 
				
			||||||
| 
						 | 
					@ -171,12 +173,12 @@ const createConversation = async () => {
 | 
				
			||||||
  await handleConversationClick(conversationId);
 | 
					  await handleConversationClick(conversationId);
 | 
				
			||||||
  // 4. 回调
 | 
					  // 4. 回调
 | 
				
			||||||
  emits('onConversationCreate');
 | 
					  emits('onConversationCreate');
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 修改对话的标题 */
 | 
					/** 修改对话的标题 */
 | 
				
			||||||
const updateConversationTitle = async (
 | 
					async function updateConversationTitle(
 | 
				
			||||||
  conversation: AiChatConversationApi.ChatConversationVO,
 | 
					  conversation: AiChatConversationApi.ChatConversationVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 1. 二次确认
 | 
					  // 1. 二次确认
 | 
				
			||||||
  prompt({
 | 
					  prompt({
 | 
				
			||||||
    async beforeClose(scope) {
 | 
					    async beforeClose(scope) {
 | 
				
			||||||
| 
						 | 
					@ -225,12 +227,12 @@ const updateConversationTitle = async (
 | 
				
			||||||
    title: '修改标题',
 | 
					    title: '修改标题',
 | 
				
			||||||
    modelPropName: 'value',
 | 
					    modelPropName: 'value',
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 删除聊天对话 */
 | 
					/** 删除聊天对话 */
 | 
				
			||||||
const deleteChatConversation = async (
 | 
					async function deleteChatConversation(
 | 
				
			||||||
  conversation: AiChatConversationApi.ChatConversationVO,
 | 
					  conversation: AiChatConversationApi.ChatConversationVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 删除的二次确认
 | 
					    // 删除的二次确认
 | 
				
			||||||
    await confirm(`是否确认删除对话 - ${conversation.title}?`);
 | 
					    await confirm(`是否确认删除对话 - ${conversation.title}?`);
 | 
				
			||||||
| 
						 | 
					@ -242,8 +244,9 @@ const deleteChatConversation = async (
 | 
				
			||||||
    // 回调
 | 
					    // 回调
 | 
				
			||||||
    emits('onConversationDelete', conversation);
 | 
					    emits('onConversationDelete', conversation);
 | 
				
			||||||
  } catch {}
 | 
					  } catch {}
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
const handleClearConversation = async () => {
 | 
					
 | 
				
			||||||
 | 
					async function handleClearConversation() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    await confirm('确认后对话会全部清空,置顶的对话除外。');
 | 
					    await confirm('确认后对话会全部清空,置顶的对话除外。');
 | 
				
			||||||
    await deleteChatConversationMyByUnpinned();
 | 
					    await deleteChatConversationMyByUnpinned();
 | 
				
			||||||
| 
						 | 
					@ -255,18 +258,18 @@ const handleClearConversation = async () => {
 | 
				
			||||||
    // 回调 方法
 | 
					    // 回调 方法
 | 
				
			||||||
    emits('onConversationClear');
 | 
					    emits('onConversationClear');
 | 
				
			||||||
  } catch {}
 | 
					  } catch {}
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 对话置顶 */
 | 
					/** 对话置顶 */
 | 
				
			||||||
const handleTop = async (
 | 
					async function handleTop(
 | 
				
			||||||
  conversation: AiChatConversationApi.ChatConversationVO,
 | 
					  conversation: AiChatConversationApi.ChatConversationVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 更新对话置顶
 | 
					  // 更新对话置顶
 | 
				
			||||||
  conversation.pinned = !conversation.pinned;
 | 
					  conversation.pinned = !conversation.pinned;
 | 
				
			||||||
  await updateChatConversationMy(conversation);
 | 
					  await updateChatConversationMy(conversation);
 | 
				
			||||||
  // 刷新对话
 | 
					  // 刷新对话
 | 
				
			||||||
  await getChatConversationList();
 | 
					  await getChatConversationList();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ============ 角色仓库 ============
 | 
					// ============ 角色仓库 ============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,10 +47,10 @@ const documentList = computed(() => {
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 点击 document 处理 */
 | 
					/** 点击 document 处理 */
 | 
				
			||||||
const handleClick = (doc: any) => {
 | 
					function handleClick(doc: any) {
 | 
				
			||||||
  document.value = doc;
 | 
					  document.value = doc;
 | 
				
			||||||
  dialogVisible.value = true;
 | 
					  dialogVisible.value = true;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ import { useClipboard } from '@vueuse/core';
 | 
				
			||||||
import { Avatar, Button, message } from 'ant-design-vue';
 | 
					import { Avatar, Button, message } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { deleteChatMessage } from '#/api/ai/chat/message';
 | 
					import { deleteChatMessage } from '#/api/ai/chat/message';
 | 
				
			||||||
import MarkdownView from '#/components/MarkdownView/index.vue';
 | 
					import { MarkdownView } from '#/components/markdown-view';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import MessageKnowledge from './MessageKnowledge.vue';
 | 
					import MessageKnowledge from './MessageKnowledge.vue';
 | 
				
			||||||
// 定义 props
 | 
					// 定义 props
 | 
				
			||||||
| 
						 | 
					@ -67,44 +67,44 @@ function handleScroll() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 回到底部 */
 | 
					/** 回到底部 */
 | 
				
			||||||
const handleGoBottom = async () => {
 | 
					async function handleGoBottom() {
 | 
				
			||||||
  const scrollContainer = messageContainer.value;
 | 
					  const scrollContainer = messageContainer.value;
 | 
				
			||||||
  scrollContainer.scrollTop = scrollContainer.scrollHeight;
 | 
					  scrollContainer.scrollTop = scrollContainer.scrollHeight;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 回到顶部 */
 | 
					/** 回到顶部 */
 | 
				
			||||||
const handlerGoTop = async () => {
 | 
					async function handlerGoTop() {
 | 
				
			||||||
  const scrollContainer = messageContainer.value;
 | 
					  const scrollContainer = messageContainer.value;
 | 
				
			||||||
  scrollContainer.scrollTop = 0;
 | 
					  scrollContainer.scrollTop = 0;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ scrollToBottom, handlerGoTop }); // 提供方法给 parent 调用
 | 
					defineExpose({ scrollToBottom, handlerGoTop }); // 提供方法给 parent 调用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ============ 处理消息操作 ==============
 | 
					// ============ 处理消息操作 ==============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 复制 */
 | 
					/** 复制 */
 | 
				
			||||||
const copyContent = async (content: string) => {
 | 
					async function copyContent(content: string) {
 | 
				
			||||||
  await copy(content);
 | 
					  await copy(content);
 | 
				
			||||||
  message.success('复制成功!');
 | 
					  message.success('复制成功!');
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 删除 */
 | 
					/** 删除 */
 | 
				
			||||||
const onDelete = async (id: number) => {
 | 
					async function onDelete(id: number) {
 | 
				
			||||||
  // 删除 message
 | 
					  // 删除 message
 | 
				
			||||||
  await deleteChatMessage(id);
 | 
					  await deleteChatMessage(id);
 | 
				
			||||||
  message.success('删除成功!');
 | 
					  message.success('删除成功!');
 | 
				
			||||||
  // 回调
 | 
					  // 回调
 | 
				
			||||||
  emits('onDeleteSuccess');
 | 
					  emits('onDeleteSuccess');
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 刷新 */
 | 
					/** 刷新 */
 | 
				
			||||||
const onRefresh = async (message: AiChatMessageApi.ChatMessageVO) => {
 | 
					async function onRefresh(message: AiChatMessageApi.ChatMessageVO) {
 | 
				
			||||||
  emits('onRefresh', message);
 | 
					  emits('onRefresh', message);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 编辑 */
 | 
					/** 编辑 */
 | 
				
			||||||
const onEdit = async (message: AiChatMessageApi.ChatMessageVO) => {
 | 
					async function onEdit(message: AiChatMessageApi.ChatMessageVO) {
 | 
				
			||||||
  emits('onEdit', message);
 | 
					  emits('onEdit', message);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,9 +11,9 @@ const promptList = [
 | 
				
			||||||
    prompt: '写一首好听的诗歌?',
 | 
					    prompt: '写一首好听的诗歌?',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
]; /** 选中 prompt 点击 */
 | 
					]; /** 选中 prompt 点击 */
 | 
				
			||||||
const handlerPromptClick = async (prompt: any) => {
 | 
					async function handlerPromptClick(prompt: any) {
 | 
				
			||||||
  emits('onPrompt', prompt.prompt);
 | 
					  emits('onPrompt', prompt.prompt);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="relative flex h-full w-full flex-row justify-center">
 | 
					  <div class="relative flex h-full w-full flex-row justify-center">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,9 @@ import { Button } from 'ant-design-vue';
 | 
				
			||||||
const emits = defineEmits(['onNewConversation']);
 | 
					const emits = defineEmits(['onNewConversation']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 新建 conversation 聊天对话 */
 | 
					/** 新建 conversation 聊天对话 */
 | 
				
			||||||
const handlerNewChat = () => {
 | 
					function handlerNewChat() {
 | 
				
			||||||
  emits('onNewConversation');
 | 
					  emits('onNewConversation');
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,9 @@ defineProps({
 | 
				
			||||||
const emits = defineEmits(['onCategoryClick']);
 | 
					const emits = defineEmits(['onCategoryClick']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理分类点击事件 */
 | 
					/** 处理分类点击事件 */
 | 
				
			||||||
const handleCategoryClick = async (category: string) => {
 | 
					async function handleCategoryClick(category: string) {
 | 
				
			||||||
  emits('onCategoryClick', category);
 | 
					  emits('onCategoryClick', category);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@ const emits = defineEmits(['onDelete', 'onEdit', 'onUse', 'onPage']);
 | 
				
			||||||
const tabsRef = ref<any>();
 | 
					const tabsRef = ref<any>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 操作:编辑、删除 */
 | 
					/** 操作:编辑、删除 */
 | 
				
			||||||
const handleMoreClick = async (data: any) => {
 | 
					async function handleMoreClick(data: any) {
 | 
				
			||||||
  const type = data[0];
 | 
					  const type = data[0];
 | 
				
			||||||
  const role = data[1];
 | 
					  const role = data[1];
 | 
				
			||||||
  if (type === 'delete') {
 | 
					  if (type === 'delete') {
 | 
				
			||||||
| 
						 | 
					@ -41,22 +41,22 @@ const handleMoreClick = async (data: any) => {
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    emits('onEdit', role);
 | 
					    emits('onEdit', role);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选中 */
 | 
					/** 选中 */
 | 
				
			||||||
const handleUseClick = (role: any) => {
 | 
					function handleUseClick(role: any) {
 | 
				
			||||||
  emits('onUse', role);
 | 
					  emits('onUse', role);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 滚动 */
 | 
					/** 滚动 */
 | 
				
			||||||
const handleTabsScroll = async () => {
 | 
					async function handleTabsScroll() {
 | 
				
			||||||
  if (tabsRef.value) {
 | 
					  if (tabsRef.value) {
 | 
				
			||||||
    const { scrollTop, scrollHeight, clientHeight } = tabsRef.value;
 | 
					    const { scrollTop, scrollHeight, clientHeight } = tabsRef.value;
 | 
				
			||||||
    if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) {
 | 
					    if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) {
 | 
				
			||||||
      await emits('onPage');
 | 
					      await emits('onPage');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,15 +46,15 @@ const activeCategory = ref<string>('全部'); // 选择中的分类
 | 
				
			||||||
const categoryList = ref<string[]>([]); // 角色分类类别
 | 
					const categoryList = ref<string[]>([]); // 角色分类类别
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** tabs 点击 */
 | 
					/** tabs 点击 */
 | 
				
			||||||
const handleTabsClick = async (tab: any) => {
 | 
					async function handleTabsClick(tab: any) {
 | 
				
			||||||
  // 设置切换状态
 | 
					  // 设置切换状态
 | 
				
			||||||
  activeTab.value = tab;
 | 
					  activeTab.value = tab;
 | 
				
			||||||
  // 切换的时候重新加载数据
 | 
					  // 切换的时候重新加载数据
 | 
				
			||||||
  await getActiveTabsRole();
 | 
					  await getActiveTabsRole();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取 my role 我的角色 */
 | 
					/** 获取 my role 我的角色 */
 | 
				
			||||||
const getMyRole = async (append?: boolean) => {
 | 
					async function getMyRole(append?: boolean) {
 | 
				
			||||||
  const params: AiModelChatRoleApi.ChatRolePageReqVO = {
 | 
					  const params: AiModelChatRoleApi.ChatRolePageReqVO = {
 | 
				
			||||||
    ...myRoleParams,
 | 
					    ...myRoleParams,
 | 
				
			||||||
    name: search.value,
 | 
					    name: search.value,
 | 
				
			||||||
| 
						 | 
					@ -66,10 +66,10 @@ const getMyRole = async (append?: boolean) => {
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    myRoleList.value = list;
 | 
					    myRoleList.value = list;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取 public role 公共角色 */
 | 
					/** 获取 public role 公共角色 */
 | 
				
			||||||
const getPublicRole = async (append?: boolean) => {
 | 
					async function getPublicRole(append?: boolean) {
 | 
				
			||||||
  const params: AiModelChatRoleApi.ChatRolePageReqVO = {
 | 
					  const params: AiModelChatRoleApi.ChatRolePageReqVO = {
 | 
				
			||||||
    ...publicRoleParams,
 | 
					    ...publicRoleParams,
 | 
				
			||||||
    category: activeCategory.value === '全部' ? '' : activeCategory.value,
 | 
					    category: activeCategory.value === '全部' ? '' : activeCategory.value,
 | 
				
			||||||
| 
						 | 
					@ -82,10 +82,10 @@ const getPublicRole = async (append?: boolean) => {
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    publicRoleList.value = list;
 | 
					    publicRoleList.value = list;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取选中的 tabs 角色 */
 | 
					/** 获取选中的 tabs 角色 */
 | 
				
			||||||
const getActiveTabsRole = async () => {
 | 
					async function getActiveTabsRole() {
 | 
				
			||||||
  if (activeTab.value === 'my-role') {
 | 
					  if (activeTab.value === 'my-role') {
 | 
				
			||||||
    myRoleParams.pageNo = 1;
 | 
					    myRoleParams.pageNo = 1;
 | 
				
			||||||
    await getMyRole();
 | 
					    await getMyRole();
 | 
				
			||||||
| 
						 | 
					@ -93,43 +93,44 @@ const getActiveTabsRole = async () => {
 | 
				
			||||||
    publicRoleParams.pageNo = 1;
 | 
					    publicRoleParams.pageNo = 1;
 | 
				
			||||||
    await getPublicRole();
 | 
					    await getPublicRole();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取角色分类列表 */
 | 
					/** 获取角色分类列表 */
 | 
				
			||||||
const getRoleCategoryList = async () => {
 | 
					async function getRoleCategoryList() {
 | 
				
			||||||
  categoryList.value = ['全部', ...(await getCategoryList())];
 | 
					  categoryList.value = ['全部', ...(await getCategoryList())];
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理分类点击 */
 | 
					/** 处理分类点击 */
 | 
				
			||||||
const handlerCategoryClick = async (category: string) => {
 | 
					async function handlerCategoryClick(category: string) {
 | 
				
			||||||
  // 切换选择的分类
 | 
					  // 切换选择的分类
 | 
				
			||||||
  activeCategory.value = category;
 | 
					  activeCategory.value = category;
 | 
				
			||||||
  // 筛选
 | 
					  // 筛选
 | 
				
			||||||
  await getActiveTabsRole();
 | 
					  await getActiveTabsRole();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const handlerAddRole = async () => {
 | 
					async function handlerAddRole() {
 | 
				
			||||||
  formModalApi.setData({ formType: 'my-create' }).open();
 | 
					  formModalApi.setData({ formType: 'my-create' }).open();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 编辑角色 */
 | 
					/** 编辑角色 */
 | 
				
			||||||
const handlerCardEdit = async (role: any) => {
 | 
					async function handlerCardEdit(role: any) {
 | 
				
			||||||
  formModalApi.setData({ formType: 'my-update', id: role.id }).open();
 | 
					  formModalApi.setData({ formType: 'my-update', id: role.id }).open();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 添加角色成功 */
 | 
					/** 添加角色成功 */
 | 
				
			||||||
const handlerAddRoleSuccess = async () => {
 | 
					async function handlerAddRoleSuccess() {
 | 
				
			||||||
  // 刷新数据
 | 
					  // 刷新数据
 | 
				
			||||||
  await getActiveTabsRole();
 | 
					  await getActiveTabsRole();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 删除角色 */
 | 
					/** 删除角色 */
 | 
				
			||||||
const handlerCardDelete = async (role: any) => {
 | 
					async function handlerCardDelete(role: any) {
 | 
				
			||||||
  await deleteMy(role.id);
 | 
					  await deleteMy(role.id);
 | 
				
			||||||
  // 刷新数据
 | 
					  // 刷新数据
 | 
				
			||||||
  await getActiveTabsRole();
 | 
					  await getActiveTabsRole();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 角色分页:获取下一页 */
 | 
					/** 角色分页:获取下一页 */
 | 
				
			||||||
const handlerCardPage = async (type: string) => {
 | 
					async function handlerCardPage(type: string) {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    loading.value = true;
 | 
					    loading.value = true;
 | 
				
			||||||
    if (type === 'public') {
 | 
					    if (type === 'public') {
 | 
				
			||||||
| 
						 | 
					@ -142,10 +143,10 @@ const handlerCardPage = async (type: string) => {
 | 
				
			||||||
  } finally {
 | 
					  } finally {
 | 
				
			||||||
    loading.value = false;
 | 
					    loading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择 card 角色:新建聊天对话 */
 | 
					/** 选择 card 角色:新建聊天对话 */
 | 
				
			||||||
const handlerCardUse = async (role: any) => {
 | 
					async function handlerCardUse(role: any) {
 | 
				
			||||||
  // 1. 创建对话
 | 
					  // 1. 创建对话
 | 
				
			||||||
  const data: AiChatConversationApi.ChatConversationVO = {
 | 
					  const data: AiChatConversationApi.ChatConversationVO = {
 | 
				
			||||||
    roleId: role.id,
 | 
					    roleId: role.id,
 | 
				
			||||||
| 
						 | 
					@ -159,7 +160,8 @@ const handlerCardUse = async (role: any) => {
 | 
				
			||||||
      conversationId,
 | 
					      conversationId,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
  // 获取分类
 | 
					  // 获取分类
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@ const receiveMessageDisplayedText = ref('');
 | 
				
			||||||
// =========== 【聊天对话】相关 ===========
 | 
					// =========== 【聊天对话】相关 ===========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取对话信息 */
 | 
					/** 获取对话信息 */
 | 
				
			||||||
const getConversation = async (id: null | number) => {
 | 
					async function getConversation(id: null | number) {
 | 
				
			||||||
  if (!id) {
 | 
					  if (!id) {
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -72,7 +72,7 @@ const getConversation = async (id: null | number) => {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  activeConversation.value = conversation;
 | 
					  activeConversation.value = conversation;
 | 
				
			||||||
  activeConversationId.value = conversation.id;
 | 
					  activeConversationId.value = conversation.id;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 点击某个对话
 | 
					 * 点击某个对话
 | 
				
			||||||
| 
						 | 
					@ -80,9 +80,9 @@ const getConversation = async (id: null | number) => {
 | 
				
			||||||
 * @param conversation 选中的对话
 | 
					 * @param conversation 选中的对话
 | 
				
			||||||
 * @return 是否切换成功
 | 
					 * @return 是否切换成功
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const handleConversationClick = async (
 | 
					async function handleConversationClick(
 | 
				
			||||||
  conversation: AiChatConversationApi.ChatConversationVO,
 | 
					  conversation: AiChatConversationApi.ChatConversationVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 对话进行中,不允许切换
 | 
					  // 对话进行中,不允许切换
 | 
				
			||||||
  if (conversationInProgress.value) {
 | 
					  if (conversationInProgress.value) {
 | 
				
			||||||
    alert('对话中,不允许切换!');
 | 
					    alert('对话中,不允许切换!');
 | 
				
			||||||
| 
						 | 
					@ -99,20 +99,20 @@ const handleConversationClick = async (
 | 
				
			||||||
  // 清空输入框
 | 
					  // 清空输入框
 | 
				
			||||||
  prompt.value = '';
 | 
					  prompt.value = '';
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 删除某个对话*/
 | 
					/** 删除某个对话*/
 | 
				
			||||||
const handlerConversationDelete = async (
 | 
					async function handlerConversationDelete(
 | 
				
			||||||
  delConversation: AiChatConversationApi.ChatConversationVO,
 | 
					  delConversation: AiChatConversationApi.ChatConversationVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 删除的对话如果是当前选中的,那么就重置
 | 
					  // 删除的对话如果是当前选中的,那么就重置
 | 
				
			||||||
  if (activeConversationId.value === delConversation.id) {
 | 
					  if (activeConversationId.value === delConversation.id) {
 | 
				
			||||||
    await handleConversationClear();
 | 
					    await handleConversationClear();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 清空选中的对话 */
 | 
					/** 清空选中的对话 */
 | 
				
			||||||
const handleConversationClear = async () => {
 | 
					async function handleConversationClear() {
 | 
				
			||||||
  // 对话进行中,不允许切换
 | 
					  // 对话进行中,不允许切换
 | 
				
			||||||
  if (conversationInProgress.value) {
 | 
					  if (conversationInProgress.value) {
 | 
				
			||||||
    alert('对话中,不允许切换!');
 | 
					    alert('对话中,不允许切换!');
 | 
				
			||||||
| 
						 | 
					@ -121,31 +121,31 @@ const handleConversationClear = async () => {
 | 
				
			||||||
  activeConversationId.value = null;
 | 
					  activeConversationId.value = null;
 | 
				
			||||||
  activeConversation.value = null;
 | 
					  activeConversation.value = null;
 | 
				
			||||||
  activeMessageList.value = [];
 | 
					  activeMessageList.value = [];
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const openChatConversationUpdateForm = async () => {
 | 
					async function openChatConversationUpdateForm() {
 | 
				
			||||||
  formModalApi.setData({ id: activeConversationId.value }).open();
 | 
					  formModalApi.setData({ id: activeConversationId.value }).open();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
const handleConversationUpdateSuccess = async () => {
 | 
					async function handleConversationUpdateSuccess() {
 | 
				
			||||||
  // 对话更新成功,刷新最新信息
 | 
					  // 对话更新成功,刷新最新信息
 | 
				
			||||||
  await getConversation(activeConversationId.value);
 | 
					  await getConversation(activeConversationId.value);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理聊天对话的创建成功 */
 | 
					/** 处理聊天对话的创建成功 */
 | 
				
			||||||
const handleConversationCreate = async () => {
 | 
					async function handleConversationCreate() {
 | 
				
			||||||
  // 创建对话
 | 
					  // 创建对话
 | 
				
			||||||
  await conversationListRef.value.createConversation();
 | 
					  await conversationListRef.value.createConversation();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 处理聊天对话的创建成功 */
 | 
					/** 处理聊天对话的创建成功 */
 | 
				
			||||||
const handleConversationCreateSuccess = async () => {
 | 
					async function handleConversationCreateSuccess() {
 | 
				
			||||||
  // 创建新的对话,清空输入框
 | 
					  // 创建新的对话,清空输入框
 | 
				
			||||||
  prompt.value = '';
 | 
					  prompt.value = '';
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// =========== 【消息列表】相关 ===========
 | 
					// =========== 【消息列表】相关 ===========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取消息 message 列表 */
 | 
					/** 获取消息 message 列表 */
 | 
				
			||||||
const getMessageList = async () => {
 | 
					async function getMessageList() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    if (activeConversationId.value === null) {
 | 
					    if (activeConversationId.value === null) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
| 
						 | 
					@ -171,7 +171,7 @@ const getMessageList = async () => {
 | 
				
			||||||
    // 加载结束
 | 
					    // 加载结束
 | 
				
			||||||
    activeMessageListLoading.value = false;
 | 
					    activeMessageListLoading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 消息列表
 | 
					 * 消息列表
 | 
				
			||||||
| 
						 | 
					@ -196,17 +196,17 @@ const messageList = computed(() => {
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理删除 message 消息 */
 | 
					/** 处理删除 message 消息 */
 | 
				
			||||||
const handleMessageDelete = () => {
 | 
					function handleMessageDelete() {
 | 
				
			||||||
  if (conversationInProgress.value) {
 | 
					  if (conversationInProgress.value) {
 | 
				
			||||||
    alert('回答中,不能删除!');
 | 
					    alert('回答中,不能删除!');
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // 刷新 message 列表
 | 
					  // 刷新 message 列表
 | 
				
			||||||
  getMessageList();
 | 
					  getMessageList();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理 message 清空 */
 | 
					/** 处理 message 清空 */
 | 
				
			||||||
const handlerMessageClear = async () => {
 | 
					async function handlerMessageClear() {
 | 
				
			||||||
  if (!activeConversationId.value) {
 | 
					  if (!activeConversationId.value) {
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -218,16 +218,16 @@ const handlerMessageClear = async () => {
 | 
				
			||||||
    // 刷新 message 列表
 | 
					    // 刷新 message 列表
 | 
				
			||||||
    activeMessageList.value = [];
 | 
					    activeMessageList.value = [];
 | 
				
			||||||
  } catch {}
 | 
					  } catch {}
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 回到 message 列表的顶部 */
 | 
					/** 回到 message 列表的顶部 */
 | 
				
			||||||
const handleGoTopMessage = () => {
 | 
					function handleGoTopMessage() {
 | 
				
			||||||
  messageRef.value.handlerGoTop();
 | 
					  messageRef.value.handlerGoTop();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// =========== 【发送消息】相关 ===========
 | 
					// =========== 【发送消息】相关 ===========
 | 
				
			||||||
/** 处理来自 keydown 的发送消息 */
 | 
					/** 处理来自 keydown 的发送消息 */
 | 
				
			||||||
const handleSendByKeydown = async (event: any) => {
 | 
					async function handleSendByKeydown(event: any) {
 | 
				
			||||||
  // 判断用户是否在输入
 | 
					  // 判断用户是否在输入
 | 
				
			||||||
  if (isComposing.value) {
 | 
					  if (isComposing.value) {
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
| 
						 | 
					@ -248,15 +248,15 @@ const handleSendByKeydown = async (event: any) => {
 | 
				
			||||||
      event.preventDefault(); // 防止默认的提交行为
 | 
					      event.preventDefault(); // 防止默认的提交行为
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理来自【发送】按钮的发送消息 */
 | 
					/** 处理来自【发送】按钮的发送消息 */
 | 
				
			||||||
const handleSendByButton = () => {
 | 
					function handleSendByButton() {
 | 
				
			||||||
  doSendMessage(prompt.value?.trim() as string);
 | 
					  doSendMessage(prompt.value?.trim() as string);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理 prompt 输入变化 */
 | 
					/** 处理 prompt 输入变化 */
 | 
				
			||||||
const handlePromptInput = (event) => {
 | 
					function handlePromptInput(event: any) {
 | 
				
			||||||
  // 非输入法 输入设置为 true
 | 
					  // 非输入法 输入设置为 true
 | 
				
			||||||
  if (!isComposing.value) {
 | 
					  if (!isComposing.value) {
 | 
				
			||||||
    // 回车 event data 是 null
 | 
					    // 回车 event data 是 null
 | 
				
			||||||
| 
						 | 
					@ -273,27 +273,27 @@ const handlePromptInput = (event) => {
 | 
				
			||||||
  inputTimeout.value = setTimeout(() => {
 | 
					  inputTimeout.value = setTimeout(() => {
 | 
				
			||||||
    isComposing.value = false;
 | 
					    isComposing.value = false;
 | 
				
			||||||
  }, 400);
 | 
					  }, 400);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onCompositionstart = () => {
 | 
					function onCompositionstart() {
 | 
				
			||||||
  isComposing.value = true;
 | 
					  isComposing.value = true;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onCompositionend = () => {
 | 
					function onCompositionend() {
 | 
				
			||||||
  // console.log('输入结束...')
 | 
					  // console.log('输入结束...')
 | 
				
			||||||
  setTimeout(() => {
 | 
					  setTimeout(() => {
 | 
				
			||||||
    isComposing.value = false;
 | 
					    isComposing.value = false;
 | 
				
			||||||
  }, 200);
 | 
					  }, 200);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 真正执行【发送】消息操作 */
 | 
					/** 真正执行【发送】消息操作 */
 | 
				
			||||||
const doSendMessage = async (content: string) => {
 | 
					async function doSendMessage(content: string) {
 | 
				
			||||||
  // 校验
 | 
					  // 校验
 | 
				
			||||||
  if (content.length === 0) {
 | 
					  if (content.length === 0) {
 | 
				
			||||||
    message.error('发送失败,原因:内容为空!');
 | 
					    message.error('发送失败,原因:内容为空!');
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (activeConversationId.value == null) {
 | 
					  if (activeConversationId.value === null) {
 | 
				
			||||||
    message.error('还没创建对话,不能发送!');
 | 
					    message.error('还没创建对话,不能发送!');
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -304,12 +304,12 @@ const doSendMessage = async (content: string) => {
 | 
				
			||||||
    conversationId: activeConversationId.value,
 | 
					    conversationId: activeConversationId.value,
 | 
				
			||||||
    content,
 | 
					    content,
 | 
				
			||||||
  } as AiChatMessageApi.ChatMessageVO);
 | 
					  } as AiChatMessageApi.ChatMessageVO);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 真正执行【发送】消息操作 */
 | 
					/** 真正执行【发送】消息操作 */
 | 
				
			||||||
const doSendMessageStream = async (
 | 
					async function doSendMessageStream(
 | 
				
			||||||
  userMessage: AiChatMessageApi.ChatMessageVO,
 | 
					  userMessage: AiChatMessageApi.ChatMessageVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 创建 AbortController 实例,以便中止请求
 | 
					  // 创建 AbortController 实例,以便中止请求
 | 
				
			||||||
  conversationInAbortController.value = new AbortController();
 | 
					  conversationInAbortController.value = new AbortController();
 | 
				
			||||||
  // 标记对话进行中
 | 
					  // 标记对话进行中
 | 
				
			||||||
| 
						 | 
					@ -385,40 +385,40 @@ const doSendMessageStream = async (
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  } catch {}
 | 
					  } catch {}
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 停止 stream 流式调用 */
 | 
					/** 停止 stream 流式调用 */
 | 
				
			||||||
const stopStream = async () => {
 | 
					async function stopStream() {
 | 
				
			||||||
  // tip:如果 stream 进行中的 message,就需要调用 controller 结束
 | 
					  // tip:如果 stream 进行中的 message,就需要调用 controller 结束
 | 
				
			||||||
  if (conversationInAbortController.value) {
 | 
					  if (conversationInAbortController.value) {
 | 
				
			||||||
    conversationInAbortController.value.abort();
 | 
					    conversationInAbortController.value.abort();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // 设置为 false
 | 
					  // 设置为 false
 | 
				
			||||||
  conversationInProgress.value = false;
 | 
					  conversationInProgress.value = false;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 编辑 message:设置为 prompt,可以再次编辑 */
 | 
					/** 编辑 message:设置为 prompt,可以再次编辑 */
 | 
				
			||||||
const handleMessageEdit = (message: AiChatMessageApi.ChatMessageVO) => {
 | 
					function handleMessageEdit(message: AiChatMessageApi.ChatMessageVO) {
 | 
				
			||||||
  prompt.value = message.content;
 | 
					  prompt.value = message.content;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 刷新 message:基于指定消息,再次发起对话 */
 | 
					/** 刷新 message:基于指定消息,再次发起对话 */
 | 
				
			||||||
const handleMessageRefresh = (message: AiChatMessageApi.ChatMessageVO) => {
 | 
					function handleMessageRefresh(message: AiChatMessageApi.ChatMessageVO) {
 | 
				
			||||||
  doSendMessage(message.content);
 | 
					  doSendMessage(message.content);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ============== 【消息滚动】相关 =============
 | 
					// ============== 【消息滚动】相关 =============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 滚动到 message 底部 */
 | 
					/** 滚动到 message 底部 */
 | 
				
			||||||
const scrollToBottom = async (isIgnore?: boolean) => {
 | 
					async function scrollToBottom(isIgnore?: boolean) {
 | 
				
			||||||
  await nextTick();
 | 
					  await nextTick();
 | 
				
			||||||
  if (messageRef.value) {
 | 
					  if (messageRef.value) {
 | 
				
			||||||
    messageRef.value.scrollToBottom(isIgnore);
 | 
					    messageRef.value.scrollToBottom(isIgnore);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 自提滚动效果 */
 | 
					/** 自提滚动效果 */
 | 
				
			||||||
const textRoll = async () => {
 | 
					async function textRoll() {
 | 
				
			||||||
  let index = 0;
 | 
					  let index = 0;
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 只能执行一次
 | 
					    // 只能执行一次
 | 
				
			||||||
| 
						 | 
					@ -475,7 +475,7 @@ const textRoll = async () => {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let timer = setTimeout(task, textSpeed.value);
 | 
					    let timer = setTimeout(task, textSpeed.value);
 | 
				
			||||||
  } catch {}
 | 
					  } catch {}
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					@ -569,8 +569,8 @@ onMounted(async () => {
 | 
				
			||||||
            <MessageList
 | 
					            <MessageList
 | 
				
			||||||
              v-if="!activeMessageListLoading && messageList.length > 0"
 | 
					              v-if="!activeMessageListLoading && messageList.length > 0"
 | 
				
			||||||
              ref="messageRef"
 | 
					              ref="messageRef"
 | 
				
			||||||
              :conversation="activeConversation"
 | 
					              :conversation="activeConversation as any"
 | 
				
			||||||
              :list="messageList"
 | 
					              :list="messageList as any"
 | 
				
			||||||
              @on-delete-success="handleMessageDelete"
 | 
					              @on-delete-success="handleMessageDelete"
 | 
				
			||||||
              @on-edit="handleMessageEdit"
 | 
					              @on-edit="handleMessageEdit"
 | 
				
			||||||
              @on-refresh="handleMessageRefresh"
 | 
					              @on-refresh="handleMessageRefresh"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
import { DICT_TYPE } from '#/utils';
 | 
					import { DICT_TYPE, getRangePickerDefaultProps } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 列表的搜索表单 */
 | 
					/** 列表的搜索表单 */
 | 
				
			||||||
export function useGridFormSchemaConversation(): VbenFormSchema[] {
 | 
					export function useGridFormSchemaConversation(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -22,8 +22,7 @@ export function useGridFormSchemaConversation(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -118,8 +117,7 @@ export function useGridFormSchemaMessage(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import { confirm } from '@vben/common-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Button, Card, Image, message } from 'ant-design-vue';
 | 
					import { Button, Card, Image, message } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AiImageStatusEnum } from '#/utils/constants';
 | 
					import { AiImageStatusEnum } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 消息
 | 
					// 消息
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,20 +24,18 @@ const emits = defineEmits(['onBtnClick', 'onMjBtnClick']);
 | 
				
			||||||
const cardImageRef = ref<any>(); // 卡片 image ref
 | 
					const cardImageRef = ref<any>(); // 卡片 image ref
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理点击事件  */
 | 
					/** 处理点击事件  */
 | 
				
			||||||
const handleButtonClick = async (type: string, detail: AiImageApi.ImageVO) => {
 | 
					async function handleButtonClick(type: string, detail: AiImageApi.ImageVO) {
 | 
				
			||||||
  emits('onBtnClick', type, detail);
 | 
					  emits('onBtnClick', type, detail);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理 Midjourney 按钮点击事件  */
 | 
					/** 处理 Midjourney 按钮点击事件  */
 | 
				
			||||||
const handleMidjourneyBtnClick = async (
 | 
					async function handleMidjourneyBtnClick(
 | 
				
			||||||
  button: AiImageApi.ImageMidjourneyButtonsVO,
 | 
					  button: AiImageApi.ImageMidjourneyButtonsVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 确认窗体
 | 
					  // 确认窗体
 | 
				
			||||||
  await confirm(`确认操作 "${button.label} ${button.emoji}" ?`);
 | 
					  await confirm(`确认操作 "${button.label} ${button.emoji}" ?`);
 | 
				
			||||||
  emits('onMjBtnClick', button, props.detail);
 | 
					  emits('onMjBtnClick', button, props.detail);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// emits
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 监听详情 */
 | 
					/** 监听详情 */
 | 
				
			||||||
const { detail } = toRefs(props);
 | 
					const { detail } = toRefs(props);
 | 
				
			||||||
| 
						 | 
					@ -46,7 +44,7 @@ watch(detail, async (newVal) => {
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const loading = ref();
 | 
					const loading = ref();
 | 
				
			||||||
/** 处理加载状态 */
 | 
					/** 处理加载状态 */
 | 
				
			||||||
const handleLoading = async (status: number) => {
 | 
					async function handleLoading(status: number) {
 | 
				
			||||||
  // 情况一:如果是生成中,则设置加载中的 loading
 | 
					  // 情况一:如果是生成中,则设置加载中的 loading
 | 
				
			||||||
  if (status === AiImageStatusEnum.IN_PROGRESS) {
 | 
					  if (status === AiImageStatusEnum.IN_PROGRESS) {
 | 
				
			||||||
    loading.value = message.loading({
 | 
					    loading.value = message.loading({
 | 
				
			||||||
| 
						 | 
					@ -57,7 +55,7 @@ const handleLoading = async (status: number) => {
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    if (loading.value) setTimeout(loading.value, 100);
 | 
					    if (loading.value) setTimeout(loading.value, 100);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,8 @@ import type { AiImageApi } from '#/api/ai/image';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ref, toRefs, watch } from 'vue';
 | 
					import { ref, toRefs, watch } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { formatDate } from '@vben/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Image } from 'ant-design-vue';
 | 
					import { Image } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getImageMy } from '#/api/ai/image';
 | 
					import { getImageMy } from '#/api/ai/image';
 | 
				
			||||||
| 
						 | 
					@ -12,8 +14,7 @@ import {
 | 
				
			||||||
  StableDiffusionClipGuidancePresets,
 | 
					  StableDiffusionClipGuidancePresets,
 | 
				
			||||||
  StableDiffusionSamplers,
 | 
					  StableDiffusionSamplers,
 | 
				
			||||||
  StableDiffusionStylePresets,
 | 
					  StableDiffusionStylePresets,
 | 
				
			||||||
} from '#/utils/constants';
 | 
					} from '#/utils';
 | 
				
			||||||
import { formatTime } from '#/utils/formatTime';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 图片详细信息
 | 
					// 图片详细信息
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
| 
						 | 
					@ -25,9 +26,9 @@ const props = defineProps({
 | 
				
			||||||
const detail = ref<AiImageApi.ImageVO>({} as AiImageApi.ImageVO);
 | 
					const detail = ref<AiImageApi.ImageVO>({} as AiImageApi.ImageVO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**  获取图片详情  */
 | 
					/**  获取图片详情  */
 | 
				
			||||||
const getImageDetail = async (id: number) => {
 | 
					async function getImageDetail(id: number) {
 | 
				
			||||||
  detail.value = await getImageMy(id);
 | 
					  detail.value = await getImageMy(id);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { id } = toRefs(props);
 | 
					const { id } = toRefs(props);
 | 
				
			||||||
watch(
 | 
					watch(
 | 
				
			||||||
| 
						 | 
					@ -53,10 +54,10 @@ watch(
 | 
				
			||||||
    <div class="tip text-lg font-bold">时间</div>
 | 
					    <div class="tip text-lg font-bold">时间</div>
 | 
				
			||||||
    <div class="body mt-2 text-gray-600">
 | 
					    <div class="body mt-2 text-gray-600">
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        提交时间:{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
 | 
					        提交时间:{{ formatDate(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        生成时间:{{ formatTime(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}
 | 
					        生成时间:{{ formatDate(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import { onMounted, onUnmounted, reactive, ref } from 'vue';
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from 'vue-router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { confirm, useVbenDrawer } from '@vben/common-ui';
 | 
					import { confirm, useVbenDrawer } from '@vben/common-ui';
 | 
				
			||||||
 | 
					import { downloadFileFromImageUrl } from '@vben/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useDebounceFn } from '@vueuse/core';
 | 
					import { useDebounceFn } from '@vueuse/core';
 | 
				
			||||||
import { Button, Card, message, Pagination } from 'ant-design-vue';
 | 
					import { Button, Card, message, Pagination } from 'ant-design-vue';
 | 
				
			||||||
| 
						 | 
					@ -15,8 +16,7 @@ import {
 | 
				
			||||||
  getImagePageMy,
 | 
					  getImagePageMy,
 | 
				
			||||||
  midjourneyAction,
 | 
					  midjourneyAction,
 | 
				
			||||||
} from '#/api/ai/image';
 | 
					} from '#/api/ai/image';
 | 
				
			||||||
import { AiImageStatusEnum } from '#/utils/constants';
 | 
					import { AiImageStatusEnum } from '#/utils';
 | 
				
			||||||
import { download } from '#/utils/download';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ImageCard from './ImageCard.vue';
 | 
					import ImageCard from './ImageCard.vue';
 | 
				
			||||||
import ImageDetail from './ImageDetail.vue';
 | 
					import ImageDetail from './ImageDetail.vue';
 | 
				
			||||||
| 
						 | 
					@ -43,18 +43,18 @@ const inProgressTimer = ref<any>(); // 生成中的 image 定时器,轮询生
 | 
				
			||||||
const showImageDetailId = ref<number>(0); // 图片详情的图片编号
 | 
					const showImageDetailId = ref<number>(0); // 图片详情的图片编号
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理查看绘图作品 */
 | 
					/** 处理查看绘图作品 */
 | 
				
			||||||
const handleViewPublic = () => {
 | 
					function handleViewPublic() {
 | 
				
			||||||
  router.push({
 | 
					  router.push({
 | 
				
			||||||
    name: 'AiImageSquare',
 | 
					    name: 'AiImageSquare',
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 查看图片的详情  */
 | 
					/** 查看图片的详情  */
 | 
				
			||||||
const handleDetailOpen = async () => {
 | 
					async function handleDetailOpen() {
 | 
				
			||||||
  drawerApi.open();
 | 
					  drawerApi.open();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 获得 image 图片列表 */
 | 
					/** 获得 image 图片列表 */
 | 
				
			||||||
const getImageList = async () => {
 | 
					async function getImageList() {
 | 
				
			||||||
  const loading = message.loading({
 | 
					  const loading = message.loading({
 | 
				
			||||||
    content: `加载中...`,
 | 
					    content: `加载中...`,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
| 
						 | 
					@ -77,10 +77,10 @@ const getImageList = async () => {
 | 
				
			||||||
    // 关闭正在“加载中”的 Loading
 | 
					    // 关闭正在“加载中”的 Loading
 | 
				
			||||||
    loading();
 | 
					    loading();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
const debounceGetImageList = useDebounceFn(getImageList, 80);
 | 
					const debounceGetImageList = useDebounceFn(getImageList, 80);
 | 
				
			||||||
/** 轮询生成中的 image 列表 */
 | 
					/** 轮询生成中的 image 列表 */
 | 
				
			||||||
const refreshWatchImages = async () => {
 | 
					async function refreshWatchImages() {
 | 
				
			||||||
  const imageIds = Object.keys(inProgressImageMap.value).map(Number);
 | 
					  const imageIds = Object.keys(inProgressImageMap.value).map(Number);
 | 
				
			||||||
  if (imageIds.length === 0) {
 | 
					  if (imageIds.length === 0) {
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
| 
						 | 
					@ -101,13 +101,13 @@ const refreshWatchImages = async () => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  inProgressImageMap.value = newWatchImages;
 | 
					  inProgressImageMap.value = newWatchImages;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 图片的点击事件 */
 | 
					/** 图片的点击事件 */
 | 
				
			||||||
const handleImageButtonClick = async (
 | 
					async function handleImageButtonClick(
 | 
				
			||||||
  type: string,
 | 
					  type: string,
 | 
				
			||||||
  imageDetail: AiImageApi.ImageVO,
 | 
					  imageDetail: AiImageApi.ImageVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 详情
 | 
					  // 详情
 | 
				
			||||||
  if (type === 'more') {
 | 
					  if (type === 'more') {
 | 
				
			||||||
    showImageDetailId.value = imageDetail.id;
 | 
					    showImageDetailId.value = imageDetail.id;
 | 
				
			||||||
| 
						 | 
					@ -124,20 +124,23 @@ const handleImageButtonClick = async (
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // 下载
 | 
					  // 下载
 | 
				
			||||||
  if (type === 'download') {
 | 
					  if (type === 'download') {
 | 
				
			||||||
    await download.image({ url: imageDetail.picUrl });
 | 
					    await downloadFileFromImageUrl({
 | 
				
			||||||
 | 
					      fileName: imageDetail.model,
 | 
				
			||||||
 | 
					      source: imageDetail.picUrl,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // 重新生成
 | 
					  // 重新生成
 | 
				
			||||||
  if (type === 'regeneration') {
 | 
					  if (type === 'regeneration') {
 | 
				
			||||||
    await emits('onRegeneration', imageDetail);
 | 
					    await emits('onRegeneration', imageDetail);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 处理 Midjourney 按钮点击事件  */
 | 
					/** 处理 Midjourney 按钮点击事件  */
 | 
				
			||||||
const handleImageMidjourneyButtonClick = async (
 | 
					async function handleImageMidjourneyButtonClick(
 | 
				
			||||||
  button: AiImageApi.ImageMidjourneyButtonsVO,
 | 
					  button: AiImageApi.ImageMidjourneyButtonsVO,
 | 
				
			||||||
  imageDetail: AiImageApi.ImageVO,
 | 
					  imageDetail: AiImageApi.ImageVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  // 1. 构建 params 参数
 | 
					  // 1. 构建 params 参数
 | 
				
			||||||
  const data = {
 | 
					  const data = {
 | 
				
			||||||
    id: imageDetail.id,
 | 
					    id: imageDetail.id,
 | 
				
			||||||
| 
						 | 
					@ -147,7 +150,7 @@ const handleImageMidjourneyButtonClick = async (
 | 
				
			||||||
  await midjourneyAction(data);
 | 
					  await midjourneyAction(data);
 | 
				
			||||||
  // 3. 刷新列表
 | 
					  // 3. 刷新列表
 | 
				
			||||||
  await getImageList();
 | 
					  await getImageList();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ getImageList }); /** 组件挂在的时候 */
 | 
					defineExpose({ getImageList }); /** 组件挂在的时候 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,11 +10,7 @@ import { confirm } from '@vben/common-ui';
 | 
				
			||||||
import { Button, InputNumber, Select, Space, Textarea } from 'ant-design-vue';
 | 
					import { Button, InputNumber, Select, Space, Textarea } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { drawImage } from '#/api/ai/image';
 | 
					import { drawImage } from '#/api/ai/image';
 | 
				
			||||||
import {
 | 
					import { AiPlatformEnum, ImageHotWords, OtherPlatformEnum } from '#/utils';
 | 
				
			||||||
  AiPlatformEnum,
 | 
					 | 
				
			||||||
  ImageHotWords,
 | 
					 | 
				
			||||||
  OtherPlatformEnum,
 | 
					 | 
				
			||||||
} from '#/utils/constants';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 消息弹窗
 | 
					// 消息弹窗
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +35,7 @@ const platformModels = ref<AiModelModelApi.ModelVO[]>([]); // 模型列表
 | 
				
			||||||
const modelId = ref<number>(); // 选中的模型
 | 
					const modelId = ref<number>(); // 选中的模型
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择热词 */
 | 
					/** 选择热词 */
 | 
				
			||||||
const handleHotWordClick = async (hotWord: string) => {
 | 
					async function handleHotWordClick(hotWord: string) {
 | 
				
			||||||
  // 情况一:取消选中
 | 
					  // 情况一:取消选中
 | 
				
			||||||
  if (selectHotWord.value === hotWord) {
 | 
					  if (selectHotWord.value === hotWord) {
 | 
				
			||||||
    selectHotWord.value = '';
 | 
					    selectHotWord.value = '';
 | 
				
			||||||
| 
						 | 
					@ -49,10 +45,10 @@ const handleHotWordClick = async (hotWord: string) => {
 | 
				
			||||||
  // 情况二:选中
 | 
					  // 情况二:选中
 | 
				
			||||||
  selectHotWord.value = hotWord; // 选中
 | 
					  selectHotWord.value = hotWord; // 选中
 | 
				
			||||||
  prompt.value = hotWord; // 替换提示词
 | 
					  prompt.value = hotWord; // 替换提示词
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 图片生成 */
 | 
					/** 图片生成 */
 | 
				
			||||||
const handleGenerateImage = async () => {
 | 
					async function handleGenerateImage() {
 | 
				
			||||||
  // 二次确认
 | 
					  // 二次确认
 | 
				
			||||||
  await confirm(`确认生成内容?`);
 | 
					  await confirm(`确认生成内容?`);
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
| 
						 | 
					@ -76,17 +72,17 @@ const handleGenerateImage = async () => {
 | 
				
			||||||
    // 加载结束
 | 
					    // 加载结束
 | 
				
			||||||
    drawIn.value = false;
 | 
					    drawIn.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 填充值 */
 | 
					/** 填充值 */
 | 
				
			||||||
const settingValues = async (detail: AiImageApi.ImageVO) => {
 | 
					async function settingValues(detail: AiImageApi.ImageVO) {
 | 
				
			||||||
  prompt.value = detail.prompt;
 | 
					  prompt.value = detail.prompt;
 | 
				
			||||||
  width.value = detail.width;
 | 
					  width.value = detail.width;
 | 
				
			||||||
  height.value = detail.height;
 | 
					  height.value = detail.height;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 平台切换 */
 | 
					/** 平台切换 */
 | 
				
			||||||
const handlerPlatformChange = async (platform: any) => {
 | 
					async function handlerPlatformChange(platform: any) {
 | 
				
			||||||
  // 根据选择的平台筛选模型
 | 
					  // 根据选择的平台筛选模型
 | 
				
			||||||
  platformModels.value = props.models.filter(
 | 
					  platformModels.value = props.models.filter(
 | 
				
			||||||
    (item: AiModelModelApi.ModelVO) => item.platform === platform,
 | 
					    (item: AiModelModelApi.ModelVO) => item.platform === platform,
 | 
				
			||||||
| 
						 | 
					@ -96,7 +92,7 @@ const handlerPlatformChange = async (platform: any) => {
 | 
				
			||||||
      ? platformModels.value[0].id
 | 
					      ? platformModels.value[0].id
 | 
				
			||||||
      : undefined;
 | 
					      : undefined;
 | 
				
			||||||
  // 切换平台,默认选择一个模型
 | 
					  // 切换平台,默认选择一个模型
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 监听 models 变化 */
 | 
					/** 监听 models 变化 */
 | 
				
			||||||
watch(
 | 
					watch(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type { AiImageApi } from '#/api/ai/image';
 | 
					import type { AiImageApi } from '#/api/ai/image';
 | 
				
			||||||
import type { AiModelModelApi } from '#/api/ai/model/model';
 | 
					import type { AiModelModelApi } from '#/api/ai/model/model';
 | 
				
			||||||
import type { ImageModelVO, ImageSizeVO } from '#/utils/constants';
 | 
					import type { ImageModelVO, ImageSizeVO } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ref } from 'vue';
 | 
					import { ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ import {
 | 
				
			||||||
  Dall3SizeList,
 | 
					  Dall3SizeList,
 | 
				
			||||||
  Dall3StyleList,
 | 
					  Dall3StyleList,
 | 
				
			||||||
  ImageHotWords,
 | 
					  ImageHotWords,
 | 
				
			||||||
} from '#/utils/constants';
 | 
					} from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 接收父组件传入的模型列表
 | 
					// 接收父组件传入的模型列表
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@ const selectSize = ref<string>('1024x1024'); // 选中 size
 | 
				
			||||||
const style = ref<string>('vivid'); // style 样式
 | 
					const style = ref<string>('vivid'); // style 样式
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择热词 */
 | 
					/** 选择热词 */
 | 
				
			||||||
const handleHotWordClick = async (hotWord: string) => {
 | 
					async function handleHotWordClick(hotWord: string) {
 | 
				
			||||||
  // 情况一:取消选中
 | 
					  // 情况一:取消选中
 | 
				
			||||||
  if (selectHotWord.value === hotWord) {
 | 
					  if (selectHotWord.value === hotWord) {
 | 
				
			||||||
    selectHotWord.value = '';
 | 
					    selectHotWord.value = '';
 | 
				
			||||||
| 
						 | 
					@ -47,10 +47,10 @@ const handleHotWordClick = async (hotWord: string) => {
 | 
				
			||||||
  // 情况二:选中
 | 
					  // 情况二:选中
 | 
				
			||||||
  selectHotWord.value = hotWord;
 | 
					  selectHotWord.value = hotWord;
 | 
				
			||||||
  prompt.value = hotWord;
 | 
					  prompt.value = hotWord;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择 model 模型 */
 | 
					/** 选择 model 模型 */
 | 
				
			||||||
const handleModelClick = async (model: ImageModelVO) => {
 | 
					async function handleModelClick(model: ImageModelVO) {
 | 
				
			||||||
  selectModel.value = model.key;
 | 
					  selectModel.value = model.key;
 | 
				
			||||||
  // 可以在这里添加模型特定的处理逻辑
 | 
					  // 可以在这里添加模型特定的处理逻辑
 | 
				
			||||||
  // 例如,如果未来需要根据不同模型设置不同参数
 | 
					  // 例如,如果未来需要根据不同模型设置不同参数
 | 
				
			||||||
| 
						 | 
					@ -73,20 +73,20 @@ const handleModelClick = async (model: ImageModelVO) => {
 | 
				
			||||||
  if (recommendedSize) {
 | 
					  if (recommendedSize) {
 | 
				
			||||||
    selectSize.value = recommendedSize.key;
 | 
					    selectSize.value = recommendedSize.key;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择 style 样式  */
 | 
					/** 选择 style 样式  */
 | 
				
			||||||
const handleStyleClick = async (imageStyle: ImageModelVO) => {
 | 
					async function handleStyleClick(imageStyle: ImageModelVO) {
 | 
				
			||||||
  style.value = imageStyle.key;
 | 
					  style.value = imageStyle.key;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择 size 大小  */
 | 
					/** 选择 size 大小  */
 | 
				
			||||||
const handleSizeClick = async (imageSize: ImageSizeVO) => {
 | 
					async function handleSizeClick(imageSize: ImageSizeVO) {
 | 
				
			||||||
  selectSize.value = imageSize.key;
 | 
					  selectSize.value = imageSize.key;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**  图片生产  */
 | 
					/**  图片生产  */
 | 
				
			||||||
const handleGenerateImage = async () => {
 | 
					async function handleGenerateImage() {
 | 
				
			||||||
  // 从 models 中查找匹配的模型
 | 
					  // 从 models 中查找匹配的模型
 | 
				
			||||||
  const matchedModel = props.models.find(
 | 
					  const matchedModel = props.models.find(
 | 
				
			||||||
    (item) =>
 | 
					    (item) =>
 | 
				
			||||||
| 
						 | 
					@ -127,10 +127,10 @@ const handleGenerateImage = async () => {
 | 
				
			||||||
    // 加载结束
 | 
					    // 加载结束
 | 
				
			||||||
    drawIn.value = false;
 | 
					    drawIn.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 填充值 */
 | 
					/** 填充值 */
 | 
				
			||||||
const settingValues = async (detail: AiImageApi.ImageVO) => {
 | 
					async function settingValues(detail: AiImageApi.ImageVO) {
 | 
				
			||||||
  prompt.value = detail.prompt;
 | 
					  prompt.value = detail.prompt;
 | 
				
			||||||
  selectModel.value = detail.model;
 | 
					  selectModel.value = detail.model;
 | 
				
			||||||
  style.value = detail.options?.style;
 | 
					  style.value = detail.options?.style;
 | 
				
			||||||
| 
						 | 
					@ -138,7 +138,7 @@ const settingValues = async (detail: AiImageApi.ImageVO) => {
 | 
				
			||||||
    (item) => item.key === `${detail.width}x${detail.height}`,
 | 
					    (item) => item.key === `${detail.width}x${detail.height}`,
 | 
				
			||||||
  ) as ImageSizeVO;
 | 
					  ) as ImageSizeVO;
 | 
				
			||||||
  await handleSizeClick(imageSize);
 | 
					  await handleSizeClick(imageSize);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 暴露组件方法 */
 | 
					/** 暴露组件方法 */
 | 
				
			||||||
defineExpose({ settingValues });
 | 
					defineExpose({ settingValues });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type { AiImageApi } from '#/api/ai/image';
 | 
					import type { AiImageApi } from '#/api/ai/image';
 | 
				
			||||||
import type { AiModelModelApi } from '#/api/ai/model/model';
 | 
					import type { AiModelModelApi } from '#/api/ai/model/model';
 | 
				
			||||||
import type { ImageModelVO, ImageSizeVO } from '#/utils/constants';
 | 
					import type { ImageModelVO, ImageSizeVO } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ref } from 'vue';
 | 
					import { ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ import {
 | 
				
			||||||
  MidjourneySizeList,
 | 
					  MidjourneySizeList,
 | 
				
			||||||
  MidjourneyVersions,
 | 
					  MidjourneyVersions,
 | 
				
			||||||
  NijiVersionList,
 | 
					  NijiVersionList,
 | 
				
			||||||
} from '#/utils/constants';
 | 
					} from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 消息弹窗
 | 
					// 消息弹窗
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ const selectVersion = ref<any>('6.0'); // 选中的 version
 | 
				
			||||||
const versionList = ref<any>(MidjourneyVersions); // version 列表
 | 
					const versionList = ref<any>(MidjourneyVersions); // version 列表
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择热词 */
 | 
					/** 选择热词 */
 | 
				
			||||||
const handleHotWordClick = async (hotWord: string) => {
 | 
					async function handleHotWordClick(hotWord: string) {
 | 
				
			||||||
  // 情况一:取消选中
 | 
					  // 情况一:取消选中
 | 
				
			||||||
  if (selectHotWord.value === hotWord) {
 | 
					  if (selectHotWord.value === hotWord) {
 | 
				
			||||||
    selectHotWord.value = '';
 | 
					    selectHotWord.value = '';
 | 
				
			||||||
| 
						 | 
					@ -61,23 +61,23 @@ const handleHotWordClick = async (hotWord: string) => {
 | 
				
			||||||
  // 情况二:选中
 | 
					  // 情况二:选中
 | 
				
			||||||
  selectHotWord.value = hotWord; // 选中
 | 
					  selectHotWord.value = hotWord; // 选中
 | 
				
			||||||
  prompt.value = hotWord; // 设置提示次
 | 
					  prompt.value = hotWord; // 设置提示次
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 点击 size 尺寸 */
 | 
					/** 点击 size 尺寸 */
 | 
				
			||||||
const handleSizeClick = async (imageSize: ImageSizeVO) => {
 | 
					async function handleSizeClick(imageSize: ImageSizeVO) {
 | 
				
			||||||
  selectSize.value = imageSize.key;
 | 
					  selectSize.value = imageSize.key;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 点击 model 模型 */
 | 
					/** 点击 model 模型 */
 | 
				
			||||||
const handleModelClick = async (model: ImageModelVO) => {
 | 
					async function handleModelClick(model: ImageModelVO) {
 | 
				
			||||||
  selectModel.value = model.key;
 | 
					  selectModel.value = model.key;
 | 
				
			||||||
  versionList.value =
 | 
					  versionList.value =
 | 
				
			||||||
    model.key === 'niji' ? NijiVersionList : MidjourneyVersions;
 | 
					    model.key === 'niji' ? NijiVersionList : MidjourneyVersions;
 | 
				
			||||||
  selectVersion.value = versionList.value[0].value;
 | 
					  selectVersion.value = versionList.value[0].value;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 图片生成 */
 | 
					/** 图片生成 */
 | 
				
			||||||
const handleGenerateImage = async () => {
 | 
					async function handleGenerateImage() {
 | 
				
			||||||
  // 从 models 中查找匹配的模型
 | 
					  // 从 models 中查找匹配的模型
 | 
				
			||||||
  const matchedModel = props.models.find(
 | 
					  const matchedModel = props.models.find(
 | 
				
			||||||
    (item) =>
 | 
					    (item) =>
 | 
				
			||||||
| 
						 | 
					@ -115,10 +115,10 @@ const handleGenerateImage = async () => {
 | 
				
			||||||
    // 加载结束
 | 
					    // 加载结束
 | 
				
			||||||
    drawIn.value = false;
 | 
					    drawIn.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 填充值 */
 | 
					/** 填充值 */
 | 
				
			||||||
const settingValues = async (detail: AiImageApi.ImageVO) => {
 | 
					async function settingValues(detail: AiImageApi.ImageVO) {
 | 
				
			||||||
  // 提示词
 | 
					  // 提示词
 | 
				
			||||||
  prompt.value = detail.prompt;
 | 
					  prompt.value = detail.prompt;
 | 
				
			||||||
  // image size
 | 
					  // image size
 | 
				
			||||||
| 
						 | 
					@ -137,7 +137,7 @@ const settingValues = async (detail: AiImageApi.ImageVO) => {
 | 
				
			||||||
  ).value;
 | 
					  ).value;
 | 
				
			||||||
  // image
 | 
					  // image
 | 
				
			||||||
  referImageUrl.value = detail.options.referImageUrl;
 | 
					  referImageUrl.value = detail.options.referImageUrl;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 暴露组件方法 */
 | 
					/** 暴露组件方法 */
 | 
				
			||||||
defineExpose({ settingValues });
 | 
					defineExpose({ settingValues });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,10 +23,7 @@ import {
 | 
				
			||||||
  StableDiffusionClipGuidancePresets,
 | 
					  StableDiffusionClipGuidancePresets,
 | 
				
			||||||
  StableDiffusionSamplers,
 | 
					  StableDiffusionSamplers,
 | 
				
			||||||
  StableDiffusionStylePresets,
 | 
					  StableDiffusionStylePresets,
 | 
				
			||||||
} from '#/utils/constants';
 | 
					} from '#/utils';
 | 
				
			||||||
import { hasChinese } from '#/utils/utils';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 消息弹窗
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 接收父组件传入的模型列表
 | 
					// 接收父组件传入的模型列表
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
| 
						 | 
					@ -35,8 +32,13 @@ const props = defineProps({
 | 
				
			||||||
    default: () => [] as AiModelModelApi.ModelVO[],
 | 
					    default: () => [] as AiModelModelApi.ModelVO[],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emits = defineEmits(['onDrawStart', 'onDrawComplete']);
 | 
					const emits = defineEmits(['onDrawStart', 'onDrawComplete']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function hasChinese(str: string) {
 | 
				
			||||||
 | 
					  return /[\u4E00-\u9FA5]/.test(str);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 定义属性
 | 
					// 定义属性
 | 
				
			||||||
const drawIn = ref<boolean>(false); // 生成中
 | 
					const drawIn = ref<boolean>(false); // 生成中
 | 
				
			||||||
const selectHotWord = ref<string>(''); // 选中的热词
 | 
					const selectHotWord = ref<string>(''); // 选中的热词
 | 
				
			||||||
| 
						 | 
					@ -52,7 +54,7 @@ const clipGuidancePreset = ref<string>('NONE'); // 文本提示相匹配的图
 | 
				
			||||||
const stylePreset = ref<string>('3d-model'); // 风格
 | 
					const stylePreset = ref<string>('3d-model'); // 风格
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择热词 */
 | 
					/** 选择热词 */
 | 
				
			||||||
const handleHotWordClick = async (hotWord: string) => {
 | 
					async function handleHotWordClick(hotWord: string) {
 | 
				
			||||||
  // 情况一:取消选中
 | 
					  // 情况一:取消选中
 | 
				
			||||||
  if (selectHotWord.value === hotWord) {
 | 
					  if (selectHotWord.value === hotWord) {
 | 
				
			||||||
    selectHotWord.value = '';
 | 
					    selectHotWord.value = '';
 | 
				
			||||||
| 
						 | 
					@ -62,10 +64,10 @@ const handleHotWordClick = async (hotWord: string) => {
 | 
				
			||||||
  // 情况二:选中
 | 
					  // 情况二:选中
 | 
				
			||||||
  selectHotWord.value = hotWord; // 选中
 | 
					  selectHotWord.value = hotWord; // 选中
 | 
				
			||||||
  prompt.value = hotWord; // 替换提示词
 | 
					  prompt.value = hotWord; // 替换提示词
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 图片生成 */
 | 
					/** 图片生成 */
 | 
				
			||||||
const handleGenerateImage = async () => {
 | 
					async function handleGenerateImage() {
 | 
				
			||||||
  // 从 models 中查找匹配的模型
 | 
					  // 从 models 中查找匹配的模型
 | 
				
			||||||
  const selectModel = 'stable-diffusion-v1-6';
 | 
					  const selectModel = 'stable-diffusion-v1-6';
 | 
				
			||||||
  const matchedModel = props.models.find(
 | 
					  const matchedModel = props.models.find(
 | 
				
			||||||
| 
						 | 
					@ -112,10 +114,10 @@ const handleGenerateImage = async () => {
 | 
				
			||||||
    // 加载结束
 | 
					    // 加载结束
 | 
				
			||||||
    drawIn.value = false;
 | 
					    drawIn.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 填充值 */
 | 
					/** 填充值 */
 | 
				
			||||||
const settingValues = async (detail: AiImageApi.ImageVO) => {
 | 
					async function settingValues(detail: AiImageApi.ImageVO) {
 | 
				
			||||||
  prompt.value = detail.prompt;
 | 
					  prompt.value = detail.prompt;
 | 
				
			||||||
  width.value = detail.width;
 | 
					  width.value = detail.width;
 | 
				
			||||||
  height.value = detail.height;
 | 
					  height.value = detail.height;
 | 
				
			||||||
| 
						 | 
					@ -125,7 +127,7 @@ const settingValues = async (detail: AiImageApi.ImageVO) => {
 | 
				
			||||||
  sampler.value = detail.options?.sampler;
 | 
					  sampler.value = detail.options?.sampler;
 | 
				
			||||||
  clipGuidancePreset.value = detail.options?.clipGuidancePreset;
 | 
					  clipGuidancePreset.value = detail.options?.clipGuidancePreset;
 | 
				
			||||||
  stylePreset.value = detail.options?.stylePreset;
 | 
					  stylePreset.value = detail.options?.stylePreset;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 暴露组件方法 */
 | 
					/** 暴露组件方法 */
 | 
				
			||||||
defineExpose({ settingValues });
 | 
					defineExpose({ settingValues });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import { Page } from '@vben/common-ui';
 | 
				
			||||||
import { Segmented } from 'ant-design-vue';
 | 
					import { Segmented } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getModelSimpleList } from '#/api/ai/model/model';
 | 
					import { getModelSimpleList } from '#/api/ai/model/model';
 | 
				
			||||||
import { AiModelTypeEnum, AiPlatformEnum } from '#/utils/constants';
 | 
					import { AiModelTypeEnum, AiPlatformEnum } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Common from './components/common/index.vue';
 | 
					import Common from './components/common/index.vue';
 | 
				
			||||||
import Dall3 from './components/dall3/index.vue';
 | 
					import Dall3 from './components/dall3/index.vue';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
import { DICT_TYPE, getDictOptions } from '#/utils';
 | 
					import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 列表的搜索表单 */
 | 
					/** 列表的搜索表单 */
 | 
				
			||||||
export function useGridFormSchema(): VbenFormSchema[] {
 | 
					export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -49,8 +49,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@ import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 | 
				
			||||||
import { deleteImage, getImagePage, updateImage } from '#/api/ai/image';
 | 
					import { deleteImage, getImagePage, updateImage } from '#/api/ai/image';
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
import { $t } from '#/locales';
 | 
					import { $t } from '#/locales';
 | 
				
			||||||
import { AiImageStatusEnum } from '#/utils/constants';
 | 
					import { AiImageStatusEnum } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useGridColumns, useGridFormSchema } from './data';
 | 
					import { useGridColumns, useGridFormSchema } from './data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import { useDebounceFn } from '@vueuse/core';
 | 
				
			||||||
import { Input, Pagination } from 'ant-design-vue';
 | 
					import { Input, Pagination } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getImagePageMy } from '#/api/ai/image';
 | 
					import { getImagePageMy } from '#/api/ai/image';
 | 
				
			||||||
// TODO @fan:加个 loading 加载中的状态
 | 
					
 | 
				
			||||||
const loading = ref(true); // 列表的加载中
 | 
					const loading = ref(true); // 列表的加载中
 | 
				
			||||||
const list = ref<AiImageApi.ImageVO[]>([]); // 列表的数据
 | 
					const list = ref<AiImageApi.ImageVO[]>([]); // 列表的数据
 | 
				
			||||||
const total = ref(0); // 列表的总页数
 | 
					const total = ref(0); // 列表的总页数
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,7 @@ const queryParams = reactive({
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 查询列表 */
 | 
					/** 查询列表 */
 | 
				
			||||||
const getList = async () => {
 | 
					async function getList() {
 | 
				
			||||||
  loading.value = true;
 | 
					  loading.value = true;
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const data = await getImagePageMy(queryParams);
 | 
					    const data = await getImagePageMy(queryParams);
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@ const getList = async () => {
 | 
				
			||||||
  } finally {
 | 
					  } finally {
 | 
				
			||||||
    loading.value = false;
 | 
					    loading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
const debounceGetList = useDebounceFn(getList, 80);
 | 
					const debounceGetList = useDebounceFn(getList, 80);
 | 
				
			||||||
/** 搜索按钮操作 */
 | 
					/** 搜索按钮操作 */
 | 
				
			||||||
const handleQuery = () => {
 | 
					const handleQuery = () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,9 @@ const parent = inject('parent') as any;
 | 
				
			||||||
const pollingTimer = ref<null | number>(null); // 轮询定时器 ID,用于跟踪和清除轮询进程
 | 
					const pollingTimer = ref<null | number>(null); // 轮询定时器 ID,用于跟踪和清除轮询进程
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 判断文件处理是否完成 */
 | 
					/** 判断文件处理是否完成 */
 | 
				
			||||||
const isProcessComplete = (file: any) => {
 | 
					function isProcessComplete(file: any) {
 | 
				
			||||||
  return file.progress === 100;
 | 
					  return file.progress === 100;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 判断所有文件是否都处理完成 */
 | 
					/** 判断所有文件是否都处理完成 */
 | 
				
			||||||
const allProcessComplete = computed(() => {
 | 
					const allProcessComplete = computed(() => {
 | 
				
			||||||
| 
						 | 
					@ -29,14 +29,14 @@ const allProcessComplete = computed(() => {
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 完成按钮点击事件处理 */
 | 
					/** 完成按钮点击事件处理 */
 | 
				
			||||||
const handleComplete = () => {
 | 
					function handleComplete() {
 | 
				
			||||||
  if (parent?.exposed?.handleBack) {
 | 
					  if (parent?.exposed?.handleBack) {
 | 
				
			||||||
    parent.exposed.handleBack();
 | 
					    parent.exposed.handleBack();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取文件处理进度 */
 | 
					/** 获取文件处理进度 */
 | 
				
			||||||
const getProcessList = async () => {
 | 
					async function getProcessList() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 1. 调用 API 获取处理进度
 | 
					    // 1. 调用 API 获取处理进度
 | 
				
			||||||
    const documentIds = props.modelValue.list
 | 
					    const documentIds = props.modelValue.list
 | 
				
			||||||
| 
						 | 
					@ -82,7 +82,7 @@ const getProcessList = async () => {
 | 
				
			||||||
    console.error('获取处理进度失败:', error);
 | 
					    console.error('获取处理进度失败:', error);
 | 
				
			||||||
    pollingTimer.value = window.setTimeout(getProcessList, 5000);
 | 
					    pollingTimer.value = window.setTimeout(getProcessList, 5000);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 组件挂载时开始轮询 */
 | 
					/** 组件挂载时开始轮询 */
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,12 +42,13 @@ const currentFile = ref<any>(null); // 当前选中的文件
 | 
				
			||||||
const submitLoading = ref(false); // 提交按钮加载状态
 | 
					const submitLoading = ref(false); // 提交按钮加载状态
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 选择文件 */
 | 
					/** 选择文件 */
 | 
				
			||||||
const selectFile = async (index: number) => {
 | 
					async function selectFile(index: number) {
 | 
				
			||||||
  currentFile.value = modelData.value.list[index];
 | 
					  currentFile.value = modelData.value.list[index];
 | 
				
			||||||
  await splitContentFile(currentFile.value);
 | 
					  await splitContentFile(currentFile.value);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取文件分段内容 */
 | 
					/** 获取文件分段内容 */
 | 
				
			||||||
const splitContentFile = async (file: any) => {
 | 
					async function splitContentFile(file: any) {
 | 
				
			||||||
  if (!file || !file.url) {
 | 
					  if (!file || !file.url) {
 | 
				
			||||||
    message.warning('文件 URL 不存在');
 | 
					    message.warning('文件 URL 不存在');
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
| 
						 | 
					@ -65,9 +66,9 @@ const splitContentFile = async (file: any) => {
 | 
				
			||||||
  } finally {
 | 
					  } finally {
 | 
				
			||||||
    splitLoading.value = false;
 | 
					    splitLoading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 处理预览分段 */
 | 
					/** 处理预览分段 */
 | 
				
			||||||
const handleAutoSegment = async () => {
 | 
					async function handleAutoSegment() {
 | 
				
			||||||
  // 如果没有选中文件,默认选中第一个
 | 
					  // 如果没有选中文件,默认选中第一个
 | 
				
			||||||
  if (
 | 
					  if (
 | 
				
			||||||
    !currentFile.value &&
 | 
					    !currentFile.value &&
 | 
				
			||||||
| 
						 | 
					@ -84,18 +85,18 @@ const handleAutoSegment = async () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 获取分段内容
 | 
					  // 获取分段内容
 | 
				
			||||||
  await splitContentFile(currentFile.value);
 | 
					  await splitContentFile(currentFile.value);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 上一步按钮处理 */
 | 
					/** 上一步按钮处理 */
 | 
				
			||||||
const handlePrevStep = () => {
 | 
					function handlePrevStep() {
 | 
				
			||||||
  const parentEl = parent || getCurrentInstance()?.parent;
 | 
					  const parentEl = parent || getCurrentInstance()?.parent;
 | 
				
			||||||
  if (parentEl && typeof parentEl.exposed?.goToPrevStep === 'function') {
 | 
					  if (parentEl && typeof parentEl.exposed?.goToPrevStep === 'function') {
 | 
				
			||||||
    parentEl.exposed.goToPrevStep();
 | 
					    parentEl.exposed.goToPrevStep();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 保存操作 */
 | 
					/** 保存操作 */
 | 
				
			||||||
const handleSave = async () => {
 | 
					async function handleSave() {
 | 
				
			||||||
  // 保存前验证
 | 
					  // 保存前验证
 | 
				
			||||||
  if (
 | 
					  if (
 | 
				
			||||||
    !currentFile?.value?.segments ||
 | 
					    !currentFile?.value?.segments ||
 | 
				
			||||||
| 
						 | 
					@ -140,7 +141,7 @@ const handleSave = async () => {
 | 
				
			||||||
    // 关闭按钮加载状态
 | 
					    // 关闭按钮加载状态
 | 
				
			||||||
    submitLoading.value = false;
 | 
					    submitLoading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,11 +10,11 @@ import { computed, getCurrentInstance, inject, onMounted, ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { IconifyIcon } from '@vben/icons';
 | 
					import { IconifyIcon } from '@vben/icons';
 | 
				
			||||||
import { $t } from '@vben/locales';
 | 
					import { $t } from '@vben/locales';
 | 
				
			||||||
 | 
					import { generateAcceptedFileTypes } from '@vben/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Button, Form, message, UploadDragger } from 'ant-design-vue';
 | 
					import { Button, Form, message, UploadDragger } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useUpload } from '#/components/upload/use-upload';
 | 
					import { useUpload } from '#/components/upload/use-upload';
 | 
				
			||||||
import { generateAcceptedFileTypes } from '#/utils/upload';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
  modelValue: {
 | 
					  modelValue: {
 | 
				
			||||||
| 
						 | 
					@ -70,14 +70,14 @@ const modelData = computed({
 | 
				
			||||||
  set: (val) => emit('update:modelValue', val),
 | 
					  set: (val) => emit('update:modelValue', val),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
/** 确保 list 属性存在 */
 | 
					/** 确保 list 属性存在 */
 | 
				
			||||||
const ensureListExists = () => {
 | 
					function ensureListExists() {
 | 
				
			||||||
  if (!props.modelValue.list) {
 | 
					  if (!props.modelValue.list) {
 | 
				
			||||||
    emit('update:modelValue', {
 | 
					    emit('update:modelValue', {
 | 
				
			||||||
      ...props.modelValue,
 | 
					      ...props.modelValue,
 | 
				
			||||||
      list: [],
 | 
					      list: [],
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 是否所有文件都已上传完成 */
 | 
					/** 是否所有文件都已上传完成 */
 | 
				
			||||||
const isAllUploaded = computed(() => {
 | 
					const isAllUploaded = computed(() => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
| 
						 | 
					@ -93,7 +93,7 @@ const isAllUploaded = computed(() => {
 | 
				
			||||||
 * @param file 待上传的文件
 | 
					 * @param file 待上传的文件
 | 
				
			||||||
 * @returns 是否允许上传
 | 
					 * @returns 是否允许上传
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const beforeUpload = (file: any) => {
 | 
					function beforeUpload(file: any) {
 | 
				
			||||||
  // 1.1 检查文件扩展名
 | 
					  // 1.1 检查文件扩展名
 | 
				
			||||||
  const fileName = file.name.toLowerCase();
 | 
					  const fileName = file.name.toLowerCase();
 | 
				
			||||||
  const fileExtension = fileName.slice(
 | 
					  const fileExtension = fileName.slice(
 | 
				
			||||||
| 
						 | 
					@ -112,7 +112,7 @@ const beforeUpload = (file: any) => {
 | 
				
			||||||
  // 2. 增加上传中的文件计数
 | 
					  // 2. 增加上传中的文件计数
 | 
				
			||||||
  uploadingCount.value++;
 | 
					  uploadingCount.value++;
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
async function customRequest(info: UploadRequestOption<any>) {
 | 
					async function customRequest(info: UploadRequestOption<any>) {
 | 
				
			||||||
  const file = info.file as File;
 | 
					  const file = info.file as File;
 | 
				
			||||||
  const name = file?.name;
 | 
					  const name = file?.name;
 | 
				
			||||||
| 
						 | 
					@ -148,7 +148,7 @@ async function customRequest(info: UploadRequestOption<any>) {
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param index 要移除的文件索引
 | 
					 * @param index 要移除的文件索引
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const removeFile = (index: number) => {
 | 
					function removeFile(index: number) {
 | 
				
			||||||
  // 从列表中移除文件
 | 
					  // 从列表中移除文件
 | 
				
			||||||
  const newList = [...props.modelValue.list];
 | 
					  const newList = [...props.modelValue.list];
 | 
				
			||||||
  newList.splice(index, 1);
 | 
					  newList.splice(index, 1);
 | 
				
			||||||
| 
						 | 
					@ -157,10 +157,10 @@ const removeFile = (index: number) => {
 | 
				
			||||||
    ...props.modelValue,
 | 
					    ...props.modelValue,
 | 
				
			||||||
    list: newList,
 | 
					    list: newList,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 下一步按钮处理 */
 | 
					/** 下一步按钮处理 */
 | 
				
			||||||
const handleNextStep = () => {
 | 
					function handleNextStep() {
 | 
				
			||||||
  // 1.1 检查是否有文件上传
 | 
					  // 1.1 检查是否有文件上传
 | 
				
			||||||
  if (!modelData.value.list || modelData.value.list.length === 0) {
 | 
					  if (!modelData.value.list || modelData.value.list.length === 0) {
 | 
				
			||||||
    message.warning('请上传至少一个文件');
 | 
					    message.warning('请上传至少一个文件');
 | 
				
			||||||
| 
						 | 
					@ -177,7 +177,7 @@ const handleNextStep = () => {
 | 
				
			||||||
  if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') {
 | 
					  if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') {
 | 
				
			||||||
    parentEl.exposed.goToNextStep();
 | 
					    parentEl.exposed.goToNextStep();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
| 
						 | 
					@ -210,9 +210,9 @@ onMounted(() => {
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
              <div class="ant-upload-text text-[16px] text-[#606266]">
 | 
					              <div class="ant-upload-text text-[16px] text-[#606266]">
 | 
				
			||||||
                拖拽文件至此,或者
 | 
					                拖拽文件至此,或者
 | 
				
			||||||
                <em class="cursor-pointer not-italic text-[#409eff]"
 | 
					                <em class="cursor-pointer not-italic text-[#409eff]">
 | 
				
			||||||
                  >选择文件</em
 | 
					                  选择文件
 | 
				
			||||||
                >
 | 
					                </em>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div class="ant-upload-tip mt-10px text-[12px] text-[#909399]">
 | 
					              <div class="ant-upload-tip mt-10px text-[12px] text-[#909399]">
 | 
				
			||||||
                已支持 {{ supportedFileTypes.join('、') }},每个文件不超过
 | 
					                已支持 {{ supportedFileTypes.join('、') }},每个文件不超过
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,7 +56,7 @@ provide('parent', getCurrentInstance()); // 提供 parent 给子组件使用
 | 
				
			||||||
const tabs = useTabs();
 | 
					const tabs = useTabs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 返回列表页 */
 | 
					/** 返回列表页 */
 | 
				
			||||||
const handleBack = () => {
 | 
					function handleBack() {
 | 
				
			||||||
  // 关闭当前页签
 | 
					  // 关闭当前页签
 | 
				
			||||||
  tabs.closeCurrentTab();
 | 
					  tabs.closeCurrentTab();
 | 
				
			||||||
  // 跳转到列表页,使用路径, 目前后端的路由 name: 'name'+ menuId
 | 
					  // 跳转到列表页,使用路径, 目前后端的路由 name: 'name'+ menuId
 | 
				
			||||||
| 
						 | 
					@ -66,10 +66,10 @@ const handleBack = () => {
 | 
				
			||||||
      knowledgeId: route.query.knowledgeId,
 | 
					      knowledgeId: route.query.knowledgeId,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化数据 */
 | 
					/** 初始化数据 */
 | 
				
			||||||
const initData = async () => {
 | 
					async function initData() {
 | 
				
			||||||
  if (route.query.knowledgeId) {
 | 
					  if (route.query.knowledgeId) {
 | 
				
			||||||
    formData.value.knowledgeId = route.query.knowledgeId as any;
 | 
					    formData.value.knowledgeId = route.query.knowledgeId as any;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -91,20 +91,20 @@ const initData = async () => {
 | 
				
			||||||
    // 进入下一步
 | 
					    // 进入下一步
 | 
				
			||||||
    goToNextStep();
 | 
					    goToNextStep();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 切换到下一步 */
 | 
					/** 切换到下一步 */
 | 
				
			||||||
const goToNextStep = () => {
 | 
					function goToNextStep() {
 | 
				
			||||||
  if (currentStep.value < steps.length - 1) {
 | 
					  if (currentStep.value < steps.length - 1) {
 | 
				
			||||||
    currentStep.value++;
 | 
					    currentStep.value++;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 切换到上一步 */
 | 
					/** 切换到上一步 */
 | 
				
			||||||
const goToPrevStep = () => {
 | 
					function goToPrevStep() {
 | 
				
			||||||
  if (currentStep.value > 0) {
 | 
					  if (currentStep.value > 0) {
 | 
				
			||||||
    currentStep.value--;
 | 
					    currentStep.value--;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ import {
 | 
				
			||||||
  updateKnowledgeDocumentStatus,
 | 
					  updateKnowledgeDocumentStatus,
 | 
				
			||||||
} from '#/api/ai/knowledge/document';
 | 
					} from '#/api/ai/knowledge/document';
 | 
				
			||||||
import { $t } from '#/locales';
 | 
					import { $t } from '#/locales';
 | 
				
			||||||
import { CommonStatusEnum } from '#/utils/constants';
 | 
					import { CommonStatusEnum } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useGridColumns, useGridFormSchema } from './data';
 | 
					import { useGridColumns, useGridFormSchema } from './data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import {
 | 
				
			||||||
  CommonStatusEnum,
 | 
					  CommonStatusEnum,
 | 
				
			||||||
  DICT_TYPE,
 | 
					  DICT_TYPE,
 | 
				
			||||||
  getDictOptions,
 | 
					  getDictOptions,
 | 
				
			||||||
 | 
					  getRangePickerDefaultProps,
 | 
				
			||||||
} from '#/utils';
 | 
					} from '#/utils';
 | 
				
			||||||
/** 新增/修改的表单 */
 | 
					/** 新增/修改的表单 */
 | 
				
			||||||
export function useFormSchema(): VbenFormSchema[] {
 | 
					export function useFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -112,8 +113,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,20 +57,20 @@ async function handleDelete(row: AiKnowledgeKnowledgeApi.KnowledgeVO) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/** 文档按钮操作 */
 | 
					/** 文档按钮操作 */
 | 
				
			||||||
const router = useRouter();
 | 
					const router = useRouter();
 | 
				
			||||||
const handleDocument = (id: number) => {
 | 
					function handleDocument(id: number) {
 | 
				
			||||||
  router.push({
 | 
					  router.push({
 | 
				
			||||||
    name: 'AiKnowledgeDocument',
 | 
					    name: 'AiKnowledgeDocument',
 | 
				
			||||||
    query: { knowledgeId: id },
 | 
					    query: { knowledgeId: id },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 跳转到文档召回测试页面 */
 | 
					/** 跳转到文档召回测试页面 */
 | 
				
			||||||
const handleRetrieval = (id: number) => {
 | 
					function handleRetrieval(id: number) {
 | 
				
			||||||
  router.push({
 | 
					  router.push({
 | 
				
			||||||
    name: 'AiKnowledgeRetrieval',
 | 
					    name: 'AiKnowledgeRetrieval',
 | 
				
			||||||
    query: { id },
 | 
					    query: { id },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const [Grid, gridApi] = useVbenVxeGrid({
 | 
					const [Grid, gridApi] = useVbenVxeGrid({
 | 
				
			||||||
  formOptions: {
 | 
					  formOptions: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ const queryParams = reactive({
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 调用文档召回测试接口 */
 | 
					/** 调用文档召回测试接口 */
 | 
				
			||||||
const getRetrievalResult = async () => {
 | 
					async function getRetrievalResult() {
 | 
				
			||||||
  if (!queryParams.content) {
 | 
					  if (!queryParams.content) {
 | 
				
			||||||
    message.warning('请输入查询文本');
 | 
					    message.warning('请输入查询文本');
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
| 
						 | 
					@ -54,15 +54,15 @@ const getRetrievalResult = async () => {
 | 
				
			||||||
  } finally {
 | 
					  } finally {
 | 
				
			||||||
    loading.value = false;
 | 
					    loading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 展开/收起段落内容 */
 | 
					/** 展开/收起段落内容 */
 | 
				
			||||||
const toggleExpand = (segment: any) => {
 | 
					function toggleExpand(segment: any) {
 | 
				
			||||||
  segment.expanded = !segment.expanded;
 | 
					  segment.expanded = !segment.expanded;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取知识库信息 */
 | 
					/** 获取知识库信息 */
 | 
				
			||||||
const getKnowledgeInfo = async (id: number) => {
 | 
					async function getKnowledgeInfo(id: number) {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const knowledge = await getKnowledge(id);
 | 
					    const knowledge = await getKnowledge(id);
 | 
				
			||||||
    if (knowledge) {
 | 
					    if (knowledge) {
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ const getKnowledgeInfo = async (id: number) => {
 | 
				
			||||||
        knowledge.similarityThreshold || queryParams.similarityThreshold;
 | 
					        knowledge.similarityThreshold || queryParams.similarityThreshold;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } catch {}
 | 
					  } catch {}
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ import {
 | 
				
			||||||
  updateKnowledgeSegmentStatus,
 | 
					  updateKnowledgeSegmentStatus,
 | 
				
			||||||
} from '#/api/ai/knowledge/segment';
 | 
					} from '#/api/ai/knowledge/segment';
 | 
				
			||||||
import { $t } from '#/locales';
 | 
					import { $t } from '#/locales';
 | 
				
			||||||
import { CommonStatusEnum } from '#/utils/constants';
 | 
					import { CommonStatusEnum } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useGridColumns, useGridFormSchema } from './data';
 | 
					import { useGridColumns, useGridFormSchema } from './data';
 | 
				
			||||||
import Form from './modules/form.vue';
 | 
					import Form from './modules/form.vue';
 | 
				
			||||||
| 
						 | 
					@ -93,9 +93,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 修改是否发布 */
 | 
					/** 修改是否发布 */
 | 
				
			||||||
const handleStatusChange = async (
 | 
					async function handleStatusChange(
 | 
				
			||||||
  row: AiKnowledgeSegmentApi.KnowledgeSegmentVO,
 | 
					  row: AiKnowledgeSegmentApi.KnowledgeSegmentVO,
 | 
				
			||||||
) => {
 | 
					) {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 修改状态的二次确认
 | 
					    // 修改状态的二次确认
 | 
				
			||||||
    const text = row.status ? '启用' : '禁用';
 | 
					    const text = row.status ? '启用' : '禁用';
 | 
				
			||||||
| 
						 | 
					@ -112,7 +112,8 @@ const handleStatusChange = async (
 | 
				
			||||||
        ? CommonStatusEnum.DISABLE
 | 
					        ? CommonStatusEnum.DISABLE
 | 
				
			||||||
        : CommonStatusEnum.ENABLE;
 | 
					        : CommonStatusEnum.ENABLE;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
  gridApi.formApi.setFieldValue('documentId', route.query.documentId);
 | 
					  gridApi.formApi.setFieldValue('documentId', route.query.documentId);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import { nextTick, onMounted, ref } from 'vue';
 | 
				
			||||||
import { alert, Page } from '@vben/common-ui';
 | 
					import { alert, Page } from '@vben/common-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { generateMindMap } from '#/api/ai/mindmap';
 | 
					import { generateMindMap } from '#/api/ai/mindmap';
 | 
				
			||||||
import { MindMapContentExample } from '#/utils/constants';
 | 
					import { MindMapContentExample } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Left from './modules/Left.vue';
 | 
					import Left from './modules/Left.vue';
 | 
				
			||||||
import Right from './modules/Right.vue';
 | 
					import Right from './modules/Right.vue';
 | 
				
			||||||
| 
						 | 
					@ -21,13 +21,13 @@ const leftRef = ref<InstanceType<typeof Left>>(); // 左边组件
 | 
				
			||||||
const rightRef = ref(); // 右边组件
 | 
					const rightRef = ref(); // 右边组件
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 使用已有内容直接生成 */
 | 
					/** 使用已有内容直接生成 */
 | 
				
			||||||
const directGenerate = (existPrompt: string) => {
 | 
					function directGenerate(existPrompt: string) {
 | 
				
			||||||
  isEnd.value = false; // 先设置为 false 再设置为 true,让子组建的 watch 能够监听到
 | 
					  isEnd.value = false; // 先设置为 false 再设置为 true,让子组建的 watch 能够监听到
 | 
				
			||||||
  generatedContent.value = existPrompt;
 | 
					  generatedContent.value = existPrompt;
 | 
				
			||||||
  isEnd.value = true;
 | 
					  isEnd.value = true;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 提交生成 */
 | 
					/** 提交生成 */
 | 
				
			||||||
const submit = (data: AiMindmapApi.AiMindMapGenerateReqVO) => {
 | 
					function submit(data: AiMindmapApi.AiMindMapGenerateReqVO) {
 | 
				
			||||||
  isGenerating.value = true;
 | 
					  isGenerating.value = true;
 | 
				
			||||||
  isStart.value = true;
 | 
					  isStart.value = true;
 | 
				
			||||||
  isEnd.value = false;
 | 
					  isEnd.value = false;
 | 
				
			||||||
| 
						 | 
					@ -59,13 +59,13 @@ const submit = (data: AiMindmapApi.AiMindMapGenerateReqVO) => {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    ctrl: ctrl.value,
 | 
					    ctrl: ctrl.value,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 停止 stream 生成 */
 | 
					/** 停止 stream 生成 */
 | 
				
			||||||
const stopStream = () => {
 | 
					function stopStream() {
 | 
				
			||||||
  isGenerating.value = false;
 | 
					  isGenerating.value = false;
 | 
				
			||||||
  isStart.value = false;
 | 
					  isStart.value = false;
 | 
				
			||||||
  ctrl.value?.abort();
 | 
					  ctrl.value?.abort();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import { reactive, ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Button, Textarea } from 'ant-design-vue';
 | 
					import { Button, Textarea } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { MindMapContentExample } from '#/utils/constants';
 | 
					import { MindMapContentExample } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineProps<{
 | 
					defineProps<{
 | 
				
			||||||
  isGenerating: boolean;
 | 
					  isGenerating: boolean;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,11 +7,10 @@ import {
 | 
				
			||||||
  Toolbar,
 | 
					  Toolbar,
 | 
				
			||||||
  Transformer,
 | 
					  Transformer,
 | 
				
			||||||
} from '@vben/plugins/markmap';
 | 
					} from '@vben/plugins/markmap';
 | 
				
			||||||
 | 
					import { downloadImageByCanvas } from '@vben/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Button, Card, message } from 'ant-design-vue';
 | 
					import { Button, Card, message } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { download } from '#/utils/download';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
  generatedContent: string; // 生成结果
 | 
					  generatedContent: string; // 生成结果
 | 
				
			||||||
  isEnd: boolean; // 是否结束
 | 
					  isEnd: boolean; // 是否结束
 | 
				
			||||||
| 
						 | 
					@ -85,7 +84,7 @@ const update = () => {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
/** 处理内容 */
 | 
					/** 处理内容 */
 | 
				
			||||||
const processContent = (text: string) => {
 | 
					function processContent(text: string) {
 | 
				
			||||||
  const arr: string[] = [];
 | 
					  const arr: string[] = [];
 | 
				
			||||||
  const lines = text.split('\n');
 | 
					  const lines = text.split('\n');
 | 
				
			||||||
  for (let line of lines) {
 | 
					  for (let line of lines) {
 | 
				
			||||||
| 
						 | 
					@ -97,21 +96,21 @@ const processContent = (text: string) => {
 | 
				
			||||||
    arr.push(line);
 | 
					    arr.push(line);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return arr.join('\n');
 | 
					  return arr.join('\n');
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 下载图片:download SVG to png file */
 | 
					/** 下载图片:download SVG to png file */
 | 
				
			||||||
const downloadImage = () => {
 | 
					function downloadImage() {
 | 
				
			||||||
  const svgElement = mindMapRef.value;
 | 
					  const svgElement = mindMapRef.value;
 | 
				
			||||||
  // 将 SVG 渲染到图片对象
 | 
					  // 将 SVG 渲染到图片对象
 | 
				
			||||||
  const serializer = new XMLSerializer();
 | 
					  const serializer = new XMLSerializer();
 | 
				
			||||||
  const source = `<?xml version="1.0" standalone="no"?>\r\n${serializer.serializeToString(svgRef.value!)}`;
 | 
					  const source = `<?xml version="1.0" standalone="no"?>\r\n${serializer.serializeToString(svgRef.value!)}`;
 | 
				
			||||||
  const base64Url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}`;
 | 
					  const base64Url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}`;
 | 
				
			||||||
  download.image({
 | 
					  downloadImageByCanvas({
 | 
				
			||||||
    url: base64Url,
 | 
					    url: base64Url,
 | 
				
			||||||
    canvasWidth: svgElement?.offsetWidth,
 | 
					    canvasWidth: svgElement?.offsetWidth,
 | 
				
			||||||
    canvasHeight: svgElement?.offsetHeight,
 | 
					    canvasHeight: svgElement?.offsetHeight,
 | 
				
			||||||
    drawWithImageSize: false,
 | 
					    drawWithImageSize: false,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
defineExpose({
 | 
					defineExpose({
 | 
				
			||||||
  scrollBottom() {
 | 
					  scrollBottom() {
 | 
				
			||||||
    mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight);
 | 
					    mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
 | 
					import { getRangePickerDefaultProps } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 列表的搜索表单 */
 | 
					/** 列表的搜索表单 */
 | 
				
			||||||
export function useGridFormSchema(): VbenFormSchema[] {
 | 
					export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -26,8 +27,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,13 +75,13 @@ const [Grid, gridApi] = useVbenVxeGrid({
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  } as VxeTableGridOptions<AiMindmapApi.MindMapVO>,
 | 
					  } as VxeTableGridOptions<AiMindmapApi.MindMapVO>,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const openPreview = async (row: AiMindmapApi.MindMapVO) => {
 | 
					async function openPreview(row: AiMindmapApi.MindMapVO) {
 | 
				
			||||||
  previewVisible.value = false;
 | 
					  previewVisible.value = false;
 | 
				
			||||||
  drawerApi.open();
 | 
					  drawerApi.open();
 | 
				
			||||||
  await nextTick();
 | 
					  await nextTick();
 | 
				
			||||||
  previewVisible.value = true;
 | 
					  previewVisible.value = true;
 | 
				
			||||||
  previewContent.value = row.generatedContent;
 | 
					  previewContent.value = row.generatedContent;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
  // 获得下拉数据
 | 
					  // 获得下拉数据
 | 
				
			||||||
  userList.value = await getSimpleUserList();
 | 
					  userList.value = await getSimpleUserList();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,9 +109,9 @@ onMounted(async () => {
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      </template>
 | 
					      </template>
 | 
				
			||||||
      <template #keyId="{ row }">
 | 
					      <template #keyId="{ row }">
 | 
				
			||||||
        <span>{{
 | 
					        <span>
 | 
				
			||||||
          apiKeyList.find((item) => item.id === row.keyId)?.name
 | 
					          {{ apiKeyList.find((item) => item.id === row.keyId)?.name }}
 | 
				
			||||||
        }}</span>
 | 
					        </span>
 | 
				
			||||||
      </template>
 | 
					      </template>
 | 
				
			||||||
      <template #actions="{ row }">
 | 
					      <template #actions="{ row }">
 | 
				
			||||||
        <TableAction
 | 
					        <TableAction
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,12 @@
 | 
				
			||||||
import type { VbenFormSchema } from '#/adapter/form';
 | 
					import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
 | 
					import {
 | 
				
			||||||
 | 
					  CommonStatusEnum,
 | 
				
			||||||
 | 
					  DICT_TYPE,
 | 
				
			||||||
 | 
					  getDictOptions,
 | 
				
			||||||
 | 
					  getRangePickerDefaultProps,
 | 
				
			||||||
 | 
					} from '#/utils';
 | 
				
			||||||
/** 新增/修改的表单 */
 | 
					/** 新增/修改的表单 */
 | 
				
			||||||
export function useFormSchema(): VbenFormSchema[] {
 | 
					export function useFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
  return [
 | 
					  return [
 | 
				
			||||||
| 
						 | 
					@ -64,8 +69,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import { Page } from '@vben/common-ui';
 | 
				
			||||||
import List from './list/index.vue';
 | 
					import List from './list/index.vue';
 | 
				
			||||||
import Mode from './mode/index.vue';
 | 
					import Mode from './mode/index.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Index' });
 | 
					defineOptions({ name: 'AiMusicIndex' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const listRef = ref<Nullable<{ generateMusic: (...args: any) => void }>>(null);
 | 
					const listRef = ref<Nullable<{ generateMusic: (...args: any) => void }>>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,12 +4,11 @@ import type { Nullable } from '@vben/types';
 | 
				
			||||||
import { inject, reactive, ref } from 'vue';
 | 
					import { inject, reactive, ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { IconifyIcon } from '@vben/icons';
 | 
					import { IconifyIcon } from '@vben/icons';
 | 
				
			||||||
 | 
					import { formatPast } from '@vben/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Image, Slider } from 'ant-design-vue';
 | 
					import { Image, Slider } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { formatPast } from '#/utils/formatTime';
 | 
					defineOptions({ name: 'AiMusicAudioBarIndex' });
 | 
				
			||||||
 | 
					 | 
				
			||||||
defineOptions({ name: 'Index' });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const currentSong = inject('currentSong', {});
 | 
					const currentSong = inject('currentSong', {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import audioBar from './audioBar/index.vue';
 | 
				
			||||||
import songCard from './songCard/index.vue';
 | 
					import songCard from './songCard/index.vue';
 | 
				
			||||||
import songInfo from './songInfo/index.vue';
 | 
					import songInfo from './songInfo/index.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Index' });
 | 
					defineOptions({ name: 'AiMusicListIndex' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const currentType = ref('mine');
 | 
					const currentType = ref('mine');
 | 
				
			||||||
// loading 状态
 | 
					// loading 状态
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import { IconifyIcon } from '@vben/icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Image } from 'ant-design-vue';
 | 
					import { Image } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Index' });
 | 
					defineOptions({ name: 'AiMusicSongCardIndex' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineProps({
 | 
					defineProps({
 | 
				
			||||||
  songInfo: {
 | 
					  songInfo: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import { inject } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Button, Card, Image } from 'ant-design-vue';
 | 
					import { Button, Card, Image } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Index' });
 | 
					defineOptions({ name: 'AiMusicSongInfoIndex' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const currentSong = inject('currentSong', {});
 | 
					const currentSong = inject('currentSong', {});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import { Select, Switch, Textarea } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Title from '../title/index.vue';
 | 
					import Title from '../title/index.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Desc' });
 | 
					defineOptions({ name: 'AiMusicModeDesc' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const formData = reactive({
 | 
					const formData = reactive({
 | 
				
			||||||
  desc: '',
 | 
					  desc: '',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import { Button, Card, Radio } from 'ant-design-vue';
 | 
				
			||||||
import desc from './desc.vue';
 | 
					import desc from './desc.vue';
 | 
				
			||||||
import lyric from './lyric.vue';
 | 
					import lyric from './lyric.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Index' });
 | 
					defineOptions({ name: 'AiMusicModeIndex' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emits = defineEmits(['generateMusic']);
 | 
					const emits = defineEmits(['generateMusic']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import { Button, Input, Select, Space, Tag, Textarea } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Title from '../title/index.vue';
 | 
					import Title from '../title/index.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'Lyric' });
 | 
					defineOptions({ name: 'AiMusicModeLyric' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tags = ['rock', 'punk', 'jazz', 'soul', 'country', 'kidsmusic', 'pop'];
 | 
					const tags = ['rock', 'punk', 'jazz', 'soul', 'country', 'kidsmusic', 'pop'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
defineOptions({ name: 'Index' });
 | 
					defineOptions({ name: 'AiMusicTitleIndex' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineProps({
 | 
					defineProps({
 | 
				
			||||||
  title: {
 | 
					  title: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
import { DICT_TYPE, getDictOptions } from '#/utils';
 | 
					import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 列表的搜索表单 */
 | 
					/** 列表的搜索表单 */
 | 
				
			||||||
export function useGridFormSchema(): VbenFormSchema[] {
 | 
					export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -45,8 +45,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@ import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 | 
				
			||||||
import { deleteMusic, getMusicPage, updateMusic } from '#/api/ai/music';
 | 
					import { deleteMusic, getMusicPage, updateMusic } from '#/api/ai/music';
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
import { $t } from '#/locales';
 | 
					import { $t } from '#/locales';
 | 
				
			||||||
import { AiMusicStatusEnum } from '#/utils/constants';
 | 
					import { AiMusicStatusEnum } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useGridColumns, useGridFormSchema } from './data';
 | 
					import { useGridColumns, useGridFormSchema } from './data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import type { VbenFormSchema } from '#/adapter/form';
 | 
					import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DICT_TYPE, getDictOptions } from '#/utils';
 | 
					import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 列表的搜索表单 */
 | 
					/** 列表的搜索表单 */
 | 
				
			||||||
export function useGridFormSchema(): VbenFormSchema[] {
 | 
					export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -30,8 +30,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,14 +28,14 @@ const basicInfoRef = ref<InstanceType<typeof BasicInfo>>();
 | 
				
			||||||
const workflowDesignRef = ref<InstanceType<typeof WorkflowDesign>>();
 | 
					const workflowDesignRef = ref<InstanceType<typeof WorkflowDesign>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 步骤校验函数 */
 | 
					/** 步骤校验函数 */
 | 
				
			||||||
const validateBasic = async () => {
 | 
					async function validateBasic() {
 | 
				
			||||||
  await basicInfoRef.value?.validate();
 | 
					  await basicInfoRef.value?.validate();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 工作流设计校验 */
 | 
					/** 工作流设计校验 */
 | 
				
			||||||
const validateWorkflow = async () => {
 | 
					async function validateWorkflow() {
 | 
				
			||||||
  await workflowDesignRef.value?.validate();
 | 
					  await workflowDesignRef.value?.validate();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const currentStep = ref(-1); // 步骤控制。-1 用于,一开始全部不展示等当前页面数据初始化完成
 | 
					const currentStep = ref(-1); // 步骤控制。-1 用于,一开始全部不展示等当前页面数据初始化完成
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,8 @@ provide('workflowData', workflowData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化数据 */
 | 
					/** 初始化数据 */
 | 
				
			||||||
const actionType = route.params.type as string;
 | 
					const actionType = route.params.type as string;
 | 
				
			||||||
const initData = async () => {
 | 
					
 | 
				
			||||||
 | 
					async function initData() {
 | 
				
			||||||
  if (actionType === 'update') {
 | 
					  if (actionType === 'update') {
 | 
				
			||||||
    const workflowId = route.params.id as string;
 | 
					    const workflowId = route.params.id as string;
 | 
				
			||||||
    formData.value = await getWorkflow(workflowId);
 | 
					    formData.value = await getWorkflow(workflowId);
 | 
				
			||||||
| 
						 | 
					@ -79,10 +80,10 @@ const initData = async () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 设置当前步骤
 | 
					  // 设置当前步骤
 | 
				
			||||||
  currentStep.value = 0;
 | 
					  currentStep.value = 0;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 校验所有步骤数据是否完整 */
 | 
					/** 校验所有步骤数据是否完整 */
 | 
				
			||||||
const validateAllSteps = async () => {
 | 
					async function validateAllSteps() {
 | 
				
			||||||
  // 基本信息校验
 | 
					  // 基本信息校验
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    await validateBasic();
 | 
					    await validateBasic();
 | 
				
			||||||
| 
						 | 
					@ -99,10 +100,10 @@ const validateAllSteps = async () => {
 | 
				
			||||||
    throw new Error('请完善工作流信息');
 | 
					    throw new Error('请完善工作流信息');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 保存操作 */
 | 
					/** 保存操作 */
 | 
				
			||||||
const handleSave = async () => {
 | 
					async function handleSave() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 保存前校验所有步骤的数据
 | 
					    // 保存前校验所有步骤的数据
 | 
				
			||||||
    await validateAllSteps();
 | 
					    await validateAllSteps();
 | 
				
			||||||
| 
						 | 
					@ -124,10 +125,10 @@ const handleSave = async () => {
 | 
				
			||||||
    console.error('保存失败:', error);
 | 
					    console.error('保存失败:', error);
 | 
				
			||||||
    message.warning(error.message || '请完善所有步骤的必填信息');
 | 
					    message.warning(error.message || '请完善所有步骤的必填信息');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 发布操作 */
 | 
					/** 发布操作 */
 | 
				
			||||||
const handleDeploy = async () => {
 | 
					async function handleDeploy() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 修改场景下直接发布,新增场景下需要先确认
 | 
					    // 修改场景下直接发布,新增场景下需要先确认
 | 
				
			||||||
    if (!formData.value.id) {
 | 
					    if (!formData.value.id) {
 | 
				
			||||||
| 
						 | 
					@ -158,10 +159,10 @@ const handleDeploy = async () => {
 | 
				
			||||||
    console.error('发布失败:', error);
 | 
					    console.error('发布失败:', error);
 | 
				
			||||||
    message.warning(error.message || '发布失败');
 | 
					    message.warning(error.message || '发布失败');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 步骤切换处理 */
 | 
					/** 步骤切换处理 */
 | 
				
			||||||
const handleStepClick = async (index: number) => {
 | 
					async function handleStepClick(index: number) {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    if (index !== 0) {
 | 
					    if (index !== 0) {
 | 
				
			||||||
      await validateBasic();
 | 
					      await validateBasic();
 | 
				
			||||||
| 
						 | 
					@ -176,17 +177,17 @@ const handleStepClick = async (index: number) => {
 | 
				
			||||||
    console.error('步骤切换失败:', error);
 | 
					    console.error('步骤切换失败:', error);
 | 
				
			||||||
    message.warning('请先完善当前步骤必填信息');
 | 
					    message.warning('请先完善当前步骤必填信息');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tabs = useTabs();
 | 
					const tabs = useTabs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 返回列表页 */
 | 
					/** 返回列表页 */
 | 
				
			||||||
const handleBack = () => {
 | 
					function handleBack() {
 | 
				
			||||||
  // 关闭当前页签
 | 
					  // 关闭当前页签
 | 
				
			||||||
  tabs.closeCurrentTab();
 | 
					  tabs.closeCurrentTab();
 | 
				
			||||||
  // 跳转到列表页,使用路径, 目前后端的路由 name: 'name'+ menuId
 | 
					  // 跳转到列表页,使用路径, 目前后端的路由 name: 'name'+ menuId
 | 
				
			||||||
  router.push({ path: '/ai/workflow' });
 | 
					  router.push({ path: '/ai/workflow' });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,9 +18,9 @@ const rules: Record<string, Rule[]> = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 表单校验 */
 | 
					/** 表单校验 */
 | 
				
			||||||
const validate = async () => {
 | 
					async function validate() {
 | 
				
			||||||
  await formRef.value?.validate();
 | 
					  await formRef.value?.validate();
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ validate });
 | 
					defineExpose({ validate });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,11 +4,12 @@ import type { Ref } from 'vue';
 | 
				
			||||||
import { inject, ref } from 'vue';
 | 
					import { inject, ref } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useVbenDrawer } from '@vben/common-ui';
 | 
					import { useVbenDrawer } from '@vben/common-ui';
 | 
				
			||||||
 | 
					import { isNumber } from '@vben/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineProps<{
 | 
					defineProps<{
 | 
				
			||||||
  provider: any;
 | 
					  provider: any;
 | 
				
			||||||
| 
						 | 
					@ -28,26 +29,26 @@ const [Drawer, drawerApi] = useVbenDrawer({
 | 
				
			||||||
  modal: false,
 | 
					  modal: false,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
/** 展示工作流测试抽屉 */
 | 
					/** 展示工作流测试抽屉 */
 | 
				
			||||||
const testWorkflowModel = () => {
 | 
					function testWorkflowModel() {
 | 
				
			||||||
  drawerApi.open();
 | 
					  drawerApi.open();
 | 
				
			||||||
  const startNode = getStartNode();
 | 
					  const startNode = getStartNode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 获取参数定义
 | 
					  // 获取参数定义
 | 
				
			||||||
  const parameters = startNode.data?.parameters || [];
 | 
					  const parameters = startNode.data?.parameters || [];
 | 
				
			||||||
  const paramDefinitions = {};
 | 
					  const paramDefinitions: Record<string, any> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 加入参数选项方便用户添加非必须参数
 | 
					  // 加入参数选项方便用户添加非必须参数
 | 
				
			||||||
  parameters.forEach((param) => {
 | 
					  parameters.forEach((param: any) => {
 | 
				
			||||||
    paramDefinitions[param.name] = param;
 | 
					    paramDefinitions[param.name] = param;
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function mergeIfRequiredButNotSet(target) {
 | 
					  function mergeIfRequiredButNotSet(target: any) {
 | 
				
			||||||
    const needPushList = [];
 | 
					    const needPushList = [];
 | 
				
			||||||
    for (const key in paramDefinitions) {
 | 
					    for (const key in paramDefinitions) {
 | 
				
			||||||
      const param = paramDefinitions[key];
 | 
					      const param = paramDefinitions[key];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (param.required) {
 | 
					      if (param.required) {
 | 
				
			||||||
        const item = target.find((item) => item.key === key);
 | 
					        const item = target.find((item: any) => item.key === key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!item) {
 | 
					        if (!item) {
 | 
				
			||||||
          needPushList.push({
 | 
					          needPushList.push({
 | 
				
			||||||
| 
						 | 
					@ -63,10 +64,10 @@ const testWorkflowModel = () => {
 | 
				
			||||||
  mergeIfRequiredButNotSet(params4Test.value);
 | 
					  mergeIfRequiredButNotSet(params4Test.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  paramsOfStartNode.value = paramDefinitions;
 | 
					  paramsOfStartNode.value = paramDefinitions;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 运行流程 */
 | 
					/** 运行流程 */
 | 
				
			||||||
const goRun = async () => {
 | 
					async function goRun() {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const val = tinyflowRef.value.getData();
 | 
					    const val = tinyflowRef.value.getData();
 | 
				
			||||||
    loading.value = true;
 | 
					    loading.value = true;
 | 
				
			||||||
| 
						 | 
					@ -77,13 +78,13 @@ const goRun = async () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 获取参数定义
 | 
					    // 获取参数定义
 | 
				
			||||||
    const parameters = startNode.data?.parameters || [];
 | 
					    const parameters = startNode.data?.parameters || [];
 | 
				
			||||||
    const paramDefinitions = {};
 | 
					    const paramDefinitions: Record<string, any> = {};
 | 
				
			||||||
    parameters.forEach((param) => {
 | 
					    parameters.forEach((param: any) => {
 | 
				
			||||||
      paramDefinitions[param.name] = param.dataType;
 | 
					      paramDefinitions[param.name] = param.dataType;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 参数类型转换
 | 
					    // 参数类型转换
 | 
				
			||||||
    const convertedParams = {};
 | 
					    const convertedParams: Record<string, any> = {};
 | 
				
			||||||
    for (const { key, value } of params4Test.value) {
 | 
					    for (const { key, value } of params4Test.value) {
 | 
				
			||||||
      const paramKey = key.trim();
 | 
					      const paramKey = key.trim();
 | 
				
			||||||
      if (!paramKey) continue;
 | 
					      if (!paramKey) continue;
 | 
				
			||||||
| 
						 | 
					@ -95,8 +96,8 @@ const goRun = async () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        convertedParams[paramKey] = convertParamValue(value, dataType);
 | 
					        convertedParams[paramKey] = convertParamValue(value, dataType);
 | 
				
			||||||
      } catch (error_) {
 | 
					      } catch (error: any) {
 | 
				
			||||||
        throw new Error(`参数 ${paramKey} 转换失败: ${error_.message}`);
 | 
					        throw new Error(`参数 ${paramKey} 转换失败: ${error.message}`);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,42 +108,42 @@ const goRun = async () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const response = await testWorkflow(data);
 | 
					    const response = await testWorkflow(data);
 | 
				
			||||||
    testResult.value = response;
 | 
					    testResult.value = response;
 | 
				
			||||||
  } catch (error_) {
 | 
					  } catch (error: any) {
 | 
				
			||||||
    error.value =
 | 
					    error.value =
 | 
				
			||||||
      error_.response?.data?.message || '运行失败,请检查参数和网络连接';
 | 
					      error.response?.data?.message || '运行失败,请检查参数和网络连接';
 | 
				
			||||||
  } finally {
 | 
					  } finally {
 | 
				
			||||||
    loading.value = false;
 | 
					    loading.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取开始节点 */
 | 
					/** 获取开始节点 */
 | 
				
			||||||
const getStartNode = () => {
 | 
					function getStartNode() {
 | 
				
			||||||
  const val = tinyflowRef.value.getData();
 | 
					  const val = tinyflowRef.value.getData();
 | 
				
			||||||
  const startNode = val.nodes.find((node) => node.type === 'startNode');
 | 
					  const startNode = val.nodes.find((node: any) => node.type === 'startNode');
 | 
				
			||||||
  if (!startNode) {
 | 
					  if (!startNode) {
 | 
				
			||||||
    throw new Error('流程缺少开始节点');
 | 
					    throw new Error('流程缺少开始节点');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return startNode;
 | 
					  return startNode;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 添加参数项 */
 | 
					/** 添加参数项 */
 | 
				
			||||||
const addParam = () => {
 | 
					function addParam() {
 | 
				
			||||||
  params4Test.value.push({ key: '', value: '' });
 | 
					  params4Test.value.push({ key: '', value: '' });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 删除参数项 */
 | 
					/** 删除参数项 */
 | 
				
			||||||
const removeParam = (index) => {
 | 
					function removeParam(index: number) {
 | 
				
			||||||
  params4Test.value.splice(index, 1);
 | 
					  params4Test.value.splice(index, 1);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 类型转换函数 */
 | 
					/** 类型转换函数 */
 | 
				
			||||||
const convertParamValue = (value, dataType) => {
 | 
					function convertParamValue(value: string, dataType: string) {
 | 
				
			||||||
  if (value === '') return null; // 空值处理
 | 
					  if (value === '') return null; // 空值处理
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  switch (dataType) {
 | 
					  switch (dataType) {
 | 
				
			||||||
    case 'Number': {
 | 
					    case 'Number': {
 | 
				
			||||||
      const num = Number(value);
 | 
					      const num = Number(value);
 | 
				
			||||||
      if (isNaN(num)) throw new Error('非数字格式');
 | 
					      if (!isNumber(num)) throw new Error('非数字格式');
 | 
				
			||||||
      return num;
 | 
					      return num;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case 'String': {
 | 
					    case 'String': {
 | 
				
			||||||
| 
						 | 
					@ -157,24 +158,24 @@ const convertParamValue = (value, dataType) => {
 | 
				
			||||||
    case 'Object': {
 | 
					    case 'Object': {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        return JSON.parse(value);
 | 
					        return JSON.parse(value);
 | 
				
			||||||
      } catch (error_) {
 | 
					      } catch (error: any) {
 | 
				
			||||||
        throw new Error(`JSON格式错误: ${error_.message}`);
 | 
					        throw new Error(`JSON格式错误: ${error.message}`);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    default: {
 | 
					    default: {
 | 
				
			||||||
      throw new Error(`不支持的类型: ${dataType}`);
 | 
					      throw new Error(`不支持的类型: ${dataType}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
/** 表单校验 */
 | 
					/** 表单校验 */
 | 
				
			||||||
const validate = async () => {
 | 
					async function validate() {
 | 
				
			||||||
  // 获取最新的流程数据
 | 
					  // 获取最新的流程数据
 | 
				
			||||||
  if (!workflowData.value) {
 | 
					  if (!workflowData.value) {
 | 
				
			||||||
    throw new Error('请设计流程');
 | 
					    throw new Error('请设计流程');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  workflowData.value = tinyflowRef.value.getData();
 | 
					  workflowData.value = tinyflowRef.value.getData();
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ validate });
 | 
					defineExpose({ validate });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,12 @@ import { ref } from 'vue';
 | 
				
			||||||
import { createReusableTemplate } from '@vueuse/core';
 | 
					import { createReusableTemplate } from '@vueuse/core';
 | 
				
			||||||
import { Button, message, Textarea } from 'ant-design-vue';
 | 
					import { Button, message, Textarea } from 'ant-design-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DICT_TYPE, getIntDictOptions } from '#/utils';
 | 
					import {
 | 
				
			||||||
import { AiWriteTypeEnum, WriteExample } from '#/utils/constants';
 | 
					  AiWriteTypeEnum,
 | 
				
			||||||
 | 
					  DICT_TYPE,
 | 
				
			||||||
 | 
					  getIntDictOptions,
 | 
				
			||||||
 | 
					  WriteExample,
 | 
				
			||||||
 | 
					} from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Tag from './Tag.vue';
 | 
					import Tag from './Tag.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,19 +37,19 @@ function omit(obj: Record<string, any>, keysToOmit: string[]) {
 | 
				
			||||||
  return result;
 | 
					  return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/** 点击示例的时候,将定义好的文章作为示例展示出来 */
 | 
					/** 点击示例的时候,将定义好的文章作为示例展示出来 */
 | 
				
			||||||
const example = (type: 'reply' | 'write') => {
 | 
					function example(type: 'reply' | 'write') {
 | 
				
			||||||
  formData.value = {
 | 
					  formData.value = {
 | 
				
			||||||
    ...initData,
 | 
					    ...initData,
 | 
				
			||||||
    ...omit(WriteExample[type], ['data']),
 | 
					    ...omit(WriteExample[type], ['data']),
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  emit('example', type);
 | 
					  emit('example', type);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 重置,将表单值作为初选值 */
 | 
					/** 重置,将表单值作为初选值 */
 | 
				
			||||||
const reset = () => {
 | 
					function reset() {
 | 
				
			||||||
  formData.value = { ...initData };
 | 
					  formData.value = { ...initData };
 | 
				
			||||||
  emit('reset');
 | 
					  emit('reset');
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const selectedTab = ref<TabType>(AiWriteTypeEnum.WRITING);
 | 
					const selectedTab = ref<TabType>(AiWriteTypeEnum.WRITING);
 | 
				
			||||||
const tabs: {
 | 
					const tabs: {
 | 
				
			||||||
| 
						 | 
					@ -83,7 +87,7 @@ const formData = ref<AiWriteApi.WriteVO>({ ...initData });
 | 
				
			||||||
/** 用来记录切换之前所填写的数据,切换的时候给赋值回来 */
 | 
					/** 用来记录切换之前所填写的数据,切换的时候给赋值回来 */
 | 
				
			||||||
const recordFormData = {} as Record<AiWriteTypeEnum, AiWriteApi.WriteVO>;
 | 
					const recordFormData = {} as Record<AiWriteTypeEnum, AiWriteApi.WriteVO>;
 | 
				
			||||||
/** 切换tab */
 | 
					/** 切换tab */
 | 
				
			||||||
const switchTab = (value: TabType) => {
 | 
					function switchTab(value: TabType) {
 | 
				
			||||||
  if (value !== selectedTab.value) {
 | 
					  if (value !== selectedTab.value) {
 | 
				
			||||||
    // 保存之前的久数据
 | 
					    // 保存之前的久数据
 | 
				
			||||||
    recordFormData[selectedTab.value] = formData.value;
 | 
					    recordFormData[selectedTab.value] = formData.value;
 | 
				
			||||||
| 
						 | 
					@ -91,10 +95,10 @@ const switchTab = (value: TabType) => {
 | 
				
			||||||
    // 将之前的旧数据赋值回来
 | 
					    // 将之前的旧数据赋值回来
 | 
				
			||||||
    formData.value = { ...initData, ...recordFormData[value] };
 | 
					    formData.value = { ...initData, ...recordFormData[value] };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 提交写作 */
 | 
					/** 提交写作 */
 | 
				
			||||||
const submit = () => {
 | 
					function submit() {
 | 
				
			||||||
  if (selectedTab.value === 2 && !formData.value.originalContent) {
 | 
					  if (selectedTab.value === 2 && !formData.value.originalContent) {
 | 
				
			||||||
    message.warning('请输入原文');
 | 
					    message.warning('请输入原文');
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
| 
						 | 
					@ -111,7 +115,7 @@ const submit = () => {
 | 
				
			||||||
    /** 使用选中 tab 值覆盖当前的 type 类型 */
 | 
					    /** 使用选中 tab 值覆盖当前的 type 类型 */
 | 
				
			||||||
    type: selectedTab.value,
 | 
					    type: selectedTab.value,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,9 +40,9 @@ defineExpose({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 点击复制的时候复制内容 */
 | 
					/** 点击复制的时候复制内容 */
 | 
				
			||||||
const showCopy = computed(() => props.content && !props.isWriting); // 是否展示复制按钮,在生成内容完成的时候展示
 | 
					const showCopy = computed(() => props.content && !props.isWriting); // 是否展示复制按钮,在生成内容完成的时候展示
 | 
				
			||||||
const copyContent = () => {
 | 
					function copyContent() {
 | 
				
			||||||
  copy(props.content);
 | 
					  copy(props.content);
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 复制成功的时候 copied.value 为 true */
 | 
					/** 复制成功的时候 copied.value 为 true */
 | 
				
			||||||
watch(copied, (val) => {
 | 
					watch(copied, (val) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import { nextTick, ref } from 'vue';
 | 
				
			||||||
import { alert, Page } from '@vben/common-ui';
 | 
					import { alert, Page } from '@vben/common-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { writeStream } from '#/api/ai/write';
 | 
					import { writeStream } from '#/api/ai/write';
 | 
				
			||||||
import { WriteExample } from '#/utils/constants';
 | 
					import { WriteExample } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Left from './components/Left.vue';
 | 
					import Left from './components/Left.vue';
 | 
				
			||||||
import Right from './components/Right.vue';
 | 
					import Right from './components/Right.vue';
 | 
				
			||||||
| 
						 | 
					@ -16,15 +16,15 @@ const isWriting = ref(false); // 是否正在写作中
 | 
				
			||||||
const abortController = ref<AbortController>(); // // 写作进行中 abort 控制器(控制 stream 写作)
 | 
					const abortController = ref<AbortController>(); // // 写作进行中 abort 控制器(控制 stream 写作)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 停止 stream 生成 */
 | 
					/** 停止 stream 生成 */
 | 
				
			||||||
const stopStream = () => {
 | 
					function stopStream() {
 | 
				
			||||||
  abortController.value?.abort();
 | 
					  abortController.value?.abort();
 | 
				
			||||||
  isWriting.value = false;
 | 
					  isWriting.value = false;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 执行写作 */
 | 
					/** 执行写作 */
 | 
				
			||||||
const rightRef = ref<InstanceType<typeof Right>>();
 | 
					const rightRef = ref<InstanceType<typeof Right>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const submit = (data: Partial<AiWriteApi.WriteVO>) => {
 | 
					function submit(data: Partial<AiWriteApi.WriteVO>) {
 | 
				
			||||||
  abortController.value = new AbortController();
 | 
					  abortController.value = new AbortController();
 | 
				
			||||||
  writeResult.value = '';
 | 
					  writeResult.value = '';
 | 
				
			||||||
  isWriting.value = true;
 | 
					  isWriting.value = true;
 | 
				
			||||||
| 
						 | 
					@ -51,17 +51,17 @@ const submit = (data: Partial<AiWriteApi.WriteVO>) => {
 | 
				
			||||||
      throw error;
 | 
					      throw error;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 点击示例触发 */
 | 
					/** 点击示例触发 */
 | 
				
			||||||
const handleExampleClick = (type: keyof typeof WriteExample) => {
 | 
					function handleExampleClick(type: keyof typeof WriteExample) {
 | 
				
			||||||
  writeResult.value = WriteExample[type].data;
 | 
					  writeResult.value = WriteExample[type].data;
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 点击重置的时候清空写作的结果*/
 | 
					/** 点击重置的时候清空写作的结果*/
 | 
				
			||||||
const reset = () => {
 | 
					function reset() {
 | 
				
			||||||
  writeResult.value = '';
 | 
					  writeResult.value = '';
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
import { DICT_TYPE, getDictOptions } from '#/utils';
 | 
					import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 列表的搜索表单 */
 | 
					/** 列表的搜索表单 */
 | 
				
			||||||
export function useGridFormSchema(): VbenFormSchema[] {
 | 
					export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -40,8 +40,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,12 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { z } from '#/adapter/form';
 | 
					import { z } from '#/adapter/form';
 | 
				
			||||||
import { getSimpleUserList } from '#/api/system/user';
 | 
					import { getSimpleUserList } from '#/api/system/user';
 | 
				
			||||||
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
 | 
					import {
 | 
				
			||||||
 | 
					  CommonStatusEnum,
 | 
				
			||||||
 | 
					  DICT_TYPE,
 | 
				
			||||||
 | 
					  getDictOptions,
 | 
				
			||||||
 | 
					  getRangePickerDefaultProps,
 | 
				
			||||||
 | 
					} from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 新增/修改的表单 */
 | 
					/** 新增/修改的表单 */
 | 
				
			||||||
export function useFormSchema(): VbenFormSchema[] {
 | 
					export function useFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -87,7 +92,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,12 @@ import type { VbenFormSchema } from '#/adapter/form';
 | 
				
			||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
					import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { z } from '#/adapter/form';
 | 
					import { z } from '#/adapter/form';
 | 
				
			||||||
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
 | 
					import {
 | 
				
			||||||
 | 
					  CommonStatusEnum,
 | 
				
			||||||
 | 
					  DICT_TYPE,
 | 
				
			||||||
 | 
					  getDictOptions,
 | 
				
			||||||
 | 
					  getRangePickerDefaultProps,
 | 
				
			||||||
 | 
					} from '#/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 新增/修改的表单 */
 | 
					/** 新增/修改的表单 */
 | 
				
			||||||
export function useFormSchema(): VbenFormSchema[] {
 | 
					export function useFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
| 
						 | 
					@ -74,8 +79,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      label: '创建时间',
 | 
					      label: '创建时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        placeholder: ['开始时间', '结束时间'],
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        valueFormat: 'YYYY-MM-DD HH:mm:ss',
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,6 +71,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,6 +77,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,6 @@ export function useGridFormSchema(
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        placeholder: ['开始日期', '结束日期'],
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -65,7 +64,6 @@ export function useGridFormSchema(
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
        placeholder: ['开始日期', '结束日期'],
 | 
					 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,13 +50,13 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
        allowClear: true,
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    // 发起时间
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      fieldName: 'createTime',
 | 
					      fieldName: 'createTime',
 | 
				
			||||||
      label: '发起时间',
 | 
					      label: '发起时间',
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,6 +56,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
 | 
				
			||||||
      component: 'RangePicker',
 | 
					      component: 'RangePicker',
 | 
				
			||||||
      componentProps: {
 | 
					      componentProps: {
 | 
				
			||||||
        ...getRangePickerDefaultProps(),
 | 
					        ...getRangePickerDefaultProps(),
 | 
				
			||||||
 | 
					        allowClear: true,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue