feat:优化音源配置

This commit is contained in:
alger
2025-12-20 02:30:09 +08:00
parent 0f42bfc6cb
commit 85302c611a
4 changed files with 547 additions and 507 deletions

View File

@@ -119,6 +119,33 @@ export default {
imported: 'Custom Source Imported',
notImported: 'Not Imported'
}
},
lxMusic: {
tabs: {
sources: 'Source Selection',
lxMusic: 'LX Music',
customApi: 'Custom API'
},
scripts: {
title: 'Imported Scripts',
importLocal: 'Import Local',
importOnline: 'Import Online',
urlPlaceholder: 'Enter LX Music Script URL',
importBtn: 'Import',
empty: 'No imported LX Music scripts',
notConfigured: 'Not configured (Configure in LX Music Tab)',
importHint: 'Import compatible custom API plugins to extend sources',
noScriptWarning: 'Please import LX Music script first',
noSelectionWarning: 'Please select an LX Music source first',
notFound: 'Source not found',
switched: 'Switched to source: {name}',
deleted: 'Deleted source: {name}',
enterUrl: 'Please enter script URL',
invalidUrl: 'Invalid URL format',
invalidScript: 'Invalid LX Music script, globalThis.lx code not found',
nameRequired: 'Name cannot be empty',
renameSuccess: 'Rename successful'
}
}
},
application: {

View File

@@ -116,6 +116,33 @@ export default {
imported: '已导入自定义音源',
notImported: '未导入'
}
},
lxMusic: {
tabs: {
sources: '音源选择',
lxMusic: '落雪音源',
customApi: '自定义API'
},
scripts: {
title: '已导入的音源脚本',
importLocal: '本地导入',
importOnline: '在线导入',
urlPlaceholder: '输入落雪音源脚本 URL',
importBtn: '导入',
empty: '暂无已导入的落雪音源',
notConfigured: '未配置 (请去落雪音源Tab配置)',
importHint: '导入兼容的自定义 API 插件以扩展音源',
noScriptWarning: '请先导入落雪音源脚本',
noSelectionWarning: '请先选择一个落雪音源',
notFound: '音源不存在',
switched: '已切换到音源: {name}',
deleted: '已删除音源: {name}',
enterUrl: '请输入脚本 URL',
invalidUrl: '无效的 URL 格式',
invalidScript: '无效的落雪音源脚本,未找到 globalThis.lx 相关代码',
nameRequired: '名称不能为空',
renameSuccess: '重命名成功'
}
}
},
application: {

View File

@@ -0,0 +1,147 @@
<template>
<Teleport to="body">
<Transition name="fade">
<div
v-if="show"
class="fixed inset-0 z-[1000] flex items-center justify-center md:items-center items-end"
@click="handleMaskClick"
>
<!-- Overlay -->
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity"></div>
<!-- Content -->
<Transition :name="isMobile ? 'slide-up' : 'scale-fade'">
<div
v-if="show"
class="relative z-10 w-full bg-white dark:bg-[#1c1c1e] shadow-2xl overflow-hidden flex flex-col max-h-[85vh]"
:class="[
isMobile
? 'rounded-t-[20px] pb-safe'
: 'md:max-w-[720px] md:rounded-2xl'
]"
@click.stop
>
<!-- Header -->
<div
class="flex items-center justify-between px-4 py-3 border-b border-gray-100 dark:border-white/5 shrink-0"
>
<h3 class="text-[15px] font-semibold text-gray-900 dark:text-white truncate">
{{ title }}
</h3>
<button
class="p-1 -mr-1 rounded-full text-gray-400 hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"
@click="close"
>
<i class="ri-close-line text-lg"></i>
</button>
</div>
<!-- Body -->
<div class="flex-1 overflow-y-auto overscroll-contain px-4 py-3">
<slot></slot>
</div>
<!-- Footer -->
<div
v-if="$slots.footer"
class="px-4 py-3 border-t border-gray-100 dark:border-white/5 shrink-0 bg-gray-50/50 dark:bg-white/5 backdrop-blur-xl"
>
<slot name="footer"></slot>
</div>
</div>
</Transition>
</div>
</Transition>
</Teleport>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
const props = defineProps<{
modelValue: boolean;
title?: string;
}>();
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void;
(e: 'close'): void;
}>();
const show = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
});
const isMobile = ref(false);
const checkMobile = () => {
isMobile.value = window.innerWidth < 768;
};
const close = () => {
show.value = false;
emit('close');
};
const handleMaskClick = () => {
close();
};
onMounted(() => {
checkMobile();
window.addEventListener('resize', checkMobile);
});
onUnmounted(() => {
window.removeEventListener('resize', checkMobile);
});
// Prevent body scroll when modal is open
watch(show, (val) => {
if (val) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
});
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* PC Scale Fade Transition */
.scale-fade-enter-active,
.scale-fade-leave-active {
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.scale-fade-enter-from,
.scale-fade-leave-to {
opacity: 0;
transform: scale(0.95);
}
/* Mobile Slide Up Transition */
.slide-up-enter-active,
.slide-up-leave-active {
transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}
.slide-up-enter-from,
.slide-up-leave-to {
transform: translateY(100%);
}
.pb-safe {
padding-bottom: env(safe-area-inset-bottom);
}
</style>

File diff suppressed because it is too large Load Diff