Merge remote-tracking branch 'origin/dev' into dev
commit
440ad58a77
|
@ -10,12 +10,13 @@ const prefixCls = getPrefixCls('content-wrap')
|
|||
|
||||
defineProps({
|
||||
title: propTypes.string.def(''),
|
||||
message: propTypes.string.def('')
|
||||
message: propTypes.string.def(''),
|
||||
bodyStyle: propTypes.object.def({ padding: '20px' })
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard :class="[prefixCls, 'mb-15px']" shadow="never">
|
||||
<ElCard :body-style="bodyStyle" :class="[prefixCls, 'mb-15px']" shadow="never">
|
||||
<template v-if="title" #header>
|
||||
<div class="flex items-center">
|
||||
<span class="text-16px font-700">{{ title }}</span>
|
||||
|
@ -30,8 +31,6 @@ defineProps({
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</ElCard>
|
||||
</template>
|
||||
|
|
|
@ -38,24 +38,24 @@ provide('reload', reload)
|
|||
:class="[
|
||||
'p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
|
||||
{
|
||||
'!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
|
||||
'!h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
|
||||
(fixedHeader &&
|
||||
(layout === 'classic' || layout === 'topLeft' || layout === 'top') &&
|
||||
footer) ||
|
||||
(!tagsView && layout === 'top' && footer),
|
||||
'!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]':
|
||||
'!h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]':
|
||||
tagsView && layout === 'top' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]':
|
||||
'!h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]':
|
||||
!fixedHeader && layout === 'classic' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
|
||||
'!h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
|
||||
!fixedHeader && layout === 'topLeft' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]':
|
||||
'!h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]':
|
||||
fixedHeader && layout === 'cutMenu' && footer,
|
||||
|
||||
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]':
|
||||
'!h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]':
|
||||
!fixedHeader && layout === 'cutMenu' && footer
|
||||
}
|
||||
]"
|
||||
|
|
|
@ -70,6 +70,26 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/ai/music',
|
||||
component: Layout,
|
||||
redirect: '/index',
|
||||
name: 'AIMusic',
|
||||
meta: {},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/ai//music/index.vue'),
|
||||
name: 'AIMusicIndex',
|
||||
meta: {
|
||||
title: 'AI 音乐',
|
||||
icon: 'ep:home-filled',
|
||||
noCache: false,
|
||||
affix: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
component: Layout,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { store } from '../index'
|
|||
import { DictDataVO } from '@/api/system/dict/types'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
const { wsCache } = useCache('sessionStorage')
|
||||
import { getSimpleDictDataList } from '@/api/system/dict/dict.data'
|
||||
// import { getSimpleDictDataList } from '@/api/system/dict/dict.data'
|
||||
|
||||
export interface DictValueType {
|
||||
value: any
|
||||
|
@ -45,7 +45,8 @@ export const useDictStore = defineStore('dict', {
|
|||
this.dictMap = dictMap
|
||||
this.isSetDict = true
|
||||
} else {
|
||||
const res = await getSimpleDictDataList()
|
||||
const res = []
|
||||
// const res = await getSimpleDictDataList()
|
||||
// 设置数据
|
||||
const dictDataMap = new Map<string, any>()
|
||||
res.forEach((dictData: DictDataVO) => {
|
||||
|
@ -75,7 +76,8 @@ export const useDictStore = defineStore('dict', {
|
|||
},
|
||||
async resetDict() {
|
||||
wsCache.delete(CACHE_KEY.DICT_CACHE)
|
||||
const res = await getSimpleDictDataList()
|
||||
const res = []
|
||||
// const res = await getSimpleDictDataList()
|
||||
// 设置数据
|
||||
const dictDataMap = new Map<string, any>()
|
||||
res.forEach((dictData: DictDataVO) => {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<div class="h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">播放器</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
defineOptions({ name: 'Index' })
|
||||
|
||||
</script>
|
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="flex-auto flex overflow-hidden">
|
||||
<el-tabs v-model="currentType" class="flex-auto px-[var(--app-content-padding)]">
|
||||
<!-- 我的创作 -->
|
||||
<el-tab-pane label="我的创作" v-loading="loading" name="mine">
|
||||
<el-row v-if="mySongList.length" :gutter="12">
|
||||
<el-col v-for="song in mySongList" :key="song.id" :span="24">
|
||||
<songCard v-bind="song"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty v-else description="暂无音乐"/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 试听广场 -->
|
||||
<el-tab-pane label="试听广场" v-loading="loading" name="square">
|
||||
<el-row v-if="squareSongList.length" v-loading="loading" :gutter="12">
|
||||
<el-col v-for="song in squareSongList" :key="song.id" :span="24">
|
||||
<songCard v-bind="song"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty v-else description="暂无音乐"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- songInfo -->
|
||||
<songInfo v-bind="squareSongList[0]" class="flex-none"/>
|
||||
</div>
|
||||
<audioBar class="flex-none"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import songCard from './songCard/index.vue'
|
||||
import songInfo from './songInfo/index.vue'
|
||||
import audioBar from './audioBar/index.vue'
|
||||
|
||||
defineOptions({ name: 'Index' })
|
||||
|
||||
const currentType = ref('mine')
|
||||
// loading 状态
|
||||
const loading = ref(false)
|
||||
|
||||
const mySongList = ref<Recordable[]>([])
|
||||
const squareSongList = ref<Recordable[]>([])
|
||||
|
||||
/*
|
||||
*@Description: 调接口生成音乐列表
|
||||
*@MethodAuthor: xiaohong
|
||||
*@Date: 2024-06-27 17:06:44
|
||||
*/
|
||||
function generateMusic (formData: Recordable) {
|
||||
console.log(formData);
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
mySongList.value = Array.from({ length: 20 }, (_, index) => {
|
||||
return {
|
||||
id: index,
|
||||
audioUrl: '',
|
||||
videoUrl: '',
|
||||
title: '我走后',
|
||||
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)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
generateMusic
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.el-tabs__content{
|
||||
padding: 0 7px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<div class="flex bg-[var(--el-bg-color-overlay)] p-12px mb-12px rounded-1">
|
||||
<el-image :src="imageUrl" class="flex-none w-80px"/>
|
||||
<div class="ml-8px">
|
||||
<div>{{ title }}</div>
|
||||
<div class="mt-8px text-12px text-[var(--el-text-color-secondary)] line-clamp-2">
|
||||
{{ desc }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
defineOptions({ name: 'Index' })
|
||||
|
||||
defineProps({
|
||||
imageUrl: {
|
||||
type: String
|
||||
},
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
desc: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<ContentWrap class="w-300px mb-[0!important] line-height-24px">
|
||||
<el-image :src="imageUrl"/>
|
||||
<div class="">{{ title }}</div>
|
||||
<div class="text-[var(--el-text-color-secondary)] text-12px line-clamp-1">{{ desc }}</div>
|
||||
<div class="text-[var(--el-text-color-secondary)] text-12px">{{ date }}</div>
|
||||
<el-button size="small" round class="my-6px">信息复用</el-button>
|
||||
<div class="text-[var(--el-text-color-secondary)] text-12px" v-html="lyric"></div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
defineOptions({ name: 'Index' })
|
||||
|
||||
defineProps({
|
||||
imageUrl: {
|
||||
type: String
|
||||
},
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
desc: {
|
||||
type: String
|
||||
},
|
||||
date: {
|
||||
type: String
|
||||
},
|
||||
lyric: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<div>
|
||||
<Title title="音乐/歌词说明" desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲">
|
||||
<el-input
|
||||
v-model="formData.desc"
|
||||
:autosize="{ minRows: 6, maxRows: 6}"
|
||||
resize="none"
|
||||
type="textarea"
|
||||
maxlength="1200"
|
||||
show-word-limit
|
||||
placeholder="一首关于糟糕分手的欢快歌曲"
|
||||
/>
|
||||
</Title>
|
||||
|
||||
<Title title="纯音乐" desc="创建一首没有歌词的歌曲">
|
||||
<template #extra>
|
||||
<el-switch v-model="formData.pure" size="small"/>
|
||||
</template>
|
||||
</Title>
|
||||
|
||||
<Title title="版本" desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲">
|
||||
<el-select v-model="formData.version" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in [{
|
||||
value: '3',
|
||||
label: 'V3'
|
||||
}, {
|
||||
value: '2',
|
||||
label: 'V2'
|
||||
}]"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</Title>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Title from '../title/index.vue'
|
||||
|
||||
defineOptions({ name: 'Desc' })
|
||||
|
||||
const formData = reactive({
|
||||
desc: '',
|
||||
pure: false,
|
||||
version: '3'
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
formData
|
||||
})
|
||||
|
||||
</script>
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<ContentWrap class="w-300px h-full">
|
||||
<el-radio-group v-model="generateMode" class="mb-15px">
|
||||
<el-radio-button label="desc">
|
||||
描述模式
|
||||
</el-radio-button>
|
||||
<el-radio-button label="lyric">
|
||||
歌词模式
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<!-- 描述模式/歌词模式 切换 -->
|
||||
<component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef"/>
|
||||
|
||||
<el-button type="primary" round class="w-full" @click="generateMusic">
|
||||
创作音乐
|
||||
</el-button>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import desc from './desc.vue'
|
||||
import lyric from './lyric.vue'
|
||||
|
||||
defineOptions({ name: 'Index' })
|
||||
|
||||
const emits = defineEmits(['generate-music'])
|
||||
|
||||
const generateMode = ref('lyric')
|
||||
|
||||
interface ModeRef {
|
||||
formData: Recordable
|
||||
}
|
||||
const modeRef = ref<ModeRef | null>(null)
|
||||
|
||||
/*
|
||||
*@Description: 根据信息生成音乐
|
||||
*@MethodAuthor: xiaohong
|
||||
*@Date: 2024-06-27 16:40:16
|
||||
*/
|
||||
function generateMusic () {
|
||||
emits('generate-music', {formData: unref(modeRef)?.formData.value})
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<div class="">
|
||||
<Title title="歌词" desc="自己编写歌词或使用Ai生成歌词,两节/8行效果最佳">
|
||||
<el-input
|
||||
v-model="formData.lyric"
|
||||
:autosize="{ minRows: 6, maxRows: 6}"
|
||||
resize="none"
|
||||
type="textarea"
|
||||
maxlength="1200"
|
||||
show-word-limit
|
||||
placeholder="请输入您自己的歌词"
|
||||
/>
|
||||
</Title>
|
||||
|
||||
<Title title="音乐风格">
|
||||
<el-space class="flex-wrap">
|
||||
<el-tag v-for="tag in tags" :key="tag" round class="mb-8px">{{tag}}</el-tag>
|
||||
</el-space>
|
||||
|
||||
<el-button
|
||||
:type="showCustom ? 'primary': 'default'"
|
||||
round
|
||||
size="small"
|
||||
class="mb-6px"
|
||||
@click="showCustom = !showCustom"
|
||||
>自定义风格
|
||||
</el-button>
|
||||
</Title>
|
||||
|
||||
<Title v-show="showCustom" desc="描述您想要的音乐风格,Suno无法识别艺术家的名字,但可以理解流派和氛围" class="-mt-12px">
|
||||
<el-input
|
||||
v-model="formData.style"
|
||||
:autosize="{ minRows: 4, maxRows: 4}"
|
||||
resize="none"
|
||||
type="textarea"
|
||||
maxlength="256"
|
||||
show-word-limit
|
||||
placeholder="输入音乐风格(英文)"
|
||||
/>
|
||||
</Title>
|
||||
|
||||
<Title title="音乐/歌曲名称">
|
||||
<el-input v-model="formData.name" placeholder="请输入音乐/歌曲名称"/>
|
||||
</Title>
|
||||
|
||||
<Title title="版本">
|
||||
<el-select v-model="formData.version" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in [{
|
||||
value: '3',
|
||||
label: 'V3'
|
||||
}, {
|
||||
value: '2',
|
||||
label: 'V2'
|
||||
}]"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</Title>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
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>
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<div class="mb-12px">
|
||||
<div class="flex text-[var(--el-text-color-primary)] justify-between items-center">
|
||||
<span>{{title}}</span>
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
<div class="text-[var(--el-text-color-secondary)] text-12px my-8px">
|
||||
{{desc}}
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'Index' })
|
||||
|
||||
defineProps({
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
desc: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<div class="flex h-1/1">
|
||||
<!-- 模式 -->
|
||||
<Mode class="flex-none" @generate-music="generateMusic"/>
|
||||
<!-- 音频列表 -->
|
||||
<List ref="listRef" class="flex-auto"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Mode from './components/mode/index.vue'
|
||||
import List from './components/list/index.vue'
|
||||
|
||||
defineOptions({ name: 'Index' })
|
||||
|
||||
const listRef = ref<{generateMusic: (...args) => void} | null>(null)
|
||||
|
||||
function generateMusic (args: {formData: Recordable}) {
|
||||
unref(listRef)?.generateMusic(args.formData)
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue