Merge remote-tracking branch 'yudao-ui-admin-vue3/dev' into dev

# Conflicts:
#	package.json
This commit is contained in:
hhhero
2024-07-25 20:42:26 +08:00
32 changed files with 769 additions and 452 deletions

View File

@ -0,0 +1,85 @@
<template>
<div class="square-container">
<el-input
v-model="searchText"
style="width: 100%; margin-bottom: 20px"
size="large"
placeholder="请输入要搜索的内容"
:suffix-icon="Search"
@keyup.enter="handleSearch"
/>
<div class="gallery">
<div v-for="item in publicList" :key="item.id" class="gallery-item">
<img :src="item.picUrl" class="img" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ImageApi, ImageVO } from '@/api/ai/image'
import { Search } from '@element-plus/icons-vue'
/** 属性 */
// TODO @fanqueryParams 里面搞分页哈。
const pageNo = ref<number>(1)
const pageSize = ref<number>(20)
const publicList = ref<ImageVO[]>([])
const searchText = ref<string>('')
/** 获取数据 */
const getListData = async () => {
const res = await ImageApi.getImagePagePublic({
pageNo: pageNo.value,
pageSize: pageSize.value,
prompt: searchText.value
})
publicList.value = res.list as ImageVO[]
}
/** 搜索 */
const handleSearch = async () => {
await getListData()
}
onMounted(async () => {
await getListData()
})
</script>
<style scoped lang="scss">
.square-container {
background-color: #fff;
padding: 20px;
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
//max-width: 1000px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.gallery-item {
position: relative;
overflow: hidden;
background: #f0f0f0;
cursor: pointer;
transition: transform 0.3s;
}
.gallery-item img {
width: 100%;
height: auto;
display: block;
transition: transform 0.3s;
}
.gallery-item:hover img {
transform: scale(1.1);
}
.gallery-item:hover {
transform: scale(1.05);
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="flex h-1/1">
<div class="flex h-full items-stretch">
<!-- 模式 -->
<Mode class="flex-none" @generate-music="generateMusic"/>
<!-- 音频列表 -->
@ -13,8 +13,13 @@ import List from './list/index.vue'
defineOptions({ name: 'Index' })
const listRef = ref<{generateMusic: (...args) => void} | null>(null)
const listRef = ref<Nullable<{generateMusic: (...args) => void}>>(null)
/*
*@Description: 拿到左侧配置信息调用右侧音乐生成的方法
*@MethodAuthor: xiaohong
*@Date: 2024-07-19 11:13:38
*/
function generateMusic (args: {formData: Recordable}) {
unref(listRef)?.generateMusic(args.formData)
}

View File

@ -1,9 +1,70 @@
<template>
<div class="h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">播放器</div>
<div class="flex items-center justify-between px-2 h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">
<!-- 歌曲信息 -->
<div class="flex gap-[10px]">
<el-image src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" class="w-[45px]"/>
<div>
<div>{{currentSong.name}}</div>
<div class="text-[12px] text-gray-400">{{currentSong.singer}}</div>
</div>
</div>
<!-- 音频controls -->
<div class="flex gap-[12px] items-center">
<Icon icon="majesticons:back-circle" :size="20" class="text-gray-300 cursor-pointer"/>
<Icon :icon="audioProps.paused ? 'mdi:arrow-right-drop-circle' : 'solar:pause-circle-bold'" :size="30" class=" cursor-pointer" @click="toggleStatus('paused')"/>
<Icon icon="majesticons:next-circle" :size="20" class="text-gray-300 cursor-pointer"/>
<div class="flex gap-[16px] items-center">
<span>{{audioProps.currentTime}}</span>
<el-slider v-model="audioProps.duration" color="#409eff" class="w-[160px!important] "/>
<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 gap-[16px] items-center">
<Icon :icon="audioProps.muted ? 'tabler:volume-off' : 'tabler:volume'" :size="20" class="cursor-pointer" @click="toggleStatus('muted')"/>
<el-slider v-model="audioProps.volume" color="#409eff" class="w-[160px!important] "/>
</div>
</div>
</template>
<script lang="ts" setup>
import { formatPast } from '@/utils/formatTime'
import audioUrl from '@/assets/audio/response.mp3'
defineOptions({ name: 'Index' })
const currentSong = inject('currentSong', {})
const audioRef = ref<Nullable<HTMLElement>>(null)
// 音频相关属性https://www.runoob.com/tags/ref-av-dom.html
const audioProps = reactive({
autoplay: true,
paused: false,
currentTime: '00:00',
duration: '00:00',
muted: false,
volume: 50,
})
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) {
audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss')
}
</script>

View File

