pull/146/head^2
xingyu4j 2025-06-16 16:59:04 +08:00
parent d09b993bc8
commit 014785a1ad
71 changed files with 447 additions and 423 deletions

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import type { Item } from './ui/typeing';
import type { Item } from './ui/typing';
import { onMounted, onUnmounted, ref } from 'vue';

View File

@ -0,0 +1,2 @@
export { default as Tinyflow } from './tinyflow.vue';
export * from './ui/typing';

View File

@ -1,21 +1,21 @@
export declare type Item = {
export interface Item {
children?: Item[];
label: string;
value: number | string;
};
}
export type Position = {
export interface Position {
x: number;
y: number;
};
}
export type Viewport = {
export interface Viewport {
x: number;
y: number;
zoom: number;
};
}
export type Node = {
export interface Node {
data?: Record<string, any>;
draggable?: boolean;
height?: number;
@ -24,23 +24,23 @@ export type Node = {
selected?: boolean;
type?: string;
width?: number;
};
}
export type Edge = {
export interface Edge {
animated?: boolean;
id: string;
label?: string;
source: string;
target: string;
type?: string;
};
}
export type TinyflowData = Partial<{
edges: Edge[];
nodes: Node[];
viewport: Viewport;
}>;
export declare type TinyflowOptions = {
export interface TinyflowOptions {
data?: TinyflowData;
element: Element | string;
provider?: {
@ -48,7 +48,7 @@ export declare type TinyflowOptions = {
knowledge?: () => Item[] | Promise<Item[]>;
llm?: () => Item[] | Promise<Item[]>;
};
};
}
export declare class Tinyflow {
private _init;
@ -66,5 +66,3 @@ export declare class Tinyflow {
getOptions(): TinyflowOptions;
setData(data: TinyflowData): void;
}
export {};

View File

@ -0,0 +1,3 @@
export { default as MarkdownView } from './markdown-view.vue';
export * from './typing';

View File

@ -1,4 +1,6 @@
<script setup lang="ts">
import type { MarkdownViewProps } from './typing';
import { computed, onMounted, ref } from 'vue';
import { MarkdownIt } from '@vben/plugins/markmap';
@ -10,15 +12,10 @@ import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.min.css';
//
const props = defineProps({
content: {
type: String,
required: true,
},
});
const props = defineProps<MarkdownViewProps>();
const { copy } = useClipboard(); // copy
const contentRef = ref();
const contentRef = ref<HTMLElement | null>(null);
const md = new MarkdownIt({
highlight(str, lang) {
@ -40,7 +37,7 @@ const renderedMarkdown = computed(() => {
/** 初始化 */
onMounted(async () => {
// copy
contentRef.value.addEventListener('click', (e: any) => {
contentRef.value?.addEventListener('click', (e: any) => {
if (e.target.id === 'copy') {
copy(e.target?.dataset?.copy);
message.success('复制成功!');

View File

@ -0,0 +1,3 @@
export type MarkdownViewProps = {
content: string;
};

View File

@ -28,7 +28,6 @@ const props = defineProps({
default: null,
},
});
/** 新建对话 */
//
const emits = defineEmits([
@ -37,9 +36,11 @@ const emits = defineEmits([
'onConversationClear',
'onConversationDelete',
]);
const [Drawer, drawerApi] = useVbenDrawer({
connectedComponent: RoleRepository,
});
//
const searchName = ref<string>(''); //
const activeConversationId = ref<null | number>(null); // null
@ -50,7 +51,7 @@ const loading = ref<boolean>(false); // 加载中
const loadingTime = ref<any>();
/** 搜索对话 */
const searchConversation = async () => {
async function searchConversation() {
//
if (searchName.value.trim().length === 0) {
conversationMap.value = await getConversationGroupByCreateTime(
@ -64,25 +65,25 @@ const searchConversation = async () => {
conversationMap.value =
await getConversationGroupByCreateTime(filterValues);
}
};
}
/** 点击对话 */
const handleConversationClick = async (id: number) => {
async function handleConversationClick(id: number) {
//
const filterConversation = conversationList.value.find((item) => {
return item.id === id;
});
// onConversationClick
// noinspection JSVoidFunctionReturnValueUsed
const success = emits('onConversationClick', filterConversation);
const success = emits('onConversationClick', filterConversation) as any;
//
if (success) {
activeConversationId.value = id;
}
};
}
/** 获取对话列表 */
const getChatConversationList = async () => {
async function getChatConversationList() {
try {
//
loadingTime.value = setTimeout(() => {
@ -114,12 +115,12 @@ const getChatConversationList = async () => {
//
loading.value = false;
}
};
}
/** 按照 creteTime 创建时间,进行分组 */
const getConversationGroupByCreateTime = async (
async function getConversationGroupByCreateTime(
list: AiChatConversationApi.ChatConversationVO[],
) => {
) {
// (30)
// noinspection NonAsciiCharacters
const groupMap: any = {
@ -159,8 +160,9 @@ const getConversationGroupByCreateTime = async (
}
}
return groupMap;
};
const createConversation = async () => {
}
async function createConversation() {
// 1.
const conversationId = await createChatConversationMy(
{} as unknown as AiChatConversationApi.ChatConversationVO,
@ -171,12 +173,12 @@ const createConversation = async () => {
await handleConversationClick(conversationId);
// 4.
emits('onConversationCreate');
};
}
/** 修改对话的标题 */
const updateConversationTitle = async (
async function updateConversationTitle(
conversation: AiChatConversationApi.ChatConversationVO,
) => {
) {
// 1.
prompt({
async beforeClose(scope) {
@ -225,12 +227,12 @@ const updateConversationTitle = async (
title: '修改标题',
modelPropName: 'value',
});
};
}
/** 删除聊天对话 */
const deleteChatConversation = async (
async function deleteChatConversation(
conversation: AiChatConversationApi.ChatConversationVO,
) => {
) {
try {
//
await confirm(`是否确认删除对话 - ${conversation.title}?`);
@ -242,8 +244,9 @@ const deleteChatConversation = async (
//
emits('onConversationDelete', conversation);
} catch {}
};
const handleClearConversation = async () => {
}
async function handleClearConversation() {
try {
await confirm('确认后对话会全部清空,置顶的对话除外。');
await deleteChatConversationMyByUnpinned();
@ -255,18 +258,18 @@ const handleClearConversation = async () => {
//
emits('onConversationClear');
} catch {}
};
}
/** 对话置顶 */
const handleTop = async (
async function handleTop(
conversation: AiChatConversationApi.ChatConversationVO,
) => {
) {
//
conversation.pinned = !conversation.pinned;
await updateChatConversationMy(conversation);
//
await getChatConversationList();
};
}
// ============ ============

View File

@ -47,10 +47,10 @@ const documentList = computed(() => {
});
/** 点击 document 处理 */
const handleClick = (doc: any) => {
function handleClick(doc: any) {
document.value = doc;
dialogVisible.value = true;
};
}
</script>
<template>

View File

@ -14,7 +14,7 @@ import { useClipboard } from '@vueuse/core';
import { Avatar, Button, message } from 'ant-design-vue';
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';
// props
@ -67,44 +67,44 @@ function handleScroll() {
}
/** 回到底部 */
const handleGoBottom = async () => {
async function handleGoBottom() {
const scrollContainer = messageContainer.value;
scrollContainer.scrollTop = scrollContainer.scrollHeight;
};
}
/** 回到顶部 */
const handlerGoTop = async () => {
async function handlerGoTop() {
const scrollContainer = messageContainer.value;
scrollContainer.scrollTop = 0;
};
}
defineExpose({ scrollToBottom, handlerGoTop }); // parent
// ============ ==============
/** 复制 */
const copyContent = async (content: string) => {
async function copyContent(content: string) {
await copy(content);
message.success('复制成功!');
};
}
/** 删除 */
const onDelete = async (id: number) => {
async function onDelete(id: number) {
// message
await deleteChatMessage(id);
message.success('删除成功!');
//
emits('onDeleteSuccess');
};
}
/** 刷新 */
const onRefresh = async (message: AiChatMessageApi.ChatMessageVO) => {
async function onRefresh(message: AiChatMessageApi.ChatMessageVO) {
emits('onRefresh', message);
};
}
/** 编辑 */
const onEdit = async (message: AiChatMessageApi.ChatMessageVO) => {
async function onEdit(message: AiChatMessageApi.ChatMessageVO) {
emits('onEdit', message);
};
}
/** 初始化 */
onMounted(async () => {

View File

@ -11,9 +11,9 @@ const promptList = [
prompt: '写一首好听的诗歌?',
},
]; /** 选中 prompt 点击 */
const handlerPromptClick = async (prompt: any) => {
async function handlerPromptClick(prompt: any) {
emits('onPrompt', prompt.prompt);
};
}
</script>
<template>
<div class="relative flex h-full w-full flex-row justify-center">

View File

@ -4,9 +4,9 @@ import { Button } from 'ant-design-vue';
const emits = defineEmits(['onNewConversation']);
/** 新建 conversation 聊天对话 */
const handlerNewChat = () => {
function handlerNewChat() {
emits('onNewConversation');
};
}
</script>
<template>

View File

@ -19,9 +19,9 @@ defineProps({
const emits = defineEmits(['onCategoryClick']);
/** 处理分类点击事件 */
const handleCategoryClick = async (category: string) => {
async function handleCategoryClick(category: string) {
emits('onCategoryClick', category);
};
}
</script>
<template>

View File

@ -33,7 +33,7 @@ const emits = defineEmits(['onDelete', 'onEdit', 'onUse', 'onPage']);
const tabsRef = ref<any>();
/** 操作:编辑、删除 */
const handleMoreClick = async (data: any) => {
async function handleMoreClick(data: any) {
const type = data[0];
const role = data[1];
if (type === 'delete') {
@ -41,22 +41,22 @@ const handleMoreClick = async (data: any) => {
} else {
emits('onEdit', role);
}
};
}
/** 选中 */
const handleUseClick = (role: any) => {
function handleUseClick(role: any) {
emits('onUse', role);
};
}
/** 滚动 */
const handleTabsScroll = async () => {
async function handleTabsScroll() {
if (tabsRef.value) {
const { scrollTop, scrollHeight, clientHeight } = tabsRef.value;
if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) {
await emits('onPage');
}
}
};
}
</script>
<template>

View File

@ -46,15 +46,15 @@ const activeCategory = ref<string>('全部'); // 选择中的分类
const categoryList = ref<string[]>([]); //
/** tabs 点击 */
const handleTabsClick = async (tab: any) => {
async function handleTabsClick(tab: any) {
//
activeTab.value = tab;
//
await getActiveTabsRole();
};
}
/** 获取 my role 我的角色 */
const getMyRole = async (append?: boolean) => {
async function getMyRole(append?: boolean) {
const params: AiModelChatRoleApi.ChatRolePageReqVO = {
...myRoleParams,
name: search.value,
@ -66,10 +66,10 @@ const getMyRole = async (append?: boolean) => {
} else {
myRoleList.value = list;
}
};
}
/** 获取 public role 公共角色 */
const getPublicRole = async (append?: boolean) => {
async function getPublicRole(append?: boolean) {
const params: AiModelChatRoleApi.ChatRolePageReqVO = {
...publicRoleParams,
category: activeCategory.value === '全部' ? '' : activeCategory.value,
@ -82,10 +82,10 @@ const getPublicRole = async (append?: boolean) => {
} else {
publicRoleList.value = list;
}
};
}
/** 获取选中的 tabs 角色 */
const getActiveTabsRole = async () => {
async function getActiveTabsRole() {
if (activeTab.value === 'my-role') {
myRoleParams.pageNo = 1;
await getMyRole();
@ -93,43 +93,44 @@ const getActiveTabsRole = async () => {
publicRoleParams.pageNo = 1;
await getPublicRole();
}
};
}
/** 获取角色分类列表 */
const getRoleCategoryList = async () => {
async function getRoleCategoryList() {
categoryList.value = ['全部', ...(await getCategoryList())];
};
}
/** 处理分类点击 */
const handlerCategoryClick = async (category: string) => {
async function handlerCategoryClick(category: string) {
//
activeCategory.value = category;
//
await getActiveTabsRole();
};
}
const handlerAddRole = async () => {
async function handlerAddRole() {
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();
};
}
/** 添加角色成功 */
const handlerAddRoleSuccess = async () => {
async function handlerAddRoleSuccess() {
//
await getActiveTabsRole();
};
}
/** 删除角色 */
const handlerCardDelete = async (role: any) => {
async function handlerCardDelete(role: any) {
await deleteMy(role.id);
//
await getActiveTabsRole();
};
}
/** 角色分页:获取下一页 */
const handlerCardPage = async (type: string) => {
async function handlerCardPage(type: string) {
try {
loading.value = true;
if (type === 'public') {
@ -142,10 +143,10 @@ const handlerCardPage = async (type: string) => {
} finally {
loading.value = false;
}
};
}
/** 选择 card 角色:新建聊天对话 */
const handlerCardUse = async (role: any) => {
async function handlerCardUse(role: any) {
// 1.
const data: AiChatConversationApi.ChatConversationVO = {
roleId: role.id,
@ -159,7 +160,8 @@ const handlerCardUse = async (role: any) => {
conversationId,
},
});
};
}
/** 初始化 */
onMounted(async () => {
//

View File

@ -61,7 +61,7 @@ const receiveMessageDisplayedText = ref('');
// =========== ===========
/** 获取对话信息 */
const getConversation = async (id: null | number) => {
async function getConversation(id: null | number) {
if (!id) {
return;
}
@ -72,7 +72,7 @@ const getConversation = async (id: null | number) => {
}
activeConversation.value = conversation;
activeConversationId.value = conversation.id;
};
}
/**
* 点击某个对话
@ -80,9 +80,9 @@ const getConversation = async (id: null | number) => {
* @param conversation 选中的对话
* @return 是否切换成功
*/
const handleConversationClick = async (
async function handleConversationClick(
conversation: AiChatConversationApi.ChatConversationVO,
) => {
) {
//
if (conversationInProgress.value) {
alert('对话中,不允许切换!');
@ -99,20 +99,20 @@ const handleConversationClick = async (
//
prompt.value = '';
return true;
};
}
/** 删除某个对话*/
const handlerConversationDelete = async (
async function handlerConversationDelete(
delConversation: AiChatConversationApi.ChatConversationVO,
) => {
) {
//
if (activeConversationId.value === delConversation.id) {
await handleConversationClear();
}
};
}
/** 清空选中的对话 */
const handleConversationClear = async () => {
async function handleConversationClear() {
//
if (conversationInProgress.value) {
alert('对话中,不允许切换!');
@ -121,31 +121,31 @@ const handleConversationClear = async () => {
activeConversationId.value = null;
activeConversation.value = null;
activeMessageList.value = [];
};
}
const openChatConversationUpdateForm = async () => {
async function openChatConversationUpdateForm() {
formModalApi.setData({ id: activeConversationId.value }).open();
};
const handleConversationUpdateSuccess = async () => {
}
async function handleConversationUpdateSuccess() {
//
await getConversation(activeConversationId.value);
};
}
/** 处理聊天对话的创建成功 */
const handleConversationCreate = async () => {
async function handleConversationCreate() {
//
await conversationListRef.value.createConversation();
};
}
/** 处理聊天对话的创建成功 */
const handleConversationCreateSuccess = async () => {
async function handleConversationCreateSuccess() {
//
prompt.value = '';
};
}
// =========== ===========
/** 获取消息 message 列表 */
const getMessageList = async () => {
async function getMessageList() {
try {
if (activeConversationId.value === null) {
return;
@ -171,7 +171,7 @@ const getMessageList = async () => {
//
activeMessageListLoading.value = false;
}
};
}
/**
* 消息列表
@ -196,17 +196,17 @@ const messageList = computed(() => {
});
/** 处理删除 message 消息 */
const handleMessageDelete = () => {
function handleMessageDelete() {
if (conversationInProgress.value) {
alert('回答中,不能删除!');
return;
}
// message
getMessageList();
};
}
/** 处理 message 清空 */
const handlerMessageClear = async () => {
async function handlerMessageClear() {
if (!activeConversationId.value) {
return;
}
@ -218,16 +218,16 @@ const handlerMessageClear = async () => {
// message
activeMessageList.value = [];
} catch {}
};
}
/** 回到 message 列表的顶部 */
const handleGoTopMessage = () => {
function handleGoTopMessage() {
messageRef.value.handlerGoTop();
};
}
// =========== ===========
/** 处理来自 keydown 的发送消息 */
const handleSendByKeydown = async (event: any) => {
async function handleSendByKeydown(event: any) {
//
if (isComposing.value) {
return;
@ -248,15 +248,15 @@ const handleSendByKeydown = async (event: any) => {
event.preventDefault(); //
}
}
};
}
/** 处理来自【发送】按钮的发送消息 */
const handleSendByButton = () => {
function handleSendByButton() {
doSendMessage(prompt.value?.trim() as string);
};
}
/** 处理 prompt 输入变化 */
const handlePromptInput = (event) => {
function handlePromptInput(event: any) {
// true
if (!isComposing.value) {
// event data null
@ -273,27 +273,27 @@ const handlePromptInput = (event) => {
inputTimeout.value = setTimeout(() => {
isComposing.value = false;
}, 400);
};
}
const onCompositionstart = () => {
function onCompositionstart() {
isComposing.value = true;
};
}
const onCompositionend = () => {
function onCompositionend() {
// console.log('...')
setTimeout(() => {
isComposing.value = false;
}, 200);
};
}
/** 真正执行【发送】消息操作 */
const doSendMessage = async (content: string) => {
async function doSendMessage(content: string) {
//
if (content.length === 0) {
message.error('发送失败,原因:内容为空!');
return;
}
if (activeConversationId.value == null) {
if (activeConversationId.value === null) {
message.error('还没创建对话,不能发送!');
return;
}
@ -304,12 +304,12 @@ const doSendMessage = async (content: string) => {
conversationId: activeConversationId.value,
content,
} as AiChatMessageApi.ChatMessageVO);
};
}
/** 真正执行【发送】消息操作 */
const doSendMessageStream = async (
async function doSendMessageStream(
userMessage: AiChatMessageApi.ChatMessageVO,
) => {
) {
// AbortController 便
conversationInAbortController.value = new AbortController();
//
@ -385,40 +385,40 @@ const doSendMessageStream = async (
},
);
} catch {}
};
}
/** 停止 stream 流式调用 */
const stopStream = async () => {
async function stopStream() {
// tip stream message controller
if (conversationInAbortController.value) {
conversationInAbortController.value.abort();
}
// false
conversationInProgress.value = false;
};
}
/** 编辑 message设置为 prompt可以再次编辑 */
const handleMessageEdit = (message: AiChatMessageApi.ChatMessageVO) => {
function handleMessageEdit(message: AiChatMessageApi.ChatMessageVO) {
prompt.value = message.content;
};
}
/** 刷新 message基于指定消息再次发起对话 */
const handleMessageRefresh = (message: AiChatMessageApi.ChatMessageVO) => {
function handleMessageRefresh(message: AiChatMessageApi.ChatMessageVO) {
doSendMessage(message.content);
};
}
// ============== =============
/** 滚动到 message 底部 */
const scrollToBottom = async (isIgnore?: boolean) => {
async function scrollToBottom(isIgnore?: boolean) {
await nextTick();
if (messageRef.value) {
messageRef.value.scrollToBottom(isIgnore);
}
};
}
/** 自提滚动效果 */
const textRoll = async () => {
async function textRoll() {
let index = 0;
try {
//
@ -475,7 +475,7 @@ const textRoll = async () => {
};
let timer = setTimeout(task, textSpeed.value);
} catch {}
};
}
/** 初始化 */
onMounted(async () => {
@ -569,8 +569,8 @@ onMounted(async () => {
<MessageList
v-if="!activeMessageListLoading && messageList.length > 0"
ref="messageRef"
:conversation="activeConversation"
:list="messageList"
:conversation="activeConversation as any"
:list="messageList as any"
@on-delete-success="handleMessageDelete"
@on-edit="handleMessageEdit"
@on-refresh="handleMessageRefresh"

View File

@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleUserList } from '#/api/system/user';
import { DICT_TYPE } from '#/utils';
import { DICT_TYPE, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchemaConversation(): VbenFormSchema[] {
@ -22,8 +22,7 @@ export function useGridFormSchemaConversation(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},
@ -118,8 +117,7 @@ export function useGridFormSchemaMessage(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -9,7 +9,7 @@ import { confirm } from '@vben/common-ui';
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 handleButtonClick = async (type: string, detail: AiImageApi.ImageVO) => {
async function handleButtonClick(type: string, detail: AiImageApi.ImageVO) {
emits('onBtnClick', type, detail);
};
}
/** 处理 Midjourney 按钮点击事件 */
const handleMidjourneyBtnClick = async (
async function handleMidjourneyBtnClick(
button: AiImageApi.ImageMidjourneyButtonsVO,
) => {
) {
//
await confirm(`确认操作 "${button.label} ${button.emoji}" ?`);
emits('onMjBtnClick', button, props.detail);
};
// emits
}
/** 监听详情 */
const { detail } = toRefs(props);
@ -46,7 +44,7 @@ watch(detail, async (newVal) => {
});
const loading = ref();
/** 处理加载状态 */
const handleLoading = async (status: number) => {
async function handleLoading(status: number) {
// loading
if (status === AiImageStatusEnum.IN_PROGRESS) {
loading.value = message.loading({
@ -57,7 +55,7 @@ const handleLoading = async (status: number) => {
} else {
if (loading.value) setTimeout(loading.value, 100);
}
};
}
/** 初始化 */
onMounted(async () => {

View File

@ -3,6 +3,8 @@ import type { AiImageApi } from '#/api/ai/image';
import { ref, toRefs, watch } from 'vue';
import { formatDate } from '@vben/utils';
import { Image } from 'ant-design-vue';
import { getImageMy } from '#/api/ai/image';
@ -12,8 +14,7 @@ import {
StableDiffusionClipGuidancePresets,
StableDiffusionSamplers,
StableDiffusionStylePresets,
} from '#/utils/constants';
import { formatTime } from '#/utils/formatTime';
} from '#/utils';
//
const props = defineProps({
@ -25,9 +26,9 @@ const props = defineProps({
const detail = ref<AiImageApi.ImageVO>({} as AiImageApi.ImageVO);
/** 获取图片详情 */
const getImageDetail = async (id: number) => {
async function getImageDetail(id: number) {
detail.value = await getImageMy(id);
};
}
const { id } = toRefs(props);
watch(
@ -53,10 +54,10 @@ watch(
<div class="tip text-lg font-bold">时间</div>
<div class="body mt-2 text-gray-600">
<div>
提交时间{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
提交时间{{ formatDate(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}
</div>
<div>
生成时间{{ formatTime(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}
生成时间{{ formatDate(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}
</div>
</div>
</div>

View File

@ -5,6 +5,7 @@ import { onMounted, onUnmounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { confirm, useVbenDrawer } from '@vben/common-ui';
import { downloadFileFromImageUrl } from '@vben/utils';
import { useDebounceFn } from '@vueuse/core';
import { Button, Card, message, Pagination } from 'ant-design-vue';
@ -15,8 +16,7 @@ import {
getImagePageMy,
midjourneyAction,
} from '#/api/ai/image';
import { AiImageStatusEnum } from '#/utils/constants';
import { download } from '#/utils/download';
import { AiImageStatusEnum } from '#/utils';
import ImageCard from './ImageCard.vue';
import ImageDetail from './ImageDetail.vue';
@ -43,18 +43,18 @@ const inProgressTimer = ref<any>(); // 生成中的 image 定时器,轮询生
const showImageDetailId = ref<number>(0); //
/** 处理查看绘图作品 */
const handleViewPublic = () => {
function handleViewPublic() {
router.push({
name: 'AiImageSquare',
});
};
}
/** 查看图片的详情 */
const handleDetailOpen = async () => {
async function handleDetailOpen() {
drawerApi.open();
};
}
/** 获得 image 图片列表 */
const getImageList = async () => {
async function getImageList() {
const loading = message.loading({
content: `加载中...`,
});
@ -77,10 +77,10 @@ const getImageList = async () => {
// Loading
loading();
}
};
}
const debounceGetImageList = useDebounceFn(getImageList, 80);
/** 轮询生成中的 image 列表 */
const refreshWatchImages = async () => {
async function refreshWatchImages() {
const imageIds = Object.keys(inProgressImageMap.value).map(Number);
if (imageIds.length === 0) {
return;
@ -101,13 +101,13 @@ const refreshWatchImages = async () => {
}
});
inProgressImageMap.value = newWatchImages;
};
}
/** 图片的点击事件 */
const handleImageButtonClick = async (
async function handleImageButtonClick(
type: string,
imageDetail: AiImageApi.ImageVO,
) => {
) {
//
if (type === 'more') {
showImageDetailId.value = imageDetail.id;
@ -124,20 +124,23 @@ const handleImageButtonClick = async (
}
//
if (type === 'download') {
await download.image({ url: imageDetail.picUrl });
await downloadFileFromImageUrl({
fileName: imageDetail.model,
source: imageDetail.picUrl,
});
return;
}
//
if (type === 'regeneration') {
await emits('onRegeneration', imageDetail);
}
};
}
/** 处理 Midjourney 按钮点击事件 */
const handleImageMidjourneyButtonClick = async (
async function handleImageMidjourneyButtonClick(
button: AiImageApi.ImageMidjourneyButtonsVO,
imageDetail: AiImageApi.ImageVO,
) => {
) {
// 1. params
const data = {
id: imageDetail.id,
@ -147,7 +150,7 @@ const handleImageMidjourneyButtonClick = async (
await midjourneyAction(data);
// 3.
await getImageList();
};
}
defineExpose({ getImageList }); /** 组件挂在的时候 */
onMounted(async () => {

View File

@ -10,11 +10,7 @@ import { confirm } from '@vben/common-ui';
import { Button, InputNumber, Select, Space, Textarea } from 'ant-design-vue';
import { drawImage } from '#/api/ai/image';
import {
AiPlatformEnum,
ImageHotWords,
OtherPlatformEnum,
} from '#/utils/constants';
import { AiPlatformEnum, ImageHotWords, OtherPlatformEnum } from '#/utils';
//
@ -39,7 +35,7 @@ const platformModels = ref<AiModelModelApi.ModelVO[]>([]); // 模型列表
const modelId = ref<number>(); //
/** 选择热词 */
const handleHotWordClick = async (hotWord: string) => {
async function handleHotWordClick(hotWord: string) {
//
if (selectHotWord.value === hotWord) {
selectHotWord.value = '';
@ -49,10 +45,10 @@ const handleHotWordClick = async (hotWord: string) => {
//
selectHotWord.value = hotWord; //
prompt.value = hotWord; //
};
}
/** 图片生成 */
const handleGenerateImage = async () => {
async function handleGenerateImage() {
//
await confirm(`确认生成内容?`);
try {
@ -76,17 +72,17 @@ const handleGenerateImage = async () => {
//
drawIn.value = false;
}
};
}
/** 填充值 */
const settingValues = async (detail: AiImageApi.ImageVO) => {
async function settingValues(detail: AiImageApi.ImageVO) {
prompt.value = detail.prompt;
width.value = detail.width;
height.value = detail.height;
};
}
/** 平台切换 */
const handlerPlatformChange = async (platform: any) => {
async function handlerPlatformChange(platform: any) {
//
platformModels.value = props.models.filter(
(item: AiModelModelApi.ModelVO) => item.platform === platform,
@ -96,7 +92,7 @@ const handlerPlatformChange = async (platform: any) => {
? platformModels.value[0].id
: undefined;
//
};
}
/** 监听 models 变化 */
watch(

View File

@ -2,7 +2,7 @@
<script setup lang="ts">
import type { AiImageApi } from '#/api/ai/image';
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';
@ -17,7 +17,7 @@ import {
Dall3SizeList,
Dall3StyleList,
ImageHotWords,
} from '#/utils/constants';
} from '#/utils';
//
const props = defineProps({
@ -37,7 +37,7 @@ const selectSize = ref<string>('1024x1024'); // 选中 size
const style = ref<string>('vivid'); // style
/** 选择热词 */
const handleHotWordClick = async (hotWord: string) => {
async function handleHotWordClick(hotWord: string) {
//
if (selectHotWord.value === hotWord) {
selectHotWord.value = '';
@ -47,10 +47,10 @@ const handleHotWordClick = async (hotWord: string) => {
//
selectHotWord.value = hotWord;
prompt.value = hotWord;
};
}
/** 选择 model 模型 */
const handleModelClick = async (model: ImageModelVO) => {
async function handleModelClick(model: ImageModelVO) {
selectModel.value = model.key;
//
//
@ -73,20 +73,20 @@ const handleModelClick = async (model: ImageModelVO) => {
if (recommendedSize) {
selectSize.value = recommendedSize.key;
}
};
}
/** 选择 style 样式 */
const handleStyleClick = async (imageStyle: ImageModelVO) => {
async function handleStyleClick(imageStyle: ImageModelVO) {
style.value = imageStyle.key;
};
}
/** 选择 size 大小 */
const handleSizeClick = async (imageSize: ImageSizeVO) => {
async function handleSizeClick(imageSize: ImageSizeVO) {
selectSize.value = imageSize.key;
};
}
/** 图片生产 */
const handleGenerateImage = async () => {
async function handleGenerateImage() {
// models
const matchedModel = props.models.find(
(item) =>
@ -127,10 +127,10 @@ const handleGenerateImage = async () => {
//
drawIn.value = false;
}
};
}
/** 填充值 */
const settingValues = async (detail: AiImageApi.ImageVO) => {
async function settingValues(detail: AiImageApi.ImageVO) {
prompt.value = detail.prompt;
selectModel.value = detail.model;
style.value = detail.options?.style;
@ -138,7 +138,7 @@ const settingValues = async (detail: AiImageApi.ImageVO) => {
(item) => item.key === `${detail.width}x${detail.height}`,
) as ImageSizeVO;
await handleSizeClick(imageSize);
};
}
/** 暴露组件方法 */
defineExpose({ settingValues });

View File

@ -2,7 +2,7 @@
<script setup lang="ts">
import type { AiImageApi } from '#/api/ai/image';
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';
@ -26,7 +26,7 @@ import {
MidjourneySizeList,
MidjourneyVersions,
NijiVersionList,
} from '#/utils/constants';
} from '#/utils';
//
@ -51,7 +51,7 @@ const selectVersion = ref<any>('6.0'); // 选中的 version
const versionList = ref<any>(MidjourneyVersions); // version
/** 选择热词 */
const handleHotWordClick = async (hotWord: string) => {
async function handleHotWordClick(hotWord: string) {
//
if (selectHotWord.value === hotWord) {
selectHotWord.value = '';
@ -61,23 +61,23 @@ const handleHotWordClick = async (hotWord: string) => {
//
selectHotWord.value = hotWord; //
prompt.value = hotWord; //
};
}
/** 点击 size 尺寸 */
const handleSizeClick = async (imageSize: ImageSizeVO) => {
async function handleSizeClick(imageSize: ImageSizeVO) {
selectSize.value = imageSize.key;
};
}
/** 点击 model 模型 */
const handleModelClick = async (model: ImageModelVO) => {
async function handleModelClick(model: ImageModelVO) {
selectModel.value = model.key;
versionList.value =
model.key === 'niji' ? NijiVersionList : MidjourneyVersions;
selectVersion.value = versionList.value[0].value;
};
}
/** 图片生成 */
const handleGenerateImage = async () => {
async function handleGenerateImage() {
// models
const matchedModel = props.models.find(
(item) =>
@ -115,10 +115,10 @@ const handleGenerateImage = async () => {
//
drawIn.value = false;
}
};
}
/** 填充值 */
const settingValues = async (detail: AiImageApi.ImageVO) => {
async function settingValues(detail: AiImageApi.ImageVO) {
//
prompt.value = detail.prompt;
// image size
@ -137,7 +137,7 @@ const settingValues = async (detail: AiImageApi.ImageVO) => {
).value;
// image
referImageUrl.value = detail.options.referImageUrl;
};
}
/** 暴露组件方法 */
defineExpose({ settingValues });

View File

@ -23,10 +23,7 @@ import {
StableDiffusionClipGuidancePresets,
StableDiffusionSamplers,
StableDiffusionStylePresets,
} from '#/utils/constants';
import { hasChinese } from '#/utils/utils';
//
} from '#/utils';
//
const props = defineProps({
@ -35,8 +32,13 @@ const props = defineProps({
default: () => [] as AiModelModelApi.ModelVO[],
},
});
const emits = defineEmits(['onDrawStart', 'onDrawComplete']);
function hasChinese(str: string) {
return /[\u4E00-\u9FA5]/.test(str);
}
//
const drawIn = ref<boolean>(false); //
const selectHotWord = ref<string>(''); //
@ -52,7 +54,7 @@ const clipGuidancePreset = ref<string>('NONE'); // 文本提示相匹配的图
const stylePreset = ref<string>('3d-model'); //
/** 选择热词 */
const handleHotWordClick = async (hotWord: string) => {
async function handleHotWordClick(hotWord: string) {
//
if (selectHotWord.value === hotWord) {
selectHotWord.value = '';
@ -62,10 +64,10 @@ const handleHotWordClick = async (hotWord: string) => {
//
selectHotWord.value = hotWord; //
prompt.value = hotWord; //
};
}
/** 图片生成 */
const handleGenerateImage = async () => {
async function handleGenerateImage() {
// models
const selectModel = 'stable-diffusion-v1-6';
const matchedModel = props.models.find(
@ -112,10 +114,10 @@ const handleGenerateImage = async () => {
//
drawIn.value = false;
}
};
}
/** 填充值 */
const settingValues = async (detail: AiImageApi.ImageVO) => {
async function settingValues(detail: AiImageApi.ImageVO) {
prompt.value = detail.prompt;
width.value = detail.width;
height.value = detail.height;
@ -125,7 +127,7 @@ const settingValues = async (detail: AiImageApi.ImageVO) => {
sampler.value = detail.options?.sampler;
clipGuidancePreset.value = detail.options?.clipGuidancePreset;
stylePreset.value = detail.options?.stylePreset;
};
}
/** 暴露组件方法 */
defineExpose({ settingValues });

View File

@ -9,7 +9,7 @@ import { Page } from '@vben/common-ui';
import { Segmented } from 'ant-design-vue';
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 Dall3 from './components/dall3/index.vue';

View File

@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleUserList } from '#/api/system/user';
import { DICT_TYPE, getDictOptions } from '#/utils';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
@ -49,8 +49,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -13,7 +13,7 @@ import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteImage, getImagePage, updateImage } from '#/api/ai/image';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales';
import { AiImageStatusEnum } from '#/utils/constants';
import { AiImageStatusEnum } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data';

View File

@ -9,7 +9,7 @@ import { useDebounceFn } from '@vueuse/core';
import { Input, Pagination } from 'ant-design-vue';
import { getImagePageMy } from '#/api/ai/image';
// TODO @fan loading
const loading = ref(true); //
const list = ref<AiImageApi.ImageVO[]>([]); //
const total = ref(0); //
@ -21,7 +21,7 @@ const queryParams = reactive({
});
/** 查询列表 */
const getList = async () => {
async function getList() {
loading.value = true;
try {
const data = await getImagePageMy(queryParams);
@ -30,7 +30,7 @@ const getList = async () => {
} finally {
loading.value = false;
}
};
}
const debounceGetList = useDebounceFn(getList, 80);
/** 搜索按钮操作 */
const handleQuery = () => {

View File

@ -19,9 +19,9 @@ const parent = inject('parent') as any;
const pollingTimer = ref<null | number>(null); // ID
/** 判断文件处理是否完成 */
const isProcessComplete = (file: any) => {
function isProcessComplete(file: any) {
return file.progress === 100;
};
}
/** 判断所有文件是否都处理完成 */
const allProcessComplete = computed(() => {
@ -29,14 +29,14 @@ const allProcessComplete = computed(() => {
});
/** 完成按钮点击事件处理 */
const handleComplete = () => {
function handleComplete() {
if (parent?.exposed?.handleBack) {
parent.exposed.handleBack();
}
};
}
/** 获取文件处理进度 */
const getProcessList = async () => {
async function getProcessList() {
try {
// 1. API
const documentIds = props.modelValue.list
@ -82,7 +82,7 @@ const getProcessList = async () => {
console.error('获取处理进度失败:', error);
pollingTimer.value = window.setTimeout(getProcessList, 5000);
}
};
}
/** 组件挂载时开始轮询 */
onMounted(() => {

View File

@ -42,12 +42,13 @@ const currentFile = ref<any>(null); // 当前选中的文件
const submitLoading = ref(false); //
/** 选择文件 */
const selectFile = async (index: number) => {
async function selectFile(index: number) {
currentFile.value = modelData.value.list[index];
await splitContentFile(currentFile.value);
};
}
/** 获取文件分段内容 */
const splitContentFile = async (file: any) => {
async function splitContentFile(file: any) {
if (!file || !file.url) {
message.warning('文件 URL 不存在');
return;
@ -65,9 +66,9 @@ const splitContentFile = async (file: any) => {
} finally {
splitLoading.value = false;
}
};
}
/** 处理预览分段 */
const handleAutoSegment = async () => {
async function handleAutoSegment() {
//
if (
!currentFile.value &&
@ -84,18 +85,18 @@ const handleAutoSegment = async () => {
//
await splitContentFile(currentFile.value);
};
}
/** 上一步按钮处理 */
const handlePrevStep = () => {
function handlePrevStep() {
const parentEl = parent || getCurrentInstance()?.parent;
if (parentEl && typeof parentEl.exposed?.goToPrevStep === 'function') {
parentEl.exposed.goToPrevStep();
}
};
}
/** 保存操作 */
const handleSave = async () => {
async function handleSave() {
//
if (
!currentFile?.value?.segments ||
@ -140,7 +141,7 @@ const handleSave = async () => {
//
submitLoading.value = false;
}
};
}
/** 初始化 */
onMounted(async () => {

View File

@ -10,11 +10,11 @@ import { computed, getCurrentInstance, inject, onMounted, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { $t } from '@vben/locales';
import { generateAcceptedFileTypes } from '@vben/utils';
import { Button, Form, message, UploadDragger } from 'ant-design-vue';
import { useUpload } from '#/components/upload/use-upload';
import { generateAcceptedFileTypes } from '#/utils/upload';
const props = defineProps({
modelValue: {
@ -70,14 +70,14 @@ const modelData = computed({
set: (val) => emit('update:modelValue', val),
});
/** 确保 list 属性存在 */
const ensureListExists = () => {
function ensureListExists() {
if (!props.modelValue.list) {
emit('update:modelValue', {
...props.modelValue,
list: [],
});
}
};
}
/** 是否所有文件都已上传完成 */
const isAllUploaded = computed(() => {
return (
@ -93,7 +93,7 @@ const isAllUploaded = computed(() => {
* @param file 待上传的文件
* @returns 是否允许上传
*/
const beforeUpload = (file: any) => {
function beforeUpload(file: any) {
// 1.1
const fileName = file.name.toLowerCase();
const fileExtension = fileName.slice(
@ -112,7 +112,7 @@ const beforeUpload = (file: any) => {
// 2.
uploadingCount.value++;
return true;
};
}
async function customRequest(info: UploadRequestOption<any>) {
const file = info.file as File;
const name = file?.name;
@ -148,7 +148,7 @@ async function customRequest(info: UploadRequestOption<any>) {
*
* @param index 要移除的文件索引
*/
const removeFile = (index: number) => {
function removeFile(index: number) {
//
const newList = [...props.modelValue.list];
newList.splice(index, 1);
@ -157,10 +157,10 @@ const removeFile = (index: number) => {
...props.modelValue,
list: newList,
});
};
}
/** 下一步按钮处理 */
const handleNextStep = () => {
function handleNextStep() {
// 1.1
if (!modelData.value.list || modelData.value.list.length === 0) {
message.warning('请上传至少一个文件');
@ -177,7 +177,7 @@ const handleNextStep = () => {
if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') {
parentEl.exposed.goToNextStep();
}
};
}
/** 初始化 */
onMounted(() => {
@ -210,9 +210,9 @@ onMounted(() => {
/>
<div class="ant-upload-text text-[16px] text-[#606266]">
拖拽文件至此或者
<em class="cursor-pointer not-italic text-[#409eff]"
>选择文件</em
>
<em class="cursor-pointer not-italic text-[#409eff]">
选择文件
</em>
</div>
<div class="ant-upload-tip mt-10px text-[12px] text-[#909399]">
已支持 {{ supportedFileTypes.join('、') }}每个文件不超过

View File

@ -56,7 +56,7 @@ provide('parent', getCurrentInstance()); // 提供 parent 给子组件使用
const tabs = useTabs();
/** 返回列表页 */
const handleBack = () => {
function handleBack() {
//
tabs.closeCurrentTab();
// 使 name 'name'+ menuId
@ -66,10 +66,10 @@ const handleBack = () => {
knowledgeId: route.query.knowledgeId,
},
});
};
}
/** 初始化数据 */
const initData = async () => {
async function initData() {
if (route.query.knowledgeId) {
formData.value.knowledgeId = route.query.knowledgeId as any;
}
@ -91,20 +91,20 @@ const initData = async () => {
//
goToNextStep();
}
};
}
/** 切换到下一步 */
const goToNextStep = () => {
function goToNextStep() {
if (currentStep.value < steps.length - 1) {
currentStep.value++;
}
};
}
/** 切换到上一步 */
const goToPrevStep = () => {
function goToPrevStep() {
if (currentStep.value > 0) {
currentStep.value--;
}
};
}
/** 初始化 */
onMounted(async () => {

View File

@ -17,7 +17,7 @@ import {
updateKnowledgeDocumentStatus,
} from '#/api/ai/knowledge/document';
import { $t } from '#/locales';
import { CommonStatusEnum } from '#/utils/constants';
import { CommonStatusEnum } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data';

View File

@ -8,6 +8,7 @@ import {
CommonStatusEnum,
DICT_TYPE,
getDictOptions,
getRangePickerDefaultProps,
} from '#/utils';
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
@ -112,8 +113,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -57,20 +57,20 @@ async function handleDelete(row: AiKnowledgeKnowledgeApi.KnowledgeVO) {
}
/** 文档按钮操作 */
const router = useRouter();
const handleDocument = (id: number) => {
function handleDocument(id: number) {
router.push({
name: 'AiKnowledgeDocument',
query: { knowledgeId: id },
});
};
}
/** 跳转到文档召回测试页面 */
const handleRetrieval = (id: number) => {
function handleRetrieval(id: number) {
router.push({
name: 'AiKnowledgeRetrieval',
query: { id },
});
};
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {

View File

@ -32,7 +32,7 @@ const queryParams = reactive({
});
/** 调用文档召回测试接口 */
const getRetrievalResult = async () => {
async function getRetrievalResult() {
if (!queryParams.content) {
message.warning('请输入查询文本');
return;
@ -54,15 +54,15 @@ const getRetrievalResult = async () => {
} finally {
loading.value = false;
}
};
}
/** 展开/收起段落内容 */
const toggleExpand = (segment: any) => {
function toggleExpand(segment: any) {
segment.expanded = !segment.expanded;
};
}
/** 获取知识库信息 */
const getKnowledgeInfo = async (id: number) => {
async function getKnowledgeInfo(id: number) {
try {
const knowledge = await getKnowledge(id);
if (knowledge) {
@ -71,7 +71,7 @@ const getKnowledgeInfo = async (id: number) => {
knowledge.similarityThreshold || queryParams.similarityThreshold;
}
} catch {}
};
}
/** 初始化 */
onMounted(() => {

View File

@ -18,7 +18,7 @@ import {
updateKnowledgeSegmentStatus,
} from '#/api/ai/knowledge/segment';
import { $t } from '#/locales';
import { CommonStatusEnum } from '#/utils/constants';
import { CommonStatusEnum } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
@ -93,9 +93,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
});
/** 修改是否发布 */
const handleStatusChange = async (
async function handleStatusChange(
row: AiKnowledgeSegmentApi.KnowledgeSegmentVO,
) => {
) {
try {
//
const text = row.status ? '启用' : '禁用';
@ -112,7 +112,8 @@ const handleStatusChange = async (
? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE;
}
};
}
onMounted(() => {
gridApi.formApi.setFieldValue('documentId', route.query.documentId);
});

View File

@ -6,7 +6,7 @@ import { nextTick, onMounted, ref } from 'vue';
import { alert, Page } from '@vben/common-ui';
import { generateMindMap } from '#/api/ai/mindmap';
import { MindMapContentExample } from '#/utils/constants';
import { MindMapContentExample } from '#/utils';
import Left from './modules/Left.vue';
import Right from './modules/Right.vue';
@ -21,13 +21,13 @@ const leftRef = ref<InstanceType<typeof Left>>(); // 左边组件
const rightRef = ref(); //
/** 使用已有内容直接生成 */
const directGenerate = (existPrompt: string) => {
function directGenerate(existPrompt: string) {
isEnd.value = false; // false true watch
generatedContent.value = existPrompt;
isEnd.value = true;
};
}
/** 提交生成 */
const submit = (data: AiMindmapApi.AiMindMapGenerateReqVO) => {
function submit(data: AiMindmapApi.AiMindMapGenerateReqVO) {
isGenerating.value = true;
isStart.value = true;
isEnd.value = false;
@ -59,13 +59,13 @@ const submit = (data: AiMindmapApi.AiMindMapGenerateReqVO) => {
},
ctrl: ctrl.value,
});
};
}
/** 停止 stream 生成 */
const stopStream = () => {
function stopStream() {
isGenerating.value = false;
isStart.value = false;
ctrl.value?.abort();
};
}
/** 初始化 */
onMounted(() => {

View File

@ -3,7 +3,7 @@ import { reactive, ref } from 'vue';
import { Button, Textarea } from 'ant-design-vue';
import { MindMapContentExample } from '#/utils/constants';
import { MindMapContentExample } from '#/utils';
defineProps<{
isGenerating: boolean;

View File

@ -7,11 +7,10 @@ import {
Toolbar,
Transformer,
} from '@vben/plugins/markmap';
import { downloadImageByCanvas } from '@vben/utils';
import { Button, Card, message } from 'ant-design-vue';
import { download } from '#/utils/download';
const props = defineProps<{
generatedContent: string; //
isEnd: boolean; //
@ -85,7 +84,7 @@ const update = () => {
}
};
/** 处理内容 */
const processContent = (text: string) => {
function processContent(text: string) {
const arr: string[] = [];
const lines = text.split('\n');
for (let line of lines) {
@ -97,21 +96,21 @@ const processContent = (text: string) => {
arr.push(line);
}
return arr.join('\n');
};
}
/** 下载图片download SVG to png file */
const downloadImage = () => {
function downloadImage() {
const svgElement = mindMapRef.value;
// SVG
const serializer = new XMLSerializer();
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)}`;
download.image({
downloadImageByCanvas({
url: base64Url,
canvasWidth: svgElement?.offsetWidth,
canvasHeight: svgElement?.offsetHeight,
drawWithImageSize: false,
});
};
}
defineExpose({
scrollBottom() {
mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight);

View File

@ -2,6 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
@ -26,8 +27,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -75,13 +75,13 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
} as VxeTableGridOptions<AiMindmapApi.MindMapVO>,
});
const openPreview = async (row: AiMindmapApi.MindMapVO) => {
async function openPreview(row: AiMindmapApi.MindMapVO) {
previewVisible.value = false;
drawerApi.open();
await nextTick();
previewVisible.value = true;
previewContent.value = row.generatedContent;
};
}
onMounted(async () => {
//
userList.value = await getSimpleUserList();

View File

@ -109,9 +109,9 @@ onMounted(async () => {
/>
</template>
<template #keyId="{ row }">
<span>{{
apiKeyList.find((item) => item.id === row.keyId)?.name
}}</span>
<span>
{{ apiKeyList.find((item) => item.id === row.keyId)?.name }}
</span>
</template>
<template #actions="{ row }">
<TableAction

View File

@ -1,7 +1,12 @@
import type { VbenFormSchema } from '#/adapter/form';
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[] {
return [
@ -64,8 +69,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -8,7 +8,7 @@ import { Page } from '@vben/common-ui';
import List from './list/index.vue';
import Mode from './mode/index.vue';
defineOptions({ name: 'Index' });
defineOptions({ name: 'AiMusicIndex' });
const listRef = ref<Nullable<{ generateMusic: (...args: any) => void }>>(null);

View File

@ -4,12 +4,11 @@ import type { Nullable } from '@vben/types';
import { inject, reactive, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { formatPast } from '@vben/utils';
import { Image, Slider } from 'ant-design-vue';
import { formatPast } from '#/utils/formatTime';
defineOptions({ name: 'Index' });
defineOptions({ name: 'AiMusicAudioBarIndex' });
const currentSong = inject('currentSong', {});

View File

@ -9,7 +9,7 @@ import audioBar from './audioBar/index.vue';
import songCard from './songCard/index.vue';
import songInfo from './songInfo/index.vue';
defineOptions({ name: 'Index' });
defineOptions({ name: 'AiMusicListIndex' });
const currentType = ref('mine');
// loading

View File

@ -5,7 +5,7 @@ import { IconifyIcon } from '@vben/icons';
import { Image } from 'ant-design-vue';
defineOptions({ name: 'Index' });
defineOptions({ name: 'AiMusicSongCardIndex' });
defineProps({
songInfo: {

View File

@ -3,7 +3,7 @@ import { inject } from 'vue';
import { Button, Card, Image } from 'ant-design-vue';
defineOptions({ name: 'Index' });
defineOptions({ name: 'AiMusicSongInfoIndex' });
const currentSong = inject('currentSong', {});
</script>

View File

@ -5,7 +5,7 @@ import { Select, Switch, Textarea } from 'ant-design-vue';
import Title from '../title/index.vue';
defineOptions({ name: 'Desc' });
defineOptions({ name: 'AiMusicModeDesc' });
const formData = reactive({
desc: '',

View File

@ -8,7 +8,7 @@ import { Button, Card, Radio } from 'ant-design-vue';
import desc from './desc.vue';
import lyric from './lyric.vue';
defineOptions({ name: 'Index' });
defineOptions({ name: 'AiMusicModeIndex' });
const emits = defineEmits(['generateMusic']);

View File

@ -5,7 +5,7 @@ import { Button, Input, Select, Space, Tag, Textarea } from 'ant-design-vue';
import Title from '../title/index.vue';
defineOptions({ name: 'Lyric' });
defineOptions({ name: 'AiMusicModeLyric' });
const tags = ['rock', 'punk', 'jazz', 'soul', 'country', 'kidsmusic', 'pop'];

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
defineOptions({ name: 'Index' });
defineOptions({ name: 'AiMusicTitleIndex' });
defineProps({
title: {

View File

@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleUserList } from '#/api/system/user';
import { DICT_TYPE, getDictOptions } from '#/utils';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
@ -45,8 +45,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -13,7 +13,7 @@ import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteMusic, getMusicPage, updateMusic } from '#/api/ai/music';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales';
import { AiMusicStatusEnum } from '#/utils/constants';
import { AiMusicStatusEnum } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data';

View File

@ -1,7 +1,7 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE, getDictOptions } from '#/utils';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
@ -30,8 +30,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -28,14 +28,14 @@ const basicInfoRef = ref<InstanceType<typeof BasicInfo>>();
const workflowDesignRef = ref<InstanceType<typeof WorkflowDesign>>();
/** 步骤校验函数 */
const validateBasic = async () => {
async function validateBasic() {
await basicInfoRef.value?.validate();
};
}
/** 工作流设计校验 */
const validateWorkflow = async () => {
async function validateWorkflow() {
await workflowDesignRef.value?.validate();
};
}
const currentStep = ref(-1); // -1
@ -60,7 +60,8 @@ provide('workflowData', workflowData);
/** 初始化数据 */
const actionType = route.params.type as string;
const initData = async () => {
async function initData() {
if (actionType === 'update') {
const workflowId = route.params.id as string;
formData.value = await getWorkflow(workflowId);
@ -79,10 +80,10 @@ const initData = async () => {
//
currentStep.value = 0;
};
}
/** 校验所有步骤数据是否完整 */
const validateAllSteps = async () => {
async function validateAllSteps() {
//
try {
await validateBasic();
@ -99,10 +100,10 @@ const validateAllSteps = async () => {
throw new Error('请完善工作流信息');
}
return true;
};
}
/** 保存操作 */
const handleSave = async () => {
async function handleSave() {
try {
//
await validateAllSteps();
@ -124,10 +125,10 @@ const handleSave = async () => {
console.error('保存失败:', error);
message.warning(error.message || '请完善所有步骤的必填信息');
}
};
}
/** 发布操作 */
const handleDeploy = async () => {
async function handleDeploy() {
try {
//
if (!formData.value.id) {
@ -158,10 +159,10 @@ const handleDeploy = async () => {
console.error('发布失败:', error);
message.warning(error.message || '发布失败');
}
};
}
/** 步骤切换处理 */
const handleStepClick = async (index: number) => {
async function handleStepClick(index: number) {
try {
if (index !== 0) {
await validateBasic();
@ -176,17 +177,17 @@ const handleStepClick = async (index: number) => {
console.error('步骤切换失败:', error);
message.warning('请先完善当前步骤必填信息');
}
};
}
const tabs = useTabs();
/** 返回列表页 */
const handleBack = () => {
function handleBack() {
//
tabs.closeCurrentTab();
// 使 name 'name'+ menuId
router.push({ path: '/ai/workflow' });
};
}
/** 初始化 */
onMounted(async () => {

View File

@ -18,9 +18,9 @@ const rules: Record<string, Rule[]> = {
};
/** 表单校验 */
const validate = async () => {
async function validate() {
await formRef.value?.validate();
};
}
defineExpose({ validate });
</script>

View File

@ -4,11 +4,12 @@ import type { Ref } from 'vue';
import { inject, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { isNumber } from '@vben/utils';
import { Button, Input, Select } from 'ant-design-vue';
import { testWorkflow } from '#/api/ai/workflow';
import Tinyflow from '#/components/tinyflow/tinyflow.vue';
import { Tinyflow } from '#/components/tinyflow';
defineProps<{
provider: any;
@ -28,26 +29,26 @@ const [Drawer, drawerApi] = useVbenDrawer({
modal: false,
});
/** 展示工作流测试抽屉 */
const testWorkflowModel = () => {
function testWorkflowModel() {
drawerApi.open();
const startNode = getStartNode();
//
const parameters = startNode.data?.parameters || [];
const paramDefinitions = {};
const paramDefinitions: Record<string, any> = {};
// 便
parameters.forEach((param) => {
parameters.forEach((param: any) => {
paramDefinitions[param.name] = param;
});
function mergeIfRequiredButNotSet(target) {
function mergeIfRequiredButNotSet(target: any) {
const needPushList = [];
for (const key in paramDefinitions) {
const param = paramDefinitions[key];
if (param.required) {
const item = target.find((item) => item.key === key);
const item = target.find((item: any) => item.key === key);
if (!item) {
needPushList.push({
@ -63,10 +64,10 @@ const testWorkflowModel = () => {
mergeIfRequiredButNotSet(params4Test.value);
paramsOfStartNode.value = paramDefinitions;
};
}
/** 运行流程 */
const goRun = async () => {
async function goRun() {
try {
const val = tinyflowRef.value.getData();
loading.value = true;
@ -77,13 +78,13 @@ const goRun = async () => {
//
const parameters = startNode.data?.parameters || [];
const paramDefinitions = {};
parameters.forEach((param) => {
const paramDefinitions: Record<string, any> = {};
parameters.forEach((param: any) => {
paramDefinitions[param.name] = param.dataType;
});
//
const convertedParams = {};
const convertedParams: Record<string, any> = {};
for (const { key, value } of params4Test.value) {
const paramKey = key.trim();
if (!paramKey) continue;
@ -95,8 +96,8 @@ const goRun = async () => {
try {
convertedParams[paramKey] = convertParamValue(value, dataType);
} catch (error_) {
throw new Error(`参数 ${paramKey} 转换失败: ${error_.message}`);
} catch (error: any) {
throw new Error(`参数 ${paramKey} 转换失败: ${error.message}`);
}
}
@ -107,42 +108,42 @@ const goRun = async () => {
const response = await testWorkflow(data);
testResult.value = response;
} catch (error_) {
} catch (error: any) {
error.value =
error_.response?.data?.message || '运行失败,请检查参数和网络连接';
error.response?.data?.message || '运行失败,请检查参数和网络连接';
} finally {
loading.value = false;
}
};
}
/** 获取开始节点 */
const getStartNode = () => {
function getStartNode() {
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) {
throw new Error('流程缺少开始节点');
}
return startNode;
};
}
/** 添加参数项 */
const addParam = () => {
function addParam() {
params4Test.value.push({ key: '', value: '' });
};
}
/** 删除参数项 */
const removeParam = (index) => {
function removeParam(index: number) {
params4Test.value.splice(index, 1);
};
}
/** 类型转换函数 */
const convertParamValue = (value, dataType) => {
function convertParamValue(value: string, dataType: string) {
if (value === '') return null; //
switch (dataType) {
case 'Number': {
const num = Number(value);
if (isNaN(num)) throw new Error('非数字格式');
if (!isNumber(num)) throw new Error('非数字格式');
return num;
}
case 'String': {
@ -157,24 +158,24 @@ const convertParamValue = (value, dataType) => {
case 'Object': {
try {
return JSON.parse(value);
} catch (error_) {
throw new Error(`JSON格式错误: ${error_.message}`);
} catch (error: any) {
throw new Error(`JSON格式错误: ${error.message}`);
}
}
default: {
throw new Error(`不支持的类型: ${dataType}`);
}
}
};
}
/** 表单校验 */
const validate = async () => {
async function validate() {
//
if (!workflowData.value) {
throw new Error('请设计流程');
}
workflowData.value = tinyflowRef.value.getData();
return true;
};
}
defineExpose({ validate });
</script>

View File

@ -6,8 +6,12 @@ import { ref } from 'vue';
import { createReusableTemplate } from '@vueuse/core';
import { Button, message, Textarea } from 'ant-design-vue';
import { DICT_TYPE, getIntDictOptions } from '#/utils';
import { AiWriteTypeEnum, WriteExample } from '#/utils/constants';
import {
AiWriteTypeEnum,
DICT_TYPE,
getIntDictOptions,
WriteExample,
} from '#/utils';
import Tag from './Tag.vue';
@ -33,19 +37,19 @@ function omit(obj: Record<string, any>, keysToOmit: string[]) {
return result;
}
/** 点击示例的时候,将定义好的文章作为示例展示出来 */
const example = (type: 'reply' | 'write') => {
function example(type: 'reply' | 'write') {
formData.value = {
...initData,
...omit(WriteExample[type], ['data']),
};
emit('example', type);
};
}
/** 重置,将表单值作为初选值 */
const reset = () => {
function reset() {
formData.value = { ...initData };
emit('reset');
};
}
const selectedTab = ref<TabType>(AiWriteTypeEnum.WRITING);
const tabs: {
@ -83,7 +87,7 @@ const formData = ref<AiWriteApi.WriteVO>({ ...initData });
/** 用来记录切换之前所填写的数据,切换的时候给赋值回来 */
const recordFormData = {} as Record<AiWriteTypeEnum, AiWriteApi.WriteVO>;
/** 切换tab */
const switchTab = (value: TabType) => {
function switchTab(value: TabType) {
if (value !== selectedTab.value) {
//
recordFormData[selectedTab.value] = formData.value;
@ -91,10 +95,10 @@ const switchTab = (value: TabType) => {
//
formData.value = { ...initData, ...recordFormData[value] };
}
};
}
/** 提交写作 */
const submit = () => {
function submit() {
if (selectedTab.value === 2 && !formData.value.originalContent) {
message.warning('请输入原文');
return;
@ -111,7 +115,7 @@ const submit = () => {
/** 使用选中 tab 值覆盖当前的 type 类型 */
type: selectedTab.value,
});
};
}
</script>
<template>

View File

@ -40,9 +40,9 @@ defineExpose({
/** 点击复制的时候复制内容 */
const showCopy = computed(() => props.content && !props.isWriting); //
const copyContent = () => {
function copyContent() {
copy(props.content);
};
}
/** 复制成功的时候 copied.value 为 true */
watch(copied, (val) => {

View File

@ -6,7 +6,7 @@ import { nextTick, ref } from 'vue';
import { alert, Page } from '@vben/common-ui';
import { writeStream } from '#/api/ai/write';
import { WriteExample } from '#/utils/constants';
import { WriteExample } from '#/utils';
import Left from './components/Left.vue';
import Right from './components/Right.vue';
@ -16,15 +16,15 @@ const isWriting = ref(false); // 是否正在写作中
const abortController = ref<AbortController>(); // // abort ( stream )
/** 停止 stream 生成 */
const stopStream = () => {
function stopStream() {
abortController.value?.abort();
isWriting.value = false;
};
}
/** 执行写作 */
const rightRef = ref<InstanceType<typeof Right>>();
const submit = (data: Partial<AiWriteApi.WriteVO>) => {
function submit(data: Partial<AiWriteApi.WriteVO>) {
abortController.value = new AbortController();
writeResult.value = '';
isWriting.value = true;
@ -51,17 +51,17 @@ const submit = (data: Partial<AiWriteApi.WriteVO>) => {
throw error;
},
});
};
}
/** 点击示例触发 */
const handleExampleClick = (type: keyof typeof WriteExample) => {
function handleExampleClick(type: keyof typeof WriteExample) {
writeResult.value = WriteExample[type].data;
};
}
/** 点击重置的时候清空写作的结果*/
const reset = () => {
function reset() {
writeResult.value = '';
};
}
</script>
<template>

View File

@ -2,7 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleUserList } from '#/api/system/user';
import { DICT_TYPE, getDictOptions } from '#/utils';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
@ -40,8 +40,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -3,7 +3,12 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
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[] {
@ -87,7 +92,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -2,7 +2,12 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
import {
CommonStatusEnum,
DICT_TYPE,
getDictOptions,
getRangePickerDefaultProps,
} from '#/utils';
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
@ -74,8 +79,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
label: '创建时间',
component: 'RangePicker',
componentProps: {
placeholder: ['开始时间', '结束时间'],
valueFormat: 'YYYY-MM-DD HH:mm:ss',
...getRangePickerDefaultProps(),
allowClear: true,
},
},

View File

@ -71,6 +71,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];

View File

@ -77,6 +77,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];

View File

@ -55,7 +55,6 @@ export function useGridFormSchema(
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
placeholder: ['开始日期', '结束日期'],
allowClear: true,
},
},
@ -65,7 +64,6 @@ export function useGridFormSchema(
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
placeholder: ['开始日期', '结束日期'],
allowClear: true,
},
},

View File

@ -21,6 +21,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];

View File

@ -50,13 +50,13 @@ export function useGridFormSchema(): VbenFormSchema[] {
allowClear: true,
},
},
// 发起时间
{
fieldName: 'createTime',
label: '发起时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];

View File

@ -21,6 +21,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];

View File

@ -56,6 +56,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];