基于旧项目 ai_music_v2.0 迁移,与 Painting/Video 统一架构:HTTP 轮询 + suanli 后端、 API 驱动配置、mode 独立 ref 驱动控件显隐。新增 AudioPlayer/CustomSlider 通用组件, dialogBox/set.vue/taskPolling/modelApi 完成集成适配。
146 lines
4.2 KiB
Vue
146 lines
4.2 KiB
Vue
<template>
|
|
<div class="custom-popover-wrapper">
|
|
<div class="choice-btn" :class="{ active: showPopover }" @click.stop="togglePopover">
|
|
<img :src="currentIcon" alt="" style="width: 16px;">
|
|
<span>{{ currentLabel }}</span>
|
|
</div>
|
|
<Transition name="popover">
|
|
<div v-show="showPopover" class="custom-popover" @click.stop>
|
|
<div class="select">
|
|
<div class="model-group">
|
|
<div
|
|
v-for="item in modeOptions"
|
|
:key="item.value"
|
|
class="model-item"
|
|
:class="{ active: modeValue === item.value, disabled: item.disabled }"
|
|
@click="selectMode(item)"
|
|
>
|
|
<img :src="item.icon" alt="" style="width: 16px; height: 16px;">
|
|
<span>{{ item.label }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, ref } from 'vue'
|
|
import commonModeIcon from '@/assets/dialog/commonMode.svg'
|
|
import professionalModeIcon from '@/assets/dialog/professionalMode.svg'
|
|
import remixModeIcon from '@/assets/dialog/remixMode.svg'
|
|
import editModeIcon from '@/assets/dialog/editMode.svg'
|
|
|
|
const props = defineProps({
|
|
modelValue: { type: String, default: '' },
|
|
options: { type: Array, default: () => [] }
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
const showPopover = ref(false)
|
|
|
|
const modeValue = computed({
|
|
get: () => props.modelValue,
|
|
set: (v) => emit('update:modelValue', v)
|
|
})
|
|
|
|
const iconMap = {
|
|
'常用模式': commonModeIcon,
|
|
'专业模式': professionalModeIcon,
|
|
'Remix模式': remixModeIcon,
|
|
'编辑模式': editModeIcon
|
|
}
|
|
|
|
const modeOptions = computed(() => {
|
|
if (props.options.length) return props.options.map(o => ({ ...o, icon: iconMap[o.label] || commonModeIcon }))
|
|
return [
|
|
{ value: '常用模式', label: '常用模式', icon: commonModeIcon },
|
|
{ value: '专业模式', label: '专业模式', icon: professionalModeIcon },
|
|
{ value: 'Remix模式', label: 'Remix模式', icon: remixModeIcon, disabled: true },
|
|
{ value: '编辑模式', label: '编辑模式', icon: editModeIcon, disabled: true }
|
|
]
|
|
})
|
|
|
|
const currentIcon = computed(() => {
|
|
const found = modeOptions.value.find(m => m.value === modeValue.value)
|
|
return found?.icon || commonModeIcon
|
|
})
|
|
|
|
const currentLabel = computed(() => {
|
|
const found = modeOptions.value.find(m => m.value === modeValue.value)
|
|
return found?.label || modeValue.value
|
|
})
|
|
|
|
const togglePopover = () => { showPopover.value = !showPopover.value }
|
|
const selectMode = (item) => {
|
|
if (item.disabled) return
|
|
modeValue.value = item.value
|
|
showPopover.value = false
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.choice-btn {
|
|
display: flex;
|
|
height: 40px;
|
|
padding: 0 15px;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 5px;
|
|
border-radius: 10px;
|
|
border: 1px solid #E9EAEB;
|
|
background: #f5f6f7;
|
|
cursor: pointer;
|
|
position: relative;
|
|
&:hover, &.active { background: #E9EAEB; }
|
|
img { filter: brightness(0) drop-shadow(0 0 0 #000F33); }
|
|
}
|
|
|
|
.custom-popover-wrapper { position: relative; display: inline-block; }
|
|
|
|
.custom-popover {
|
|
position: absolute;
|
|
bottom: calc(100% + 8px);
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: auto;
|
|
background: #ffffff;
|
|
border: 1px solid rgba(0, 0, 0, 0.10);
|
|
border-radius: 20px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
z-index: 9999;
|
|
}
|
|
|
|
.select { padding: 20px 10px; max-height: 510px; overflow-y: auto; }
|
|
.model-group { margin-bottom: 15px; &:last-child { margin-bottom: 0; } }
|
|
|
|
.model-item {
|
|
padding: 8px 10px;
|
|
border-radius: 10px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
margin-bottom: 4px;
|
|
transition: all 0.2s ease;
|
|
color: #666;
|
|
font-family: "Microsoft YaHei";
|
|
white-space: nowrap;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
&:last-child { margin-bottom: 0; }
|
|
&:hover:not(.disabled) { background: #f5f6f7; }
|
|
&.active {
|
|
background: rgba(0, 15, 51, 0.10);
|
|
color: #333;
|
|
font-weight: 500;
|
|
img { filter: brightness(0) drop-shadow(0 0 0 #000F33); }
|
|
}
|
|
&.disabled { cursor: not-allowed; opacity: 0.5; }
|
|
}
|
|
|
|
.popover-enter-active, .popover-leave-active { transition: all 0.3s ease; }
|
|
.popover-enter-from, .popover-leave-to { opacity: 0; transform: translateX(-50%) translateY(10px); }
|
|
</style>
|