feat(ai): 添加 AI 聊天功能
- 新增 AI 聊天对话和消息相关 API - 实现聊天界面,包括对话列表、消息列表、发送消息等功能 - 添加音乐生成功能的初始框架
This commit is contained in:
@@ -1,28 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Nullable, Recordable } from '@vben/types';
|
||||
|
||||
import { ref, unref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
import Mode from './mode/index.vue';
|
||||
|
||||
defineOptions({ name: 'Index' });
|
||||
|
||||
const listRef = ref<Nullable<{ generateMusic: (...args: any) => void }>>(null);
|
||||
|
||||
function generateMusic(args: { formData: Recordable<any> }) {
|
||||
unref(listRef)?.generateMusic(args.formData);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/ai/music/index/index.vue"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/ai/music/index/index.vue
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<div class="flex h-full items-stretch">
|
||||
<!-- 模式 -->
|
||||
<Mode class="flex-none" @generate-music="generateMusic" />
|
||||
<!-- 音频列表 -->
|
||||
<List ref="listRef" class="flex-auto" />
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
70
apps/web-antd/src/views/ai/music/index/mode/desc.vue
Normal file
70
apps/web-antd/src/views/ai/music/index/mode/desc.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import { Select, Switch, Textarea } from 'ant-design-vue';
|
||||
|
||||
import Title from '../title/index.vue';
|
||||
|
||||
defineOptions({ name: 'Desc' });
|
||||
|
||||
const formData = reactive({
|
||||
desc: '',
|
||||
pure: false,
|
||||
version: '3',
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
formData,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Title
|
||||
title="音乐/歌词说明"
|
||||
desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲"
|
||||
>
|
||||
<Textarea
|
||||
v-model:value="formData.desc"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
:maxlength="1200"
|
||||
:show-count="true"
|
||||
placeholder="一首关于糟糕分手的欢快歌曲"
|
||||
/>
|
||||
</Title>
|
||||
|
||||
<Title title="纯音乐" class="mt-[20px]" desc="创建一首没有歌词的歌曲">
|
||||
<template #extra>
|
||||
<Switch v-model:checked="formData.pure" size="small" />
|
||||
</template>
|
||||
</Title>
|
||||
|
||||
<Title
|
||||
title="版本"
|
||||
desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲"
|
||||
>
|
||||
<Select
|
||||
v-model:value="formData.version"
|
||||
class="w-full"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<Select.Option
|
||||
v-for="item in [
|
||||
{
|
||||
value: '3',
|
||||
label: 'V3',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: 'V2',
|
||||
},
|
||||
]"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Title>
|
||||
</div>
|
||||
</template>
|
||||
43
apps/web-antd/src/views/ai/music/index/mode/index.vue
Normal file
43
apps/web-antd/src/views/ai/music/index/mode/index.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Nullable, Recordable } from '@vben/types';
|
||||
|
||||
import { ref, unref } from 'vue';
|
||||
|
||||
import { Button, Card, Radio } from 'ant-design-vue';
|
||||
|
||||
import desc from './desc.vue';
|
||||
import lyric from './lyric.vue';
|
||||
|
||||
defineOptions({ name: 'Index' });
|
||||
|
||||
const emits = defineEmits(['generateMusic']);
|
||||
|
||||
const generateMode = ref('lyric');
|
||||
|
||||
const modeRef = ref<Nullable<{ formData: Recordable<any> }>>(null);
|
||||
|
||||
/*
|
||||
*@Description: 根据信息生成音乐
|
||||
*@MethodAuthor: xiaohong
|
||||
*@Date: 2024-06-27 16:40:16
|
||||
*/
|
||||
function generateMusic() {
|
||||
emits('generateMusic', { formData: unref(modeRef)?.formData });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="mb-[0!important] h-full w-[300px]">
|
||||
<Radio.Group v-model:value="generateMode" class="mb-[15px]">
|
||||
<Radio.Button value="desc"> 描述模式 </Radio.Button>
|
||||
<Radio.Button value="lyric"> 歌词模式 </Radio.Button>
|
||||
</Radio.Group>
|
||||
|
||||
<!-- 描述模式/歌词模式 切换 -->
|
||||
<component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef" />
|
||||
|
||||
<Button type="primary" shape="round" class="w-full" @click="generateMusic">
|
||||
创作音乐
|
||||
</Button>
|
||||
</Card>
|
||||
</template>
|
||||
103
apps/web-antd/src/views/ai/music/index/mode/lyric.vue
Normal file
103
apps/web-antd/src/views/ai/music/index/mode/lyric.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
import { Button, Input, Select, Space, Tag, Textarea } from 'ant-design-vue';
|
||||
|
||||
import Title from '../title/index.vue';
|
||||
|
||||
defineOptions({ name: 'Lyric' });
|
||||
|
||||
const tags = ['rock', 'punk', 'jazz', 'soul', 'country', 'kidsmusic', 'pop'];
|
||||
|
||||
const showCustom = ref(false);
|
||||
|
||||
const formData = reactive({
|
||||
lyric: '',
|
||||
style: '',
|
||||
name: '',
|
||||
version: '',
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
formData,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
<Title title="歌词" desc="自己编写歌词或使用Ai生成歌词,两节/8行效果最佳">
|
||||
<Textarea
|
||||
v-model:value="formData.lyric"
|
||||
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||
:maxlength="1200"
|
||||
:show-count="true"
|
||||
placeholder="请输入您自己的歌词"
|
||||
/>
|
||||
</Title>
|
||||
|
||||
<Title title="音乐风格">
|
||||
<Space class="flex-wrap">
|
||||
<Tag v-for="tag in tags" :key="tag" class="mb-[8px]">
|
||||
{{ tag }}
|
||||
</Tag>
|
||||
</Space>
|
||||
|
||||
<Button
|
||||
:type="showCustom ? 'primary' : 'default'"
|
||||
shape="round"
|
||||
size="small"
|
||||
class="mb-[6px]"
|
||||
@click="showCustom = !showCustom"
|
||||
>
|
||||
自定义风格
|
||||
</Button>
|
||||
</Title>
|
||||
|
||||
<Title
|
||||
v-show="showCustom"
|
||||
desc="描述您想要的音乐风格,Suno无法识别艺术家的名字,但可以理解流派和氛围"
|
||||
class="mt-[12px]"
|
||||
>
|
||||
<Textarea
|
||||
v-model="formData.style"
|
||||
:autosize="{ minRows: 4, maxRows: 4 }"
|
||||
:maxlength="256"
|
||||
show-count
|
||||
placeholder="输入音乐风格(英文)"
|
||||
/>
|
||||
</Title>
|
||||
|
||||
<Title title="音乐/歌曲名称">
|
||||
<Input
|
||||
class="w-full"
|
||||
v-model="formData.name"
|
||||
placeholder="请输入音乐/歌曲名称"
|
||||
/>
|
||||
</Title>
|
||||
|
||||
<Title title="版本">
|
||||
<Select
|
||||
v-model:value="formData.version"
|
||||
class="w-full"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<Select.Option
|
||||
v-for="item in [
|
||||
{
|
||||
value: '3',
|
||||
label: 'V3',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: 'V2',
|
||||
},
|
||||
]"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Title>
|
||||
</div>
|
||||
</template>
|
||||
27
apps/web-antd/src/views/ai/music/index/title/index.vue
Normal file
27
apps/web-antd/src/views/ai/music/index/title/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'Index' });
|
||||
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
desc: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mb-[12px]">
|
||||
<div class="flex items-center justify-between" style="color: #303133">
|
||||
<span>{{ title }}</span>
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
<div class="my-[8px] text-[12px]" style="color: #909399">
|
||||
{{ desc }}
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user