feat:【ele】【ai】音乐的迁移
This commit is contained in:
@@ -27,6 +27,7 @@ export function getKnowledge(id: number) {
|
|||||||
`/ai/knowledge/get?id=${id}`,
|
`/ai/knowledge/get?id=${id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增知识库
|
// 新增知识库
|
||||||
export function createKnowledge(data: AiKnowledgeKnowledgeApi.Knowledge) {
|
export function createKnowledge(data: AiKnowledgeKnowledgeApi.Knowledge) {
|
||||||
return requestClient.post('/ai/knowledge/create', data);
|
return requestClient.post('/ai/knowledge/create', data);
|
||||||
|
|||||||
29
apps/web-ele/src/views/ai/music/index/index.vue
Normal file
29
apps/web-ele/src/views/ai/music/index/index.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { Nullable, Recordable } from '@vben/types';
|
||||||
|
|
||||||
|
import { ref, unref } from 'vue';
|
||||||
|
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import List from './list/index.vue';
|
||||||
|
import Mode from './mode/index.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicIndex' });
|
||||||
|
|
||||||
|
const listRef = ref<Nullable<{ generateMusic: (...args: any) => void }>>(null);
|
||||||
|
|
||||||
|
function generateMusic(args: { formData: Recordable<any> }) {
|
||||||
|
unref(listRef)?.generateMusic(args.formData);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<div class="flex h-full items-stretch">
|
||||||
|
<!-- 模式 -->
|
||||||
|
<Mode class="flex-none" @generate-music="generateMusic" />
|
||||||
|
<!-- 音频列表 -->
|
||||||
|
<List ref="listRef" class="flex-auto" />
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { inject, reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
import { formatPast } from '@vben/utils';
|
||||||
|
|
||||||
|
import { ElImage, ElSlider } from 'element-plus';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicAudioBarIndex' });
|
||||||
|
|
||||||
|
const currentSong = inject<any>('currentSong', {});
|
||||||
|
|
||||||
|
const audioRef = ref<HTMLAudioElement | null>(null);
|
||||||
|
const audioProps = reactive<any>({
|
||||||
|
autoplay: true,
|
||||||
|
paused: false,
|
||||||
|
currentTime: '00:00',
|
||||||
|
duration: '00:00',
|
||||||
|
muted: false,
|
||||||
|
volume: 50,
|
||||||
|
}); // 音频相关属性https://www.runoob.com/tags/ref-av-dom.html
|
||||||
|
|
||||||
|
function toggleStatus(type: string) {
|
||||||
|
audioProps[type] = !audioProps[type];
|
||||||
|
if (type === 'paused' && audioRef.value) {
|
||||||
|
if (audioProps[type]) {
|
||||||
|
audioRef.value.pause();
|
||||||
|
} else {
|
||||||
|
audioRef.value.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新播放位置 */
|
||||||
|
function audioTimeUpdate(args: any) {
|
||||||
|
audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="b-1 b-l-none h-18 bg-card flex items-center justify-between border border-solid border-rose-100 px-2"
|
||||||
|
>
|
||||||
|
<!-- 歌曲信息 -->
|
||||||
|
<div class="flex gap-2.5">
|
||||||
|
<ElImage
|
||||||
|
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||||
|
class="!w-[45px]"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<div>{{ currentSong.name }}</div>
|
||||||
|
<div class="text-xs text-gray-400">{{ currentSong.singer }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 音频controls -->
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<IconifyIcon
|
||||||
|
icon="majesticons:back-circle"
|
||||||
|
class="size-5 cursor-pointer text-gray-300"
|
||||||
|
/>
|
||||||
|
<IconifyIcon
|
||||||
|
:icon="
|
||||||
|
audioProps.paused
|
||||||
|
? 'mdi:arrow-right-drop-circle'
|
||||||
|
: 'solar:pause-circle-bold'
|
||||||
|
"
|
||||||
|
class="size-7 cursor-pointer"
|
||||||
|
@click="toggleStatus('paused')"
|
||||||
|
/>
|
||||||
|
<IconifyIcon
|
||||||
|
icon="majesticons:next-circle"
|
||||||
|
class="size-5 cursor-pointer text-gray-300"
|
||||||
|
/>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<span>{{ audioProps.currentTime }}</span>
|
||||||
|
<ElSlider v-model="audioProps.duration" color="#409eff" class="!w-40" />
|
||||||
|
<span>{{ audioProps.duration }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- 音频 -->
|
||||||
|
<audio
|
||||||
|
v-bind="audioProps"
|
||||||
|
ref="audioRef"
|
||||||
|
controls
|
||||||
|
v-show="!audioProps"
|
||||||
|
@timeupdate="audioTimeUpdate"
|
||||||
|
>
|
||||||
|
<!-- <source :src="audioUrl" /> -->
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<IconifyIcon
|
||||||
|
:icon="audioProps.muted ? 'tabler:volume-off' : 'tabler:volume'"
|
||||||
|
class="size-5 cursor-pointer"
|
||||||
|
@click="toggleStatus('muted')"
|
||||||
|
/>
|
||||||
|
<ElSlider v-model="audioProps.volume" color="#409eff" class="!w-40" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
100
apps/web-ele/src/views/ai/music/index/list/index.vue
Normal file
100
apps/web-ele/src/views/ai/music/index/list/index.vue
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
|
import { provide, ref } from 'vue';
|
||||||
|
|
||||||
|
import { ElCol, ElEmpty, ElRow, ElTabPane, ElTabs } from 'element-plus';
|
||||||
|
|
||||||
|
import audioBar from './audioBar/index.vue';
|
||||||
|
import songCard from './songCard/index.vue';
|
||||||
|
import songInfo from './songInfo/index.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicListIndex' });
|
||||||
|
|
||||||
|
const currentType = ref('mine');
|
||||||
|
const loading = ref(false); // loading 状态
|
||||||
|
const currentSong = ref({}); // 当前音乐
|
||||||
|
const mySongList = ref<Recordable<any>[]>([]);
|
||||||
|
const squareSongList = ref<Recordable<any>[]>([]);
|
||||||
|
|
||||||
|
function generateMusic(formData: Recordable<any>) {
|
||||||
|
loading.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
mySongList.value = Array.from({ length: 20 }, (_, index) => {
|
||||||
|
return {
|
||||||
|
id: index,
|
||||||
|
audioUrl: '',
|
||||||
|
videoUrl: '',
|
||||||
|
title: `我走后${index}`,
|
||||||
|
imageUrl:
|
||||||
|
'https://www.carsmp3.com/data/attachment/forum/201909/19/091020q5kgre20fidreqyt.jpg',
|
||||||
|
desc: 'Metal, symphony, film soundtrack, grand, majesticMetal, dtrack, grand, majestic',
|
||||||
|
date: '2024年04月30日 14:02:57',
|
||||||
|
lyric: `<div class="_words_17xen_66"><div>大江东去,浪淘尽,千古风流人物。
|
||||||
|
</div><div>故垒西边,人道是,三国周郎赤壁。
|
||||||
|
</div><div>乱石穿空,惊涛拍岸,卷起千堆雪。
|
||||||
|
</div><div>江山如画,一时多少豪杰。
|
||||||
|
</div><div>
|
||||||
|
</div><div>遥想公瑾当年,小乔初嫁了,雄姿英发。
|
||||||
|
</div><div>羽扇纶巾,谈笑间,樯橹灰飞烟灭。
|
||||||
|
</div><div>故国神游,多情应笑我,早生华发。
|
||||||
|
</div><div>人生如梦,一尊还酹江月。</div></div>`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
loading.value = false;
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCurrentSong(music: Recordable<any>) {
|
||||||
|
currentSong.value = music;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
generateMusic,
|
||||||
|
});
|
||||||
|
|
||||||
|
provide('currentSong', currentSong);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex flex-auto overflow-hidden">
|
||||||
|
<ElTabs
|
||||||
|
v-model="currentType"
|
||||||
|
class="flex-auto px-5"
|
||||||
|
tab-position="bottom"
|
||||||
|
>
|
||||||
|
<!-- 我的创作 -->
|
||||||
|
<ElTabPane name="mine" label="我的创作" v-loading="loading">
|
||||||
|
<ElRow v-if="mySongList.length > 0" :gutter="12">
|
||||||
|
<ElCol v-for="song in mySongList" :key="song.id" :span="24">
|
||||||
|
<songCard :song-info="song" @play="setCurrentSong(song)" />
|
||||||
|
</ElCol>
|
||||||
|
</ElRow>
|
||||||
|
<ElEmpty v-else description="暂无音乐" />
|
||||||
|
</ElTabPane>
|
||||||
|
|
||||||
|
<!-- 试听广场 -->
|
||||||
|
<ElTabPane name="square" label="试听广场" v-loading="loading">
|
||||||
|
<ElRow v-if="squareSongList.length > 0" :gutter="12">
|
||||||
|
<ElCol v-for="song in squareSongList" :key="song.id" :span="24">
|
||||||
|
<songCard :song-info="song" @play="setCurrentSong(song)" />
|
||||||
|
</ElCol>
|
||||||
|
</ElRow>
|
||||||
|
<ElEmpty v-else description="暂无音乐" />
|
||||||
|
</ElTabPane>
|
||||||
|
</ElTabs>
|
||||||
|
<!-- songInfo -->
|
||||||
|
<songInfo class="flex-none" />
|
||||||
|
</div>
|
||||||
|
<audioBar class="flex-none" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-tabs) {
|
||||||
|
.el-tabs__content {
|
||||||
|
padding: 0 7px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { inject } from 'vue';
|
||||||
|
|
||||||
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
|
import { ElImage } from 'element-plus';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicSongCardIndex' });
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
songInfo: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(['play']);
|
||||||
|
|
||||||
|
const currentSong = inject<any>('currentSong', {});
|
||||||
|
|
||||||
|
function playSong() {
|
||||||
|
emits('play');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mb-3 flex rounded p-3">
|
||||||
|
<div class="relative" @click="playSong">
|
||||||
|
<ElImage :src="songInfo.imageUrl" class="w-20 flex-none" />
|
||||||
|
<div
|
||||||
|
class="absolute left-0 top-0 flex h-full w-full cursor-pointer items-center justify-center bg-black bg-opacity-40"
|
||||||
|
>
|
||||||
|
<IconifyIcon
|
||||||
|
:icon="
|
||||||
|
currentSong.id === songInfo.id
|
||||||
|
? 'solar:pause-circle-bold'
|
||||||
|
: 'mdi:arrow-right-drop-circle'
|
||||||
|
"
|
||||||
|
:size="30"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ml-2">
|
||||||
|
<div>{{ songInfo.title }}</div>
|
||||||
|
<div class="mt-2 line-clamp-2 text-xs">
|
||||||
|
{{ songInfo.desc }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { inject } from 'vue';
|
||||||
|
|
||||||
|
import { ElButton, ElCard, ElImage } from 'element-plus';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicSongInfoIndex' });
|
||||||
|
|
||||||
|
const currentSong = inject<any>('currentSong', {});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElCard class="!mb-0 w-40 leading-6">
|
||||||
|
<ElImage :src="currentSong.imageUrl" class="h-full w-full" />
|
||||||
|
|
||||||
|
<div class="">{{ currentSong.title }}</div>
|
||||||
|
<div class="line-clamp-1 text-xs">
|
||||||
|
{{ currentSong.desc }}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs">
|
||||||
|
{{ currentSong.date }}
|
||||||
|
</div>
|
||||||
|
<ElButton size="small" round class="my-2">信息复用</ElButton>
|
||||||
|
<div class="text-xs" v-html="currentSong.lyric"></div>
|
||||||
|
</ElCard>
|
||||||
|
</template>
|
||||||
66
apps/web-ele/src/views/ai/music/index/mode/desc.vue
Normal file
66
apps/web-ele/src/views/ai/music/index/mode/desc.vue
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
|
import { ElInput, ElOption, ElSelect, ElSwitch } from 'element-plus';
|
||||||
|
|
||||||
|
import Title from '../title/index.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicModeDesc' });
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
desc: '',
|
||||||
|
pure: false,
|
||||||
|
version: '3',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
formData,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Title
|
||||||
|
title="音乐/歌词说明"
|
||||||
|
desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲"
|
||||||
|
>
|
||||||
|
<ElInput
|
||||||
|
v-model="formData.desc"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||||
|
:maxlength="1200"
|
||||||
|
:show-word-limit="true"
|
||||||
|
placeholder="一首关于糟糕分手的欢快歌曲"
|
||||||
|
/>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Title title="纯音乐" class="mt-5" desc="创建一首没有歌词的歌曲">
|
||||||
|
<template #extra>
|
||||||
|
<ElSwitch v-model="formData.pure" size="small" />
|
||||||
|
</template>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Title
|
||||||
|
title="版本"
|
||||||
|
desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲"
|
||||||
|
>
|
||||||
|
<ElSelect v-model="formData.version" class="w-full" placeholder="请选择">
|
||||||
|
<ElOption
|
||||||
|
v-for="item in [
|
||||||
|
{
|
||||||
|
value: '3',
|
||||||
|
label: 'V3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
label: 'V2',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</ElSelect>
|
||||||
|
</Title>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
38
apps/web-ele/src/views/ai/music/index/mode/index.vue
Normal file
38
apps/web-ele/src/views/ai/music/index/mode/index.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { Nullable, Recordable } from '@vben/types';
|
||||||
|
|
||||||
|
import { ref, unref } from 'vue';
|
||||||
|
|
||||||
|
import { ElButton, ElCard, ElRadioButton, ElRadioGroup } from 'element-plus';
|
||||||
|
|
||||||
|
import desc from './desc.vue';
|
||||||
|
import lyric from './lyric.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicModeIndex' });
|
||||||
|
|
||||||
|
const emits = defineEmits(['generateMusic']);
|
||||||
|
|
||||||
|
const generateMode = ref('lyric');
|
||||||
|
|
||||||
|
const modeRef = ref<Nullable<{ formData: Recordable<any> }>>(null);
|
||||||
|
|
||||||
|
function generateMusic() {
|
||||||
|
emits('generateMusic', { formData: unref(modeRef)?.formData });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElCard class="!mb-0 h-full w-80">
|
||||||
|
<ElRadioGroup v-model="generateMode" class="mb-4">
|
||||||
|
<ElRadioButton value="desc"> 描述模式 </ElRadioButton>
|
||||||
|
<ElRadioButton value="lyric"> 歌词模式 </ElRadioButton>
|
||||||
|
</ElRadioGroup>
|
||||||
|
|
||||||
|
<!-- 描述模式/歌词模式 切换 -->
|
||||||
|
<component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef" />
|
||||||
|
|
||||||
|
<ElButton type="primary" round class="w-full" @click="generateMusic">
|
||||||
|
创作音乐
|
||||||
|
</ElButton>
|
||||||
|
</ElCard>
|
||||||
|
</template>
|
||||||
107
apps/web-ele/src/views/ai/music/index/mode/lyric.vue
Normal file
107
apps/web-ele/src/views/ai/music/index/mode/lyric.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ElButton,
|
||||||
|
ElInput,
|
||||||
|
ElOption,
|
||||||
|
ElSelect,
|
||||||
|
ElSpace,
|
||||||
|
ElTag,
|
||||||
|
} from 'element-plus';
|
||||||
|
|
||||||
|
import Title from '../title/index.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'AiMusicModeLyric' });
|
||||||
|
|
||||||
|
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行效果最佳">
|
||||||
|
<ElInput
|
||||||
|
v-model="formData.lyric"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 6, maxRows: 6 }"
|
||||||
|
:maxlength="1200"
|
||||||
|
:show-word-limit="true"
|
||||||
|
placeholder="请输入您自己的歌词"
|
||||||
|
/>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Title title="音乐风格">
|
||||||
|
<ElSpace class="flex-wrap">
|
||||||
|
<ElTag v-for="tag in tags" :key="tag" class="mb-2">
|
||||||
|
{{ tag }}
|
||||||
|
</ElTag>
|
||||||
|
</ElSpace>
|
||||||
|
|
||||||
|
<ElButton
|
||||||
|
:type="showCustom ? 'primary' : 'default'"
|
||||||
|
round
|
||||||
|
size="small"
|
||||||
|
class="mb-2"
|
||||||
|
@click="showCustom = !showCustom"
|
||||||
|
>
|
||||||
|
自定义风格
|
||||||
|
</ElButton>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Title
|
||||||
|
v-show="showCustom"
|
||||||
|
desc="描述您想要的音乐风格,Suno无法识别艺术家的名字,但可以理解流派和氛围"
|
||||||
|
class="mt-3"
|
||||||
|
>
|
||||||
|
<ElInput
|
||||||
|
v-model="formData.style"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 4, maxRows: 4 }"
|
||||||
|
:maxlength="256"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="输入音乐风格(英文)"
|
||||||
|
/>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Title title="音乐/歌曲名称">
|
||||||
|
<ElInput
|
||||||
|
class="w-full"
|
||||||
|
v-model="formData.name"
|
||||||
|
placeholder="请输入音乐/歌曲名称"
|
||||||
|
/>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Title title="版本">
|
||||||
|
<ElSelect v-model="formData.version" class="w-full" placeholder="请选择">
|
||||||
|
<ElOption
|
||||||
|
v-for="item in [
|
||||||
|
{
|
||||||
|
value: '3',
|
||||||
|
label: 'V3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
label: 'V2',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</ElSelect>
|
||||||
|
</Title>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
27
apps/web-ele/src/views/ai/music/index/title/index.vue
Normal file
27
apps/web-ele/src/views/ai/music/index/title/index.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({ name: 'AiMusicTitleIndex' });
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
desc: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="flex items-center justify-between text-gray-600">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<slot name="extra"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 text-xs text-gray-400">
|
||||||
|
{{ desc }}
|
||||||
|
</div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user