@ -1,29 +1,29 @@
<template>
<div class="flex flex-col h-full">
<div class="flex flex-col">
<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-tab-pane v-loading="loading" label="我的创作" 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"/>
<songCard :songInfo="song" @play="setCurrentSong(song)"/>
</el-col>
</el-row>
<el-empty v-else description="暂无音乐"/>
</el-tab-pane>
<!-- 试听广场 -->
<el-tab-pane label="试听广场" v-loading="loading" name="square">
<el-tab-pane v-loading="loading" label="试听广场" 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"/>
<songCard :songInfo="song" @play="setCurrentSong(song)"/>
</el-col>
</el-row>
<el-empty v-else description="暂无音乐"/>
</el-tab-pane>
</el-tabs>
<!-- songInfo -->
<songInfo v-bind="squareSongList[0]" class="flex-none"/>
<songInfo class="flex-none"/>
</div>
<audioBar class="flex-none"/>
</div>
@ -36,13 +36,18 @@ import audioBar from './audioBar/index.vue'
defineOptions({ name: 'Index' })
const currentType = ref('mine')
// loading 状态
const loading = ref(false)
// 当前音乐
const currentSong = ref({})
const mySongList = ref<Recordable[]>([])
const squareSongList = ref<Recordable[]>([])
provide('currentSong', currentSong)
/*
*@Description: 调接口生成音乐列表
*@MethodAuthor: xiaohong
@ -57,7 +62,7 @@ function generateMusic (formData: Recordable) {
id: index,
audioUrl: '',
videoUrl: '',
title: '我走后',
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',
@ -76,6 +81,15 @@ function generateMusic (formData: Recordable) {
}, 3000)
}
/*
*@Description: 设置当前播放的音乐
*@MethodAuthor: xiaohong
*@Date: 2024-07-19 11:22:33
*/
function setCurrentSong (music: Recordable) {
currentSong.value = music
}
defineExpose({
generateMusic
})
@ -86,9 +100,9 @@ defineExpose({
:deep(.el-tabs) {
display: flex;
flex-direction: column;
.el-tabs__content{
padding: 0 7px;
overflow: auto;
}
.el-tabs__content {
padding: 0 7px;
overflow: auto;
}
}
</style>

View File

@ -1,10 +1,15 @@
<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="relative" @click="playSong">
<el-image :src="songInfo.imageUrl" class="flex-none w-80px"/>
<div class="bg-black bg-op-40 absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer">
<Icon :icon="currentSong.id === songInfo.id ? 'solar:pause-circle-bold':'mdi:arrow-right-drop-circle'" :size="30" />
</div>
</div>
<div class="ml-8px">
<div>{{ title }}</div>
<div>{{ songInfo.title }}</div>
<div class="mt-8px text-12px text-[var(--el-text-color-secondary)] line-clamp-2">
{{ desc }}
{{ songInfo.desc }}
</div>
</div>
</div>
@ -15,15 +20,17 @@
defineOptions({ name: 'Index' })
defineProps({
imageUrl: {
type: String
},
title: {
type: String
},
desc: {
type: String
songInfo: {
type: Object,
default: () => ({})
}
})
const emits = defineEmits(['play'])
const currentSong = inject('currentSong', {})
function playSong () {
emits('play')
}
</script>

View File

@ -1,11 +1,15 @@
<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-image :src="currentSong.imageUrl"/>
<div class="">{{ currentSong.title }}</div>
<div class="text-[var(--el-text-color-secondary)] text-12px line-clamp-1">
{{ currentSong.desc }}
</div>
<div class="text-[var(--el-text-color-secondary)] text-12px">
{{ currentSong.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>
<div class="text-[var(--el-text-color-secondary)] text-12px" v-html="currentSong.lyric"></div>
</ContentWrap>
</template>
@ -13,21 +17,6 @@
defineOptions({ name: 'Index' })
defineProps({
imageUrl: {
type: String
},
title: {
type: String
},
desc: {
type: String
},
date: {
type: String
},
lyric: {
type: String
}
})
const currentSong = inject('currentSong', {})
</script>

View File

@ -1,5 +1,5 @@
<template>
<ContentWrap class="w-300px h-full">
<ContentWrap class="w-300px h-full mb-[0!important]">
<el-radio-group v-model="generateMode" class="mb-15px">
<el-radio-button label="desc">
描述模式
@ -28,10 +28,7 @@ const emits = defineEmits(['generate-music'])
const generateMode = ref('lyric')
interface ModeRef {
formData: Recordable
}
const modeRef = ref<ModeRef | null>(null)
const modeRef = ref<Nullable<{ formData: Recordable }>>(null)
/*
*@Description: 根据信息生成音乐
@ -39,6 +36,6 @@ const modeRef = ref<ModeRef | null>(null)
*@Date: 2024-06-27 16:40:16
*/
function generateMusic () {
emits('generate-music', {formData: unref(modeRef)?.formData.value})
emits('generate-music', {formData: unref(modeRef)?.formData})
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<el-card class="my-card h-full">
<template #header
><h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
<template #header>
<h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
<span>预览</span>
<!-- 展示在右上角 -->
<el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small">
@ -10,8 +10,8 @@
</template>
复制
</el-button>
</h3></template
>
</h3>
</template>
<div ref="contentRef" class="hide-scroll-bar h-full box-border overflow-y-auto">
<div class="w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7">
@ -105,7 +105,7 @@ watch(copied, (val) => {
}
}
.my-card{
.my-card {
display: flex;
flex-direction: column;

View File

@ -1,3 +1,4 @@
<!-- 标签选项 -->
<template>
<div class="flex flex-wrap gap-[8px]">
<span

View File

@ -20,7 +20,7 @@
<script setup lang="ts">
import Left from './components/Left.vue'
import Right from './components/Right.vue'
import { WriteApi } from '@/api/ai/write'
import { WriteApi, WriteVO } from '@/api/ai/write'
import { WriteExample } from '@/views/ai/utils/constants'
const message = useMessage()
@ -37,7 +37,7 @@ const stopStream = () => {
/** 执行写作 */
const rightRef = ref<InstanceType<typeof Right>>()
const submit = (data) => {
const submit = (data: WriteVO) => {
abortController.value = new AbortController()
writeResult.value = ''
isWriting.value = true