feat(admin-ui): 管理后台整体切换为浅色主题(ModernLight) + 新增 lightbulb 图标
1. layout(管理控制台 dashboard.vue): main bg-[#09090b] text-zinc-100 → bg-[#f8fafc] text-zinc-900; header bg-zinc-950/60 border-zinc-800 → bg-white/80 border-zinc-200; 移动菜单按钮/新手引导按钮 text-zinc-300/400 hover:bg-zinc-800 → text-zinc-600 hover:bg-zinc-100 border-zinc-200
2. Admin/Sidebar.vue: bg-[#09090b] border-zinc-800 → bg-white border-zinc-200 + shadow-sm; 标题 text-zinc-100 → 900; activeTab bg-blue-600/10 text-blue-400 → bg-blue-50 text-blue-700 border-blue-200; 未选中 text-zinc-500 hover:text-zinc-200 hover:bg-zinc-800/40 → text-zinc-600 hover:text-zinc-900 hover:bg-zinc-100; 用户卡 avatar/首字母 bg-white/70 border-zinc-200 + 退出按钮 hover:bg-red-50 text-red-600
3. 批量 33 个 Admin 组件硬编码深色原子类 1414 处替换(bg-zinc-9xx / border-zinc-8xx / text-zinc-100,200,300 / shadow-black/ -> 对应 light 版本) + style 块原生 CSS rgba(255,255,255,x) 16 处改为 rgba(0,0,0,x) / var 浅色主题 + 硬编码 bg-[#0c0c0e] / border-white/10 / color:white / surface-primary fallback 对应浅色修正
4. 二轮批量追加: bg-zinc-950 + opacity 变体(80/60/50/40/30/20/10) 共 431 处 → bg-white 对应 opacity 版本;placeholder:text-zinc-800 → 500;text-blue/red/emerald/amber/rose/purple 400 版 → 700/600 深浅一致版;border color /500 /30 → 200;focus:ring /20 → /30;shadow color 阴影降低强度
5. hover:text-white 扫尾: 13 处 DataAnalysisPanel/DatabaseManager/NotificationSender/SemesterManager/SongManagement/UserSongsModal hover 文本从 white → zinc-900;按钮三态从 hover:bg-*600 白字 → hover:bg-*-100/200 对应深字色,和前台 indigo/浅色体系保持一致
6. BlacklistManager 前台可见提示 💡→ <Icon lightbulb amber-600 size=13>;ChangelogManager 徽章色从 emerald-300/blue-300/zinc-400 → emerald-700/blue-700/zinc-600 可读;Icon.vue 新增 lightbulb 分支 stroke 图标
7. 受影响文件: dashboard.vue + Sidebar.vue + Icon.vue + Admin/* (33个组件共 1874 处批量替换 + 20 处手工精细化修正)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="relative bg-zinc-900/50 border border-zinc-800/50 rounded-xl p-1 overflow-hidden">
|
||||
<div class="relative bg-zinc-50/50 border border-zinc-200 rounded-xl p-1 overflow-hidden">
|
||||
<!-- 简化版日期选择显示 -->
|
||||
<div class="flex items-center justify-between px-2.5 py-1.5" @click="toggleCalendar" style="cursor: pointer">
|
||||
<div class="flex items-center gap-2.5">
|
||||
@@ -8,18 +8,18 @@
|
||||
<span class="text-base font-black leading-none my-0">{{ getDay(modelValue) }}</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs font-bold text-zinc-200">{{ modelValue }}</span>
|
||||
<span class="text-xs font-bold text-zinc-700">{{ modelValue }}</span>
|
||||
<span class="text-[10px] text-zinc-500 font-medium leading-tight">{{ isToday(modelValue) ? '今天' : '' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center border-l border-zinc-800 pl-2">
|
||||
<button class="p-1.5 text-zinc-500 hover:text-emerald-400 transition-colors" title="跳转到今天" @click.stop="scrollToToday">
|
||||
<div class="flex items-center border-l border-zinc-200 pl-2">
|
||||
<button class="p-1.5 text-zinc-500 hover:text-emerald-700 transition-colors" title="跳转到今天" @click.stop="scrollToToday">
|
||||
<CircleDot class="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-blue-400 transition-colors"
|
||||
:class="{ 'text-blue-400': showCalendar }"
|
||||
class="p-1.5 text-zinc-500 hover:text-blue-700 transition-colors"
|
||||
:class="{ 'text-blue-700': showCalendar }"
|
||||
title="打开月历"
|
||||
>
|
||||
<CalendarIcon class="w-4 h-4" />
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<!-- 快捷选择区 + 月历网格 -->
|
||||
<Transition name="expand">
|
||||
<div v-if="showCalendar" class="border-t border-zinc-800/60 mt-1 pt-2 px-2 pb-2">
|
||||
<div v-if="showCalendar" class="border-t border-zinc-200/60 mt-1 pt-2 px-2 pb-2">
|
||||
<!-- 快捷按钮 -->
|
||||
<div class="flex flex-wrap gap-2 mb-3">
|
||||
<button
|
||||
@@ -39,7 +39,7 @@
|
||||
'px-3 py-1.5 rounded-lg text-[11px] font-bold transition-all border',
|
||||
modelValue === q.value
|
||||
? 'bg-blue-600 border-blue-500 text-white'
|
||||
: 'bg-zinc-900 border-zinc-800 text-zinc-400 hover:text-zinc-200 hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 text-zinc-400 hover:text-zinc-700 hover:border-zinc-200'
|
||||
]"
|
||||
@click="handleQuickSelect(q.value)"
|
||||
>
|
||||
@@ -48,34 +48,34 @@
|
||||
</div>
|
||||
|
||||
<!-- 月历网格 -->
|
||||
<div class="bg-zinc-950/60 border border-zinc-800 rounded-xl p-3">
|
||||
<div class="bg-white/80 border border-zinc-200 rounded-xl p-3">
|
||||
<!-- 月份切换 -->
|
||||
<div class="flex items-center justify-between mb-3 px-1">
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-200 rounded-lg hover:bg-zinc-800 transition-colors"
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-700 rounded-lg hover:bg-zinc-100 transition-colors"
|
||||
@click="calendarMonth--"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="15,18 9,12 15,6" /></svg>
|
||||
</button>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-black text-zinc-200">{{ calendarYear }}年</span>
|
||||
<span class="text-sm font-black text-zinc-700">{{ calendarYear }}年</span>
|
||||
<select
|
||||
v-model="calendarMonth"
|
||||
class="bg-zinc-900 border border-zinc-800 rounded-lg px-2 py-1 text-sm font-black text-zinc-200 focus:outline-none focus:border-blue-500/50"
|
||||
class="bg-white border border-zinc-200 rounded-lg px-2 py-1 text-sm font-black text-zinc-700 focus:outline-none focus:border-blue-500/50"
|
||||
>
|
||||
<option v-for="m in 12" :key="m" :value="m - 1">{{ m }}月</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-200 rounded-lg hover:bg-zinc-800 transition-colors"
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-700 rounded-lg hover:bg-zinc-100 transition-colors"
|
||||
@click="goToday"
|
||||
title="回到本月"
|
||||
>
|
||||
<CircleDot class="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-200 rounded-lg hover:bg-zinc-800 transition-colors"
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-700 rounded-lg hover:bg-zinc-100 transition-colors"
|
||||
@click="calendarMonth++"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9,18 15,12 9,6" /></svg>
|
||||
@@ -104,9 +104,9 @@
|
||||
'relative aspect-square rounded-lg flex items-center justify-center text-sm font-bold transition-all',
|
||||
!cell.inMonth ? 'text-zinc-800 cursor-default' : '',
|
||||
cell.inMonth && modelValue !== cell.dateStr && !cell.isToday
|
||||
? 'text-zinc-400 hover:bg-zinc-800 hover:text-zinc-200' : '',
|
||||
? 'text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700' : '',
|
||||
cell.inMonth && modelValue !== cell.dateStr && cell.isToday
|
||||
? 'text-emerald-400 ring-1 ring-emerald-500/40 hover:bg-zinc-800' : '',
|
||||
? 'text-emerald-700 ring-1 ring-emerald-500/40 hover:bg-zinc-100' : '',
|
||||
modelValue === cell.dateStr
|
||||
? 'bg-blue-600 text-white shadow-lg shadow-blue-900/20' : ''
|
||||
]"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="relative bg-zinc-900/50 border border-zinc-800/50 rounded-xl p-1 overflow-hidden">
|
||||
<div class="relative bg-zinc-50/50 border border-zinc-200 rounded-xl p-1 overflow-hidden">
|
||||
<!-- 简化版多选日期显示 -->
|
||||
<div class="flex items-center justify-between px-2.5 py-1.5" @click="toggleCalendar" style="cursor: pointer">
|
||||
<div class="flex items-center gap-2.5 min-w-0 flex-1">
|
||||
@@ -10,7 +10,7 @@
|
||||
<span class="text-[10px] font-bold leading-none opacity-80">天</span>
|
||||
</div>
|
||||
<div class="flex flex-col min-w-0 flex-1">
|
||||
<span class="text-xs font-bold text-zinc-200 truncate">
|
||||
<span class="text-xs font-bold text-zinc-700 truncate">
|
||||
{{ modelValue.length ? modelValue.slice(0, 3).join('、') + (modelValue.length > 3 ? ` 等${modelValue.length}天` : '') : '未选择日期' }}
|
||||
</span>
|
||||
<span class="text-[10px] text-zinc-500 font-medium leading-tight truncate">
|
||||
@@ -19,13 +19,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center border-l border-zinc-800 pl-2 ml-2 shrink-0">
|
||||
<button class="p-1.5 text-zinc-500 hover:text-emerald-400 transition-colors" title="跳转到今天" @click.stop="scrollToToday">
|
||||
<div class="flex items-center border-l border-zinc-200 pl-2 ml-2 shrink-0">
|
||||
<button class="p-1.5 text-zinc-500 hover:text-emerald-700 transition-colors" title="跳转到今天" @click.stop="scrollToToday">
|
||||
<CircleDot class="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-blue-400 transition-colors"
|
||||
:class="{ 'text-blue-400': showCalendar }"
|
||||
class="p-1.5 text-zinc-500 hover:text-blue-700 transition-colors"
|
||||
:class="{ 'text-blue-700': showCalendar }"
|
||||
title="打开月历"
|
||||
>
|
||||
<CalendarIcon class="w-4 h-4" />
|
||||
@@ -35,19 +35,19 @@
|
||||
|
||||
<!-- 扩展面板:快捷批量 + 月历 -->
|
||||
<Transition name="expand">
|
||||
<div v-if="showCalendar" class="border-t border-zinc-800/60 mt-1 pt-2 px-2 pb-2 space-y-3">
|
||||
<div v-if="showCalendar" class="border-t border-zinc-200/60 mt-1 pt-2 px-2 pb-2 space-y-3">
|
||||
<!-- 批量操作按钮 -->
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest px-1">批量选择</span>
|
||||
<button
|
||||
v-for="b in batchActions"
|
||||
:key="b.label"
|
||||
class="px-2.5 py-1.5 rounded-lg text-[11px] font-bold bg-zinc-900 border border-zinc-800 text-zinc-400 hover:text-zinc-200 hover:border-zinc-700 transition-all"
|
||||
class="px-2.5 py-1.5 rounded-lg text-[11px] font-bold bg-white border border-zinc-200 text-zinc-400 hover:text-zinc-700 hover:border-zinc-200 transition-all"
|
||||
@click="applyBatchAction(b.action)"
|
||||
>
|
||||
{{ b.label }}
|
||||
</button>
|
||||
<div class="w-px h-4 bg-zinc-800 mx-1" />
|
||||
<div class="w-px h-4 bg-zinc-100 mx-1" />
|
||||
<button
|
||||
class="px-2.5 py-1.5 rounded-lg text-[11px] font-bold bg-red-500/10 border border-red-500/20 text-red-400 hover:text-red-300 hover:bg-red-500/15 transition-all"
|
||||
@click="clearAll"
|
||||
@@ -63,33 +63,33 @@
|
||||
</div>
|
||||
|
||||
<!-- 月历网格 -->
|
||||
<div class="bg-zinc-950/60 border border-zinc-800 rounded-xl p-3">
|
||||
<div class="bg-white/80 border border-zinc-200 rounded-xl p-3">
|
||||
<div class="flex items-center justify-between mb-3 px-1">
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-200 rounded-lg hover:bg-zinc-800 transition-colors"
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-700 rounded-lg hover:bg-zinc-100 transition-colors"
|
||||
@click="calendarMonth--"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="15,18 9,12 15,6" /></svg>
|
||||
</button>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-black text-zinc-200">{{ calendarYear }}年</span>
|
||||
<span class="text-sm font-black text-zinc-700">{{ calendarYear }}年</span>
|
||||
<select
|
||||
v-model="calendarMonth"
|
||||
class="bg-zinc-900 border border-zinc-800 rounded-lg px-2 py-1 text-sm font-black text-zinc-200 focus:outline-none focus:border-blue-500/50"
|
||||
class="bg-white border border-zinc-200 rounded-lg px-2 py-1 text-sm font-black text-zinc-700 focus:outline-none focus:border-blue-500/50"
|
||||
>
|
||||
<option v-for="m in 12" :key="m" :value="m - 1">{{ m }}月</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-200 rounded-lg hover:bg-zinc-800 transition-colors"
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-700 rounded-lg hover:bg-zinc-100 transition-colors"
|
||||
@click="goToday"
|
||||
title="回到本月"
|
||||
>
|
||||
<CircleDot class="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-200 rounded-lg hover:bg-zinc-800 transition-colors"
|
||||
class="p-1.5 text-zinc-500 hover:text-zinc-700 rounded-lg hover:bg-zinc-100 transition-colors"
|
||||
@click="calendarMonth++"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9,18 15,12 9,6" /></svg>
|
||||
@@ -116,9 +116,9 @@
|
||||
'relative aspect-square rounded-lg flex items-center justify-center text-sm font-bold transition-all',
|
||||
!cell.inMonth ? 'text-zinc-800 cursor-default' : '',
|
||||
cell.inMonth && !isSelected(cell.dateStr) && !cell.isToday
|
||||
? 'text-zinc-400 hover:bg-zinc-800 hover:text-zinc-200' : '',
|
||||
? 'text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700' : '',
|
||||
cell.inMonth && !isSelected(cell.dateStr) && cell.isToday
|
||||
? 'text-emerald-400 ring-1 ring-emerald-500/40 hover:bg-zinc-800' : '',
|
||||
? 'text-emerald-700 ring-1 ring-emerald-500/40 hover:bg-zinc-100' : '',
|
||||
isSelected(cell.dateStr)
|
||||
? 'bg-blue-600 text-white shadow-lg shadow-blue-900/20' : '',
|
||||
cell.isRangeMiddle && !isSelected(cell.dateStr)
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
class="fixed inset-0 z-[70] bg-black/70 backdrop-blur-sm px-4 py-6 flex items-end sm:items-center justify-center"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-xl rounded-2xl border border-zinc-800 bg-zinc-950 shadow-2xl overflow-hidden"
|
||||
class="w-full max-w-xl rounded-2xl border border-zinc-200 bg-white shadow-2xl overflow-hidden"
|
||||
>
|
||||
<div class="px-5 py-4 border-b border-zinc-800 flex items-center justify-between gap-3">
|
||||
<div class="px-5 py-4 border-b border-zinc-200 flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-[10px] uppercase tracking-[0.2em] text-zinc-500 font-black">后台引导</p>
|
||||
<h3 class="text-lg font-black text-zinc-100 mt-1">{{ currentStep.title }}</h3>
|
||||
<h3 class="text-lg font-black text-zinc-900 mt-1">{{ currentStep.title }}</h3>
|
||||
</div>
|
||||
<button
|
||||
class="h-9 w-9 rounded-lg border border-zinc-700 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-900 transition-colors"
|
||||
class="h-9 w-9 rounded-lg border border-zinc-200 text-zinc-400 hover:text-zinc-700 hover:bg-white transition-colors"
|
||||
@click="handleClose"
|
||||
>
|
||||
<X :size="16" class="mx-auto" />
|
||||
@@ -21,29 +21,29 @@
|
||||
</div>
|
||||
|
||||
<div class="px-5 py-5 space-y-4">
|
||||
<p class="text-sm leading-relaxed text-zinc-300">{{ currentStep.description }}</p>
|
||||
<div class="rounded-xl border border-blue-500/20 bg-blue-500/5 p-3">
|
||||
<p class="text-sm leading-relaxed text-zinc-600">{{ currentStep.description }}</p>
|
||||
<div class="rounded-xl border border-blue-200 bg-blue-500/5 p-3">
|
||||
<p class="text-xs text-zinc-400">
|
||||
建议操作:
|
||||
<span class="text-zinc-200 font-semibold">{{ currentStep.actionHint }}</span>
|
||||
<span class="text-zinc-700 font-semibold">{{ currentStep.actionHint }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="px-5 py-4 border-t border-zinc-800 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"
|
||||
class="px-5 py-4 border-t border-zinc-200 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"
|
||||
>
|
||||
<p class="text-xs text-zinc-500 font-semibold">{{ stepIndex + 1 }} / {{ steps.length }}</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
v-if="stepIndex > 0"
|
||||
class="px-3.5 py-2 rounded-lg border border-zinc-700 text-zinc-300 hover:bg-zinc-900 text-sm"
|
||||
class="px-3.5 py-2 rounded-lg border border-zinc-200 text-zinc-600 hover:bg-white text-sm"
|
||||
@click="prevStep"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
class="px-3.5 py-2 rounded-lg border border-zinc-700 text-zinc-200 hover:bg-zinc-900 text-sm"
|
||||
class="px-3.5 py-2 rounded-lg border border-zinc-200 text-zinc-700 hover:bg-white text-sm"
|
||||
@click="goCurrentStep"
|
||||
>
|
||||
前往此功能
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 头部区域 -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">API密钥管理</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">API密钥管理</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1">管理开放API的访问密钥,控制第三方应用的访问权限</p>
|
||||
</div>
|
||||
<button
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 过滤器栏 -->
|
||||
<div
|
||||
class="bg-zinc-900/40 border border-zinc-800/60 rounded-2xl p-3 flex flex-col lg:flex-row gap-3 items-center"
|
||||
class="bg-zinc-50/40 border border-zinc-200/60 rounded-2xl p-3 flex flex-col lg:flex-row gap-3 items-center"
|
||||
>
|
||||
<div class="relative flex-1 w-full">
|
||||
<Search class="absolute left-3 top-1/2 -translate-y-1/2 text-zinc-700" :size="16" />
|
||||
@@ -24,7 +24,7 @@
|
||||
v-model="filters.search"
|
||||
type="text"
|
||||
placeholder="搜索API密钥名称或描述..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800/80 rounded-xl pl-10 pr-4 py-2.5 text-xs focus:outline-none focus:border-blue-500/30 transition-all placeholder:text-zinc-800 text-zinc-200"
|
||||
class="w-full bg-white border border-zinc-200/80 rounded-xl pl-10 pr-4 py-2.5 text-xs focus:outline-none focus:border-blue-200 transition-all placeholder:text-zinc-500 text-zinc-700"
|
||||
@input="debouncedSearch"
|
||||
>
|
||||
</div>
|
||||
@@ -42,12 +42,12 @@
|
||||
v-model="filters.createdBy"
|
||||
type="text"
|
||||
placeholder="创建者用户名"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl pl-9 pr-4 py-2.5 text-xs focus:outline-none text-zinc-400 placeholder:text-zinc-800"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl pl-9 pr-4 py-2.5 text-xs focus:outline-none text-zinc-400 placeholder:text-zinc-500"
|
||||
@input="debouncedSearch"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="p-2.5 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-600 hover:text-blue-400 transition-all"
|
||||
class="p-2.5 bg-white border border-zinc-200 rounded-xl text-zinc-600 hover:text-blue-700 transition-all"
|
||||
@click="loadApiKeys"
|
||||
>
|
||||
<RefreshCw :size="14" :class="{ 'animate-spin': loading }" />
|
||||
@@ -69,16 +69,16 @@
|
||||
class="flex flex-col items-center justify-center py-20 px-4 text-center"
|
||||
>
|
||||
<div
|
||||
class="w-20 h-20 rounded-3xl bg-zinc-900 border border-zinc-800 flex items-center justify-center mb-6 text-zinc-700 shadow-xl"
|
||||
class="w-20 h-20 rounded-3xl bg-white border border-zinc-200 flex items-center justify-center mb-6 text-zinc-700 shadow-xl"
|
||||
>
|
||||
<Key :size="32" :stroke-width="1.5" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-zinc-200">暂无API密钥</h3>
|
||||
<h3 class="text-lg font-bold text-zinc-700">暂无API密钥</h3>
|
||||
<p class="text-xs text-zinc-500 mt-2 max-w-xs leading-relaxed">
|
||||
您还没有创建任何访问密钥。创建密钥后,您可以安全地将 VoiceHub 集成到第三方应用程序中。
|
||||
</p>
|
||||
<button
|
||||
class="mt-8 flex items-center gap-2 px-6 py-3 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-300 text-xs font-bold rounded-2xl transition-all"
|
||||
class="mt-8 flex items-center gap-2 px-6 py-3 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-600 text-xs font-bold rounded-2xl transition-all"
|
||||
@click="openCreateModal"
|
||||
>
|
||||
<Plus :size="16" /> 创建您的第一个密钥
|
||||
@@ -89,12 +89,12 @@
|
||||
<div
|
||||
v-for="apiKey in apiKeys"
|
||||
:key="apiKey.id"
|
||||
class="bg-zinc-900/30 border border-zinc-800/60 rounded-2xl p-6 group hover:border-zinc-700 transition-all relative overflow-hidden"
|
||||
class="bg-zinc-50/30 border border-zinc-200/60 rounded-2xl p-6 group hover:border-zinc-200 transition-all relative overflow-hidden"
|
||||
>
|
||||
<div class="flex items-start justify-between relative z-10">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center gap-3">
|
||||
<h4 class="text-sm font-black text-zinc-100 uppercase tracking-widest">
|
||||
<h4 class="text-sm font-black text-zinc-900 uppercase tracking-widest">
|
||||
{{ apiKey.name }}
|
||||
</h4>
|
||||
<span
|
||||
@@ -104,7 +104,7 @@
|
||||
>
|
||||
<span
|
||||
v-else-if="apiKey.status === 'inactive'"
|
||||
class="inline-flex items-center gap-1.5 px-2 py-0.5 bg-zinc-800 text-zinc-500 text-[10px] font-black rounded uppercase border border-zinc-700/50"
|
||||
class="inline-flex items-center gap-1.5 px-2 py-0.5 bg-zinc-100 text-zinc-500 text-[10px] font-black rounded uppercase border border-zinc-200"
|
||||
>非活跃</span
|
||||
>
|
||||
<span
|
||||
@@ -119,13 +119,13 @@
|
||||
class="flex items-center gap-1 opacity-100 lg:opacity-0 group-hover:opacity-100 transition-all"
|
||||
>
|
||||
<button
|
||||
class="p-2 text-zinc-500 hover:text-blue-400 transition-colors"
|
||||
class="p-2 text-zinc-500 hover:text-blue-700 transition-colors"
|
||||
@click="viewApiKey(apiKey)"
|
||||
>
|
||||
<Eye :size="14" />
|
||||
</button>
|
||||
<button
|
||||
class="p-2 text-zinc-500 hover:text-amber-400 transition-colors"
|
||||
class="p-2 text-zinc-500 hover:text-amber-700 transition-colors"
|
||||
@click="editApiKey(apiKey)"
|
||||
>
|
||||
<Edit2 :size="14" />
|
||||
@@ -139,7 +139,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 grid grid-cols-2 gap-4 border-t border-zinc-800/40 pt-6 relative z-10">
|
||||
<div class="mt-8 grid grid-cols-2 gap-4 border-t border-zinc-200 pt-6 relative z-10">
|
||||
<div class="space-y-0.5">
|
||||
<span class="text-[9px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
>创建者</span
|
||||
@@ -162,7 +162,7 @@
|
||||
<span
|
||||
v-for="perm in apiKey.permissions"
|
||||
:key="perm"
|
||||
class="text-[9px] font-mono bg-zinc-950 px-1.5 py-0.5 rounded text-zinc-500 border border-zinc-800/50"
|
||||
class="text-[9px] font-mono bg-white px-1.5 py-0.5 rounded text-zinc-500 border border-zinc-200"
|
||||
>
|
||||
{{ perm }}
|
||||
</span>
|
||||
@@ -197,14 +197,14 @@
|
||||
<!-- 创建/编辑模态框 -->
|
||||
<div
|
||||
v-if="showCreateModal || showEditModal"
|
||||
class="relative w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-3xl shadow-2xl overflow-hidden animate-in fade-in zoom-in duration-200"
|
||||
class="relative w-full max-w-lg bg-white border border-zinc-200 rounded-3xl shadow-2xl overflow-hidden animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div class="p-6 border-b border-zinc-800 flex items-center justify-between">
|
||||
<h3 class="text-lg font-black text-zinc-100 uppercase tracking-widest">
|
||||
<div class="p-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-lg font-black text-zinc-900 uppercase tracking-widest">
|
||||
{{ showCreateModal ? '创建API密钥' : '编辑API密钥' }}
|
||||
</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-200 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-700 transition-colors"
|
||||
@click="closeModals"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -220,7 +220,7 @@
|
||||
v-model="form.name"
|
||||
type="text"
|
||||
placeholder="输入API密钥名称"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-3 py-2.5 text-xs focus:outline-none focus:border-blue-500/30 text-zinc-200 placeholder:text-zinc-800"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-3 py-2.5 text-xs focus:outline-none focus:border-blue-200 text-zinc-700 placeholder:text-zinc-500"
|
||||
>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
@@ -230,7 +230,7 @@
|
||||
<textarea
|
||||
v-model="form.description"
|
||||
placeholder="输入API密钥描述 (可选)"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-3 py-2.5 text-xs focus:outline-none focus:border-blue-500/30 text-zinc-200 min-h-[80px] resize-none placeholder:text-zinc-800"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-3 py-2.5 text-xs focus:outline-none focus:border-blue-200 text-zinc-700 min-h-[80px] resize-none placeholder:text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
@@ -259,22 +259,22 @@
|
||||
<label
|
||||
v-for="perm in availablePermissions"
|
||||
:key="perm.value"
|
||||
class="flex items-start gap-3 p-3 bg-zinc-950 border border-zinc-800 rounded-xl cursor-pointer hover:border-zinc-600 transition-all"
|
||||
class="flex items-start gap-3 p-3 bg-white border border-zinc-200 rounded-xl cursor-pointer hover:border-zinc-300 transition-all"
|
||||
:class="{
|
||||
'border-blue-500/30 bg-blue-500/5': form.permissions.includes(perm.value)
|
||||
'border-blue-200 bg-blue-500/5': form.permissions.includes(perm.value)
|
||||
}"
|
||||
>
|
||||
<input
|
||||
v-model="form.permissions"
|
||||
type="checkbox"
|
||||
:value="perm.value"
|
||||
class="mt-1 w-3.5 h-3.5 rounded border-zinc-800 bg-zinc-900 accent-blue-600"
|
||||
class="mt-1 w-3.5 h-3.5 rounded border-zinc-200 bg-white accent-blue-600"
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
class="text-xs font-bold"
|
||||
:class="
|
||||
form.permissions.includes(perm.value) ? 'text-blue-400' : 'text-zinc-300'
|
||||
form.permissions.includes(perm.value) ? 'text-blue-700' : 'text-zinc-600'
|
||||
"
|
||||
>
|
||||
{{ perm.label }}
|
||||
@@ -291,18 +291,18 @@
|
||||
<input
|
||||
v-model="form.isActive"
|
||||
type="checkbox"
|
||||
class="w-3.5 h-3.5 rounded border-zinc-800 bg-zinc-900 accent-blue-600"
|
||||
class="w-3.5 h-3.5 rounded border-zinc-200 bg-white accent-blue-600"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-bold text-zinc-300 group-hover:text-blue-400 transition-colors"
|
||||
class="text-xs font-bold text-zinc-600 group-hover:text-blue-700 transition-colors"
|
||||
>启用此API密钥</span
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="p-6 border-t border-zinc-800 flex gap-2 justify-end">
|
||||
<div class="p-6 border-t border-zinc-200 flex gap-2 justify-end">
|
||||
<button
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-300"
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-600"
|
||||
@click="closeModals"
|
||||
>
|
||||
取消
|
||||
@@ -320,14 +320,14 @@
|
||||
<!-- 成功模态框 -->
|
||||
<div
|
||||
v-if="showSuccessModal"
|
||||
class="relative w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-3xl shadow-2xl overflow-hidden animate-in fade-in zoom-in duration-200"
|
||||
class="relative w-full max-w-lg bg-white border border-zinc-200 rounded-3xl shadow-2xl overflow-hidden animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div class="p-6 border-b border-zinc-800 flex items-center justify-between">
|
||||
<h3 class="text-lg font-black text-zinc-100 uppercase tracking-widest">
|
||||
<div class="p-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-lg font-black text-zinc-900 uppercase tracking-widest">
|
||||
API密钥创建成功
|
||||
</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-200 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-700 transition-colors"
|
||||
@click="closeModals"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -343,7 +343,7 @@
|
||||
>
|
||||
<Check :size="24" :stroke-width="3" />
|
||||
</div>
|
||||
<h4 class="text-lg font-black text-emerald-400">API密钥创建成功!</h4>
|
||||
<h4 class="text-lg font-black text-emerald-700">API密钥创建成功!</h4>
|
||||
<p class="text-xs text-zinc-500 mt-2">请妥善保管以下API密钥,它只会显示这一次。</p>
|
||||
</div>
|
||||
|
||||
@@ -353,7 +353,7 @@
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="flex-1 bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-3 font-mono text-xs text-blue-400 break-all select-all"
|
||||
class="flex-1 bg-white border border-zinc-200 rounded-xl px-4 py-3 font-mono text-xs text-blue-700 break-all select-all"
|
||||
>
|
||||
{{ newApiKey?.apiKey }}
|
||||
</div>
|
||||
@@ -362,7 +362,7 @@
|
||||
:class="
|
||||
copied
|
||||
? 'bg-emerald-600 text-white'
|
||||
: 'bg-zinc-800 hover:bg-zinc-700 text-zinc-300'
|
||||
: 'bg-zinc-100 hover:bg-zinc-200 text-zinc-600'
|
||||
"
|
||||
@click="copyToClipboard(newApiKey?.apiKey)"
|
||||
>
|
||||
@@ -381,9 +381,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 border-t border-zinc-800">
|
||||
<div class="p-6 border-t border-zinc-200">
|
||||
<button
|
||||
class="w-full py-2.5 bg-zinc-950 border border-zinc-800 hover:border-zinc-700 text-zinc-200 text-xs font-black rounded-xl transition-all"
|
||||
class="w-full py-2.5 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-700 text-xs font-black rounded-xl transition-all"
|
||||
@click="closeModals"
|
||||
>
|
||||
我已保存,关闭窗口
|
||||
@@ -394,12 +394,12 @@
|
||||
<!-- 详情模态框 -->
|
||||
<div
|
||||
v-if="showViewModal && selectedApiKey"
|
||||
class="relative w-full max-w-2xl bg-zinc-900 border border-zinc-800 rounded-3xl shadow-2xl overflow-hidden animate-in fade-in zoom-in duration-200"
|
||||
class="relative w-full max-w-2xl bg-white border border-zinc-200 rounded-3xl shadow-2xl overflow-hidden animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div class="p-6 border-b border-zinc-800 flex items-center justify-between">
|
||||
<h3 class="text-lg font-black text-zinc-100 uppercase tracking-widest">API密钥详情</h3>
|
||||
<div class="p-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-lg font-black text-zinc-900 uppercase tracking-widest">API密钥详情</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-200 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-700 transition-colors"
|
||||
@click="closeModals"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -409,14 +409,14 @@
|
||||
<div class="p-6 space-y-8 max-h-[80vh] overflow-y-auto custom-scrollbar">
|
||||
<section class="space-y-4">
|
||||
<h5
|
||||
class="text-[10px] font-black text-zinc-600 uppercase tracking-widest border-b border-zinc-800 pb-2"
|
||||
class="text-[10px] font-black text-zinc-600 uppercase tracking-widest border-b border-zinc-200 pb-2"
|
||||
>
|
||||
基本信息
|
||||
</h5>
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="space-y-1">
|
||||
<span class="text-[10px] text-zinc-600 font-bold">名称:</span>
|
||||
<p class="text-xs font-bold text-zinc-200">{{ selectedApiKey.name }}</p>
|
||||
<p class="text-xs font-bold text-zinc-700">{{ selectedApiKey.name }}</p>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<span class="text-[10px] text-zinc-600 font-bold">状态:</span>
|
||||
@@ -428,7 +428,7 @@
|
||||
>
|
||||
<span
|
||||
v-else-if="selectedApiKey.status === 'inactive'"
|
||||
class="inline-flex items-center gap-1.5 px-2 py-0.5 bg-zinc-800 text-zinc-500 text-[10px] font-black rounded uppercase border border-zinc-700/50"
|
||||
class="inline-flex items-center gap-1.5 px-2 py-0.5 bg-zinc-100 text-zinc-500 text-[10px] font-black rounded uppercase border border-zinc-200"
|
||||
>非活跃</span
|
||||
>
|
||||
<span
|
||||
@@ -446,13 +446,13 @@
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<span class="text-[10px] text-zinc-600 font-bold">创建者:</span>
|
||||
<p class="text-xs font-bold text-zinc-200">
|
||||
<p class="text-xs font-bold text-zinc-700">
|
||||
{{ selectedApiKey.creatorName || '未知' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<span class="text-[10px] text-zinc-600 font-bold">创建时间:</span>
|
||||
<p class="text-xs font-bold text-zinc-200">
|
||||
<p class="text-xs font-bold text-zinc-700">
|
||||
{{ formatDate(selectedApiKey.createdAt) }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -460,7 +460,7 @@
|
||||
<span class="text-[10px] text-zinc-600 font-bold">过期时间:</span>
|
||||
<p
|
||||
class="text-xs font-bold"
|
||||
:class="selectedApiKey.isExpired ? 'text-red-400' : 'text-zinc-200'"
|
||||
:class="selectedApiKey.isExpired ? 'text-red-400' : 'text-zinc-700'"
|
||||
>
|
||||
{{ formatDate(selectedApiKey.expiresAt) }}
|
||||
</p>
|
||||
@@ -470,19 +470,19 @@
|
||||
|
||||
<section class="space-y-4">
|
||||
<h5
|
||||
class="text-[10px] font-black text-zinc-600 uppercase tracking-widest border-b border-zinc-800 pb-2"
|
||||
class="text-[10px] font-black text-zinc-600 uppercase tracking-widest border-b border-zinc-200 pb-2"
|
||||
>
|
||||
使用统计
|
||||
</h5>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div
|
||||
class="p-4 bg-zinc-950 border border-zinc-800 rounded-2xl flex items-center justify-between group hover:border-blue-500/30 transition-all"
|
||||
class="p-4 bg-white border border-zinc-200 rounded-2xl flex items-center justify-between group hover:border-blue-200 transition-all"
|
||||
>
|
||||
<div class="space-y-0.5">
|
||||
<span class="text-[9px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
>总调用次数</span
|
||||
>
|
||||
<p class="text-xl font-black text-zinc-100">
|
||||
<p class="text-xl font-black text-zinc-900">
|
||||
{{ selectedApiKey.usageCount || 0 }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -493,13 +493,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="p-4 bg-zinc-950 border border-zinc-800 rounded-2xl flex items-center justify-between group hover:border-amber-500/30 transition-all"
|
||||
class="p-4 bg-white border border-zinc-200 rounded-2xl flex items-center justify-between group hover:border-amber-200 transition-all"
|
||||
>
|
||||
<div class="space-y-0.5">
|
||||
<span class="text-[9px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
>最后使用时间</span
|
||||
>
|
||||
<p class="text-xs font-bold text-zinc-100">
|
||||
<p class="text-xs font-bold text-zinc-900">
|
||||
{{
|
||||
selectedApiKey.lastUsedAt
|
||||
? formatDate(selectedApiKey.lastUsedAt)
|
||||
@@ -517,7 +517,7 @@
|
||||
</section>
|
||||
|
||||
<section class="space-y-4">
|
||||
<div class="flex items-center justify-between border-b border-zinc-800 pb-2">
|
||||
<div class="flex items-center justify-between border-b border-zinc-200 pb-2">
|
||||
<h5 class="text-[10px] font-black text-zinc-600 uppercase tracking-widest">
|
||||
最近调用日志
|
||||
</h5>
|
||||
@@ -538,7 +538,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-else-if="apiLogs.length === 0"
|
||||
class="flex flex-col items-center justify-center py-10 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl"
|
||||
class="flex flex-col items-center justify-center py-10 bg-white/70 border border-zinc-200 rounded-2xl"
|
||||
>
|
||||
<History :size="24" class="text-zinc-800 mb-2" />
|
||||
<span class="text-[10px] font-bold text-zinc-700 uppercase tracking-widest"
|
||||
@@ -549,7 +549,7 @@
|
||||
v-for="log in apiLogs"
|
||||
v-else
|
||||
:key="log.id"
|
||||
class="p-3 bg-zinc-950 border border-zinc-800/40 rounded-xl flex items-center justify-between group hover:border-zinc-700 transition-all"
|
||||
class="p-3 bg-white border border-zinc-200 rounded-xl flex items-center justify-between group hover:border-zinc-200 transition-all"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
@@ -1002,11 +1002,11 @@ const copyToClipboard = async (text) => {
|
||||
const getMethodClass = (method) => {
|
||||
const map = {
|
||||
GET: 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20',
|
||||
POST: 'bg-blue-500/10 text-blue-500 border-blue-500/20',
|
||||
POST: 'bg-blue-500/10 text-blue-500 border-blue-200',
|
||||
PUT: 'bg-amber-500/10 text-amber-500 border-amber-500/20',
|
||||
DELETE: 'bg-red-500/10 text-red-500 border-red-500/20'
|
||||
}
|
||||
return map[method] || 'bg-zinc-500/10 text-zinc-500 border-zinc-500/20'
|
||||
return map[method] || 'bg-zinc-100 text-zinc-500 border-zinc-200'
|
||||
}
|
||||
|
||||
const getStatusColorClass = (code) => {
|
||||
@@ -1025,7 +1025,7 @@ onMounted(() => {
|
||||
.loading-spinner {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||
border: 2px solid rgba(0, 0, 0, 0.08);
|
||||
border-top-color: var(--border-secondary, #3b82f6);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
|
||||
@@ -520,7 +520,7 @@ const formatFileSize = (bytes) => {
|
||||
/* 主容器 */
|
||||
.backup-manager {
|
||||
min-height: 100vh;
|
||||
background: var(--bg-primary, #1a1a1a);
|
||||
background: var(--bg-primary, #ffffff);
|
||||
color: var(--text-primary, #e5e5e5);
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
@@ -623,7 +623,7 @@ const formatFileSize = (bytes) => {
|
||||
transition: all 0.2s ease;
|
||||
display: block;
|
||||
text-align: center;
|
||||
color: white;
|
||||
color: var(--text-primary, #09090b);
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
@@ -1043,7 +1043,7 @@ const formatFileSize = (bytes) => {
|
||||
right: 1.5rem;
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
color: var(--text-primary, #09090b);
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
z-index: 1001;
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 w-full max-w-2xl rounded-xl overflow-hidden shadow-2xl flex flex-col max-h-[90vh]"
|
||||
class="bg-white border border-zinc-200 w-full max-w-2xl rounded-xl overflow-hidden shadow-2xl flex flex-col max-h-[90vh]"
|
||||
@click.stop
|
||||
>
|
||||
<!-- 头部 -->
|
||||
<div class="p-8 pb-4 flex items-center justify-between border-b border-zinc-800/50">
|
||||
<div class="p-8 pb-4 flex items-center justify-between border-b border-zinc-200">
|
||||
<div>
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight flex items-center gap-3">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-xl bg-purple-600/10 flex items-center justify-center text-purple-500"
|
||||
>
|
||||
@@ -32,7 +32,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="p-3 bg-zinc-800/50 hover:bg-zinc-800 text-zinc-500 hover:text-zinc-200 rounded-xl transition-all"
|
||||
class="p-3 bg-zinc-100/50 hover:bg-zinc-100 text-zinc-500 hover:text-zinc-700 rounded-xl transition-all"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -47,7 +47,7 @@
|
||||
'relative flex flex-col p-5 rounded-xl border-2 transition-all cursor-pointer group',
|
||||
updateType === 'grade-only'
|
||||
? 'bg-purple-500/5 border-purple-500/50 ring-4 ring-purple-500/10'
|
||||
: 'bg-zinc-950 border-zinc-800 hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 hover:border-zinc-200'
|
||||
]"
|
||||
>
|
||||
<input v-model="updateType" type="radio" value="grade-only" class="sr-only" >
|
||||
@@ -57,7 +57,7 @@
|
||||
'w-10 h-10 rounded-lg flex items-center justify-center transition-colors',
|
||||
updateType === 'grade-only'
|
||||
? 'bg-purple-500 text-white'
|
||||
: 'bg-zinc-800 text-zinc-500 group-hover:text-zinc-300'
|
||||
: 'bg-zinc-100 text-zinc-500 group-hover:text-zinc-600'
|
||||
]"
|
||||
>
|
||||
<Calendar :size="18" />
|
||||
@@ -69,7 +69,7 @@
|
||||
<Check :size="12" class="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-sm font-black text-zinc-200 uppercase tracking-widest"
|
||||
<span class="text-sm font-black text-zinc-700 uppercase tracking-widest"
|
||||
>仅更新年级</span
|
||||
>
|
||||
<span class="text-[10px] text-zinc-500 mt-1 font-medium leading-relaxed"
|
||||
@@ -82,7 +82,7 @@
|
||||
'relative flex flex-col p-5 rounded-xl border-2 transition-all cursor-pointer group',
|
||||
updateType === 'excel-batch'
|
||||
? 'bg-emerald-500/5 border-emerald-500/50 ring-4 ring-emerald-500/10'
|
||||
: 'bg-zinc-950 border-zinc-800 hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 hover:border-zinc-200'
|
||||
]"
|
||||
>
|
||||
<input v-model="updateType" type="radio" value="excel-batch" class="sr-only" >
|
||||
@@ -92,7 +92,7 @@
|
||||
'w-10 h-10 rounded-lg flex items-center justify-center transition-colors',
|
||||
updateType === 'excel-batch'
|
||||
? 'bg-emerald-500 text-white'
|
||||
: 'bg-zinc-800 text-zinc-500 group-hover:text-zinc-300'
|
||||
: 'bg-zinc-100 text-zinc-500 group-hover:text-zinc-600'
|
||||
]"
|
||||
>
|
||||
<FileSpreadsheet :size="18" />
|
||||
@@ -104,7 +104,7 @@
|
||||
<Check :size="12" class="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-sm font-black text-zinc-200 uppercase tracking-widest"
|
||||
<span class="text-sm font-black text-zinc-700 uppercase tracking-widest"
|
||||
>Excel 批量更新</span
|
||||
>
|
||||
<span class="text-[10px] text-zinc-500 mt-1 font-medium leading-relaxed"
|
||||
@@ -117,7 +117,7 @@
|
||||
'relative flex flex-col p-5 rounded-xl border-2 transition-all cursor-pointer group',
|
||||
updateType === 'status-batch'
|
||||
? 'bg-amber-500/5 border-amber-500/50 ring-4 ring-amber-500/10'
|
||||
: 'bg-zinc-950 border-zinc-800 hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 hover:border-zinc-200'
|
||||
]"
|
||||
>
|
||||
<input v-model="updateType" type="radio" value="status-batch" class="sr-only" >
|
||||
@@ -127,7 +127,7 @@
|
||||
'w-10 h-10 rounded-lg flex items-center justify-center transition-colors',
|
||||
updateType === 'status-batch'
|
||||
? 'bg-amber-500 text-white'
|
||||
: 'bg-zinc-800 text-zinc-500 group-hover:text-zinc-300'
|
||||
: 'bg-zinc-100 text-zinc-500 group-hover:text-zinc-600'
|
||||
]"
|
||||
>
|
||||
<ShieldAlert :size="18" />
|
||||
@@ -139,7 +139,7 @@
|
||||
<Check :size="12" class="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-sm font-black text-zinc-200 uppercase tracking-widest"
|
||||
<span class="text-sm font-black text-zinc-700 uppercase tracking-widest"
|
||||
>设置账户状态</span
|
||||
>
|
||||
<span class="text-[10px] text-zinc-500 mt-1 font-medium leading-relaxed"
|
||||
@@ -153,7 +153,7 @@
|
||||
v-if="['grade-only', 'status-batch'].includes(updateType)"
|
||||
class="space-y-6 animate-in fade-in slide-in-from-bottom-2 duration-300"
|
||||
>
|
||||
<div class="p-6 bg-zinc-950/50 border border-zinc-800/50 rounded-xl space-y-6">
|
||||
<div class="p-6 bg-white/70 border border-zinc-200 rounded-xl space-y-6">
|
||||
<div
|
||||
class="flex items-center gap-2 text-xs font-black text-zinc-400 uppercase tracking-widest"
|
||||
>
|
||||
@@ -203,7 +203,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="max-h-48 overflow-y-auto rounded-lg border border-zinc-800 bg-zinc-950 p-2 custom-scrollbar"
|
||||
class="max-h-48 overflow-y-auto rounded-lg border border-zinc-200 bg-white p-2 custom-scrollbar"
|
||||
>
|
||||
<div
|
||||
v-if="filteredStudents.length === 0"
|
||||
@@ -215,17 +215,17 @@
|
||||
<label
|
||||
v-for="student in filteredStudents"
|
||||
:key="student.id"
|
||||
class="flex items-center gap-3 p-3 rounded-lg hover:bg-zinc-900/50 cursor-pointer transition-colors group"
|
||||
class="flex items-center gap-3 p-3 rounded-lg hover:bg-zinc-50/50 cursor-pointer transition-colors group"
|
||||
>
|
||||
<input
|
||||
v-model="selectedUserIds"
|
||||
:value="student.id"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded-md border-zinc-700 bg-zinc-950 text-purple-600 focus:ring-purple-500/20"
|
||||
class="w-4 h-4 rounded-md border-zinc-200 bg-white text-purple-700 focus:ring-purple-500/30"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<span
|
||||
class="text-xs font-bold text-zinc-200 group-hover:text-purple-400 transition-colors"
|
||||
class="text-xs font-bold text-zinc-700 group-hover:text-purple-400 transition-colors"
|
||||
>{{ student.name }}</span
|
||||
>
|
||||
<span class="text-[10px] text-zinc-600 font-mono">{{
|
||||
@@ -261,21 +261,21 @@
|
||||
/>
|
||||
</div>
|
||||
<label
|
||||
class="flex items-center gap-3 p-3 bg-zinc-950 border border-zinc-800 rounded-lg cursor-pointer hover:border-zinc-700 transition-all"
|
||||
class="flex items-center gap-3 p-3 bg-white border border-zinc-200 rounded-lg cursor-pointer hover:border-zinc-200 transition-all"
|
||||
>
|
||||
<input
|
||||
v-model="keepClass"
|
||||
type="checkbox"
|
||||
class="w-5 h-5 rounded-md border-zinc-700 bg-zinc-950 text-purple-600 focus:ring-purple-500/20"
|
||||
class="w-5 h-5 rounded-md border-zinc-200 bg-white text-purple-700 focus:ring-purple-500/30"
|
||||
>
|
||||
<span class="text-xs font-bold text-zinc-300">保持原有班级不变</span>
|
||||
<span class="text-xs font-bold text-zinc-600">保持原有班级不变</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 目标状态设置 -->
|
||||
<div v-if="updateType === 'status-batch'" class="p-6 bg-amber-500/5 border border-amber-500/20 rounded-xl space-y-6">
|
||||
<div class="flex items-center gap-2 text-xs font-black text-amber-400 uppercase tracking-widest">
|
||||
<div class="flex items-center gap-2 text-xs font-black text-amber-700 uppercase tracking-widest">
|
||||
<Save :size="14" />
|
||||
目标状态设置
|
||||
</div>
|
||||
@@ -299,7 +299,7 @@
|
||||
v-model="statusReason"
|
||||
rows="2"
|
||||
placeholder="例如: 2025届学生统一毕业"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-amber-500/30 transition-all text-zinc-200 resize-none"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-amber-200 transition-all text-zinc-700 resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -336,19 +336,19 @@
|
||||
'w-full py-12 border-2 border-dashed rounded-xl transition-all flex flex-col items-center justify-center gap-4',
|
||||
isDragOver
|
||||
? 'border-emerald-500 bg-emerald-500/10'
|
||||
: 'border-zinc-800 hover:border-emerald-500/50 hover:bg-emerald-500/5'
|
||||
: 'border-zinc-200 hover:border-emerald-500/50 hover:bg-emerald-500/5'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'w-16 h-16 rounded-lg bg-zinc-900 flex items-center justify-center transition-colors shadow-xl',
|
||||
'w-16 h-16 rounded-lg bg-white flex items-center justify-center transition-colors shadow-xl',
|
||||
isDragOver ? 'text-emerald-500' : 'text-zinc-600 group-hover:text-emerald-500'
|
||||
]"
|
||||
>
|
||||
<Upload :size="32" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-base font-black text-zinc-200 tracking-tight">
|
||||
<p class="text-base font-black text-zinc-700 tracking-tight">
|
||||
拖拽 Excel 文件到此处
|
||||
</p>
|
||||
<p class="text-xs text-zinc-500 mt-1">
|
||||
@@ -362,7 +362,7 @@
|
||||
<!-- 模板与说明 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div
|
||||
class="md:col-span-2 p-5 bg-zinc-950 border border-zinc-800 rounded-xl space-y-3"
|
||||
class="md:col-span-2 p-5 bg-white border border-zinc-200 rounded-xl space-y-3"
|
||||
>
|
||||
<div
|
||||
class="flex items-center gap-2 text-[10px] font-black text-zinc-500 uppercase tracking-widest"
|
||||
@@ -372,15 +372,15 @@
|
||||
</div>
|
||||
<ul class="text-[10px] text-zinc-600 space-y-1.5 font-medium leading-relaxed">
|
||||
<li class="flex items-start gap-2">
|
||||
<div class="w-1 h-1 rounded-full bg-zinc-700 mt-1.5" />
|
||||
<div class="w-1 h-1 rounded-full bg-zinc-200 mt-1.5" />
|
||||
第一行为表头:用户名、姓名、年级、班级、新用户名
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<div class="w-1 h-1 rounded-full bg-zinc-700 mt-1.5" />
|
||||
<div class="w-1 h-1 rounded-full bg-zinc-200 mt-1.5" />
|
||||
<span class="text-zinc-400 font-bold">用户名</span> 列用于精确匹配现有账户
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<div class="w-1 h-1 rounded-full bg-zinc-700 mt-1.5" />
|
||||
<div class="w-1 h-1 rounded-full bg-zinc-200 mt-1.5" />
|
||||
字段留空则保持原值不变,不进行覆盖更新
|
||||
</li>
|
||||
</ul>
|
||||
@@ -417,11 +417,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-xl border border-zinc-800 bg-zinc-950 overflow-hidden shadow-xl">
|
||||
<div class="rounded-xl border border-zinc-200 bg-white overflow-hidden shadow-xl">
|
||||
<div class="overflow-x-auto custom-scrollbar">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead
|
||||
class="bg-zinc-900/80 text-[10px] font-black text-zinc-500 uppercase tracking-widest border-b border-zinc-800"
|
||||
class="bg-zinc-50/80 text-[10px] font-black text-zinc-500 uppercase tracking-widest border-b border-zinc-200"
|
||||
>
|
||||
<tr>
|
||||
<th class="px-5 py-4 whitespace-nowrap">匹配用户</th>
|
||||
@@ -435,7 +435,7 @@
|
||||
v-for="(row, index) in excelPreviewData.slice(0, 10)"
|
||||
:key="index"
|
||||
:class="[
|
||||
row.error ? 'bg-red-500/5' : 'hover:bg-zinc-900/30 transition-colors'
|
||||
row.error ? 'bg-red-500/5' : 'hover:bg-zinc-50/30 transition-colors'
|
||||
]"
|
||||
>
|
||||
<td class="px-5 py-4">
|
||||
@@ -443,7 +443,7 @@
|
||||
<span
|
||||
:class="[
|
||||
'text-xs font-bold',
|
||||
row.error ? 'text-red-400' : 'text-zinc-200'
|
||||
row.error ? 'text-red-400' : 'text-zinc-700'
|
||||
]"
|
||||
>{{ row.username }}</span
|
||||
>
|
||||
@@ -459,11 +459,11 @@
|
||||
</td>
|
||||
<td class="px-5 py-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs font-bold text-emerald-400">{{
|
||||
<span class="text-xs font-bold text-emerald-700">{{
|
||||
row.newGrade || row.currentGrade || '-'
|
||||
}}</span>
|
||||
<span class="text-[10px] text-zinc-700">/</span>
|
||||
<span class="text-xs font-bold text-emerald-400">{{
|
||||
<span class="text-xs font-bold text-emerald-700">{{
|
||||
row.newClass || row.currentClass || '-'
|
||||
}}</span>
|
||||
</div>
|
||||
@@ -488,7 +488,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="excelPreviewData.length > 10"
|
||||
class="p-4 text-center border-t border-zinc-900 bg-zinc-900/20 text-[10px] text-zinc-600 font-bold uppercase tracking-widest"
|
||||
class="p-4 text-center border-t border-zinc-900 bg-zinc-50/20 text-[10px] text-zinc-600 font-bold uppercase tracking-widest"
|
||||
>
|
||||
以及另外 {{ excelPreviewData.length - 10 }} 条记录已在队列中
|
||||
</div>
|
||||
@@ -507,9 +507,9 @@
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<div class="p-8 pt-4 border-t border-zinc-800/50 bg-zinc-900/50 flex gap-3">
|
||||
<div class="p-8 pt-4 border-t border-zinc-200 bg-zinc-50/50 flex gap-3">
|
||||
<button
|
||||
class="flex-1 px-6 py-4 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
class="flex-1 px-6 py-4 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
取消操作
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<!-- 页面标题 -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight flex items-center gap-3">
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight flex items-center gap-3">
|
||||
歌曲黑名单管理
|
||||
</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1 font-medium">
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 添加黑名单项表单 -->
|
||||
<section class="bg-zinc-900/40 border border-zinc-800 rounded-xl p-8 shadow-xl">
|
||||
<section class="bg-zinc-50/40 border border-zinc-200 rounded-xl p-8 shadow-xl">
|
||||
<h3 class="text-[10px] font-black text-zinc-600 uppercase tracking-[0.2em] mb-6 px-1">
|
||||
添加黑名单项
|
||||
</h3>
|
||||
@@ -24,31 +24,31 @@
|
||||
<label class="text-[10px] font-black text-zinc-600 uppercase tracking-widest px-1"
|
||||
>封禁类型</label
|
||||
>
|
||||
<div class="flex p-1 bg-zinc-950 border border-zinc-800 rounded-lg gap-1">
|
||||
<div class="flex p-1 bg-white border border-zinc-200 rounded-lg gap-1">
|
||||
<button
|
||||
class="flex-1 flex items-center justify-center gap-2 py-2 rounded-md text-[11px] font-bold transition-all"
|
||||
:class="newItem.type === 'SONG' ? 'bg-zinc-800 text-blue-400' : 'text-zinc-600'"
|
||||
:class="newItem.type === 'SONG' ? 'bg-zinc-100 text-blue-700' : 'text-zinc-600'"
|
||||
@click="newItem.type = 'SONG'"
|
||||
>
|
||||
<Music :size="12" /> 歌曲
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 flex items-center justify-center gap-2 py-2 rounded-md text-[11px] font-bold transition-all"
|
||||
:class="newItem.type === 'KEYWORD' ? 'bg-zinc-800 text-purple-400' : 'text-zinc-600'"
|
||||
:class="newItem.type === 'KEYWORD' ? 'bg-zinc-100 text-purple-400' : 'text-zinc-600'"
|
||||
@click="newItem.type = 'KEYWORD'"
|
||||
>
|
||||
<Type :size="12" /> 关键词
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 flex items-center justify-center gap-2 py-2 rounded-md text-[11px] font-bold transition-all"
|
||||
:class="newItem.type === 'LANGUAGE' ? 'bg-zinc-800 text-amber-400' : 'text-zinc-600'"
|
||||
:class="newItem.type === 'LANGUAGE' ? 'bg-zinc-100 text-amber-700' : 'text-zinc-600'"
|
||||
@click="newItem.type = 'LANGUAGE'"
|
||||
>
|
||||
<Languages :size="12" /> 语种
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 flex items-center justify-center gap-2 py-2 rounded-md text-[11px] font-bold transition-all"
|
||||
:class="newItem.type === 'GENRE' ? 'bg-zinc-800 text-emerald-400' : 'text-zinc-600'"
|
||||
:class="newItem.type === 'GENRE' ? 'bg-zinc-100 text-emerald-700' : 'text-zinc-600'"
|
||||
@click="newItem.type = 'GENRE'"
|
||||
>
|
||||
<Disc3 :size="12" /> 曲风
|
||||
@@ -64,17 +64,18 @@
|
||||
v-model="newItem.value"
|
||||
type="text"
|
||||
:placeholder="typePlaceholder"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-5 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 placeholder:text-zinc-700 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-5 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 placeholder:text-zinc-700 transition-all"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-if="newItem.type === 'LANGUAGE' || newItem.type === 'GENRE'" class="xl:col-span-5 xl:col-start-auto space-y-1">
|
||||
<p class="text-[10px] text-zinc-600 font-bold leading-relaxed px-1">
|
||||
<p class="text-[10px] text-zinc-600 font-bold leading-relaxed px-1 flex items-start gap-1.5">
|
||||
<Icon name="lightbulb" :size="13" class="text-amber-600 shrink-0 mt-[2px]" />
|
||||
<template v-if="newItem.type === 'LANGUAGE'">
|
||||
💡 支持多语种匹配:中文 / 英文 / 日文 / 韩文 / 俄文 / 阿拉伯文 等,支持逗号分隔,例如:<span class="text-amber-400">日文,韩文</span>
|
||||
支持多语种匹配:中文 / 英文 / 日文 / 韩文 / 俄文 / 阿拉伯文 等,支持逗号分隔,例如:<span class="text-amber-700">日文,韩文</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
💡 支持曲风关键词,可逗号分隔,例如:<span class="text-emerald-400">DJ,摇滚,rap</span>
|
||||
支持曲风关键词,可逗号分隔,例如:<span class="text-emerald-700">DJ,摇滚,rap</span>
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
@@ -87,7 +88,7 @@
|
||||
v-model="newItem.reason"
|
||||
type="text"
|
||||
placeholder="简述加入黑名单的原因"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-5 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 placeholder:text-zinc-700 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-5 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 placeholder:text-zinc-700 transition-all"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -110,7 +111,7 @@
|
||||
|
||||
<!-- 筛选和搜索 -->
|
||||
<div
|
||||
class="bg-zinc-900/20 border border-zinc-800 rounded-xl p-4 flex flex-col md:flex-row gap-4 items-center"
|
||||
class="bg-zinc-50/20 border border-zinc-200 rounded-xl p-4 flex flex-col md:flex-row gap-4 items-center"
|
||||
>
|
||||
<div class="flex-1 relative w-full">
|
||||
<Search class="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-700" :size="18" />
|
||||
@@ -118,7 +119,7 @@
|
||||
v-model="filters.search"
|
||||
type="text"
|
||||
placeholder="在黑名单中搜索..."
|
||||
class="w-full bg-zinc-950/50 border border-zinc-800 rounded-lg pl-12 pr-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 placeholder:text-zinc-700 transition-all"
|
||||
class="w-full bg-white/70 border border-zinc-200 rounded-lg pl-12 pr-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 placeholder:text-zinc-700 transition-all"
|
||||
@input="debounceSearch"
|
||||
>
|
||||
</div>
|
||||
@@ -131,7 +132,7 @@
|
||||
@change="handleTypeFilterChange"
|
||||
/>
|
||||
<button
|
||||
class="p-3 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-600 hover:text-blue-400 transition-all active:scale-95"
|
||||
class="p-3 bg-white border border-zinc-200 rounded-lg text-zinc-600 hover:text-blue-700 transition-all active:scale-95"
|
||||
@click="loadBlacklist"
|
||||
>
|
||||
<Filter :size="18" />
|
||||
@@ -159,8 +160,8 @@
|
||||
<div
|
||||
v-for="item in blacklist"
|
||||
:key="item.id"
|
||||
class="group flex flex-col lg:flex-row lg:items-center gap-6 p-6 bg-zinc-900/30 border rounded-xl transition-all hover:border-zinc-700"
|
||||
:class="[item.isActive ? 'border-zinc-800' : 'border-zinc-800/40 opacity-60']"
|
||||
class="group flex flex-col lg:flex-row lg:items-center gap-6 p-6 bg-zinc-50/30 border rounded-xl transition-all hover:border-zinc-200"
|
||||
:class="[item.isActive ? 'border-zinc-200' : 'border-zinc-200 opacity-60']"
|
||||
>
|
||||
<div class="flex-1 flex items-start gap-5">
|
||||
<div
|
||||
@@ -181,7 +182,7 @@
|
||||
>
|
||||
{{ getTypeLabel(item.type) }}
|
||||
</span>
|
||||
<h5 class="text-base font-black text-zinc-100 truncate tracking-tight">
|
||||
<h5 class="text-base font-black text-zinc-900 truncate tracking-tight">
|
||||
{{ item.value }}
|
||||
</h5>
|
||||
</div>
|
||||
@@ -202,14 +203,14 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-end gap-3 pt-4 lg:pt-0 border-t lg:border-t-0 border-zinc-800/50"
|
||||
class="flex items-center justify-end gap-3 pt-4 lg:pt-0 border-t lg:border-t-0 border-zinc-200"
|
||||
>
|
||||
<button
|
||||
:disabled="loading"
|
||||
class="flex items-center gap-2 px-5 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest transition-all"
|
||||
:class="[
|
||||
item.isActive
|
||||
? 'bg-zinc-800 text-zinc-500 hover:text-amber-500 hover:bg-amber-500/5 hover:border-amber-500/10 border border-transparent'
|
||||
? 'bg-zinc-100 text-zinc-500 hover:text-amber-500 hover:bg-amber-500/5 hover:border-amber-500/10 border border-transparent'
|
||||
: 'bg-emerald-600 text-white shadow-lg shadow-emerald-900/20'
|
||||
]"
|
||||
@click="toggleItemStatus(item)"
|
||||
@@ -219,7 +220,7 @@
|
||||
</button>
|
||||
<button
|
||||
:disabled="loading"
|
||||
class="p-3 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-700 hover:text-red-500 hover:border-red-500/30 transition-all active:scale-95"
|
||||
class="p-3 bg-white border border-zinc-200 rounded-xl text-zinc-700 hover:text-red-500 hover:border-red-200 transition-all active:scale-95"
|
||||
@click="deleteItem(item)"
|
||||
>
|
||||
<Trash2 :size="16" />
|
||||
@@ -234,7 +235,7 @@
|
||||
class="py-20 flex flex-col items-center justify-center space-y-4"
|
||||
>
|
||||
<div
|
||||
class="w-12 h-12 border-4 border-zinc-800 border-t-blue-500 rounded-full animate-spin"
|
||||
class="w-12 h-12 border-4 border-zinc-200 border-t-blue-500 rounded-full animate-spin"
|
||||
/>
|
||||
<p class="text-zinc-500 text-xs font-bold uppercase tracking-widest">加载中...</p>
|
||||
</div>
|
||||
@@ -242,10 +243,10 @@
|
||||
<!-- 空状态 -->
|
||||
<div
|
||||
v-else-if="blacklist.length === 0"
|
||||
class="py-20 flex flex-col items-center justify-center text-center space-y-4 bg-zinc-900/10 border border-zinc-800/40 border-dashed rounded-xl"
|
||||
class="py-20 flex flex-col items-center justify-center text-center space-y-4 bg-zinc-50/10 border border-zinc-200 border-dashed rounded-xl"
|
||||
>
|
||||
<div
|
||||
class="w-16 h-16 rounded-lg bg-zinc-950 border border-zinc-800 flex items-center justify-center text-zinc-800 shadow-xl"
|
||||
class="w-16 h-16 rounded-lg bg-white border border-zinc-200 flex items-center justify-center text-zinc-800 shadow-xl"
|
||||
>
|
||||
<Ban :size="32" />
|
||||
</div>
|
||||
@@ -262,19 +263,19 @@
|
||||
<div v-if="pagination.pages > 1" class="flex items-center justify-center gap-4 mt-8">
|
||||
<button
|
||||
:disabled="pagination.page <= 1 || loading"
|
||||
class="p-3 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-400 hover:text-zinc-100 disabled:opacity-30 disabled:cursor-not-allowed transition-all active:scale-95"
|
||||
class="p-3 bg-white border border-zinc-200 rounded-lg text-zinc-400 hover:text-zinc-900 disabled:opacity-30 disabled:cursor-not-allowed transition-all active:scale-95"
|
||||
@click="changePage(pagination.page - 1)"
|
||||
>
|
||||
<ChevronLeft :size="20" />
|
||||
</button>
|
||||
<div
|
||||
class="px-6 py-3 bg-zinc-900/50 border border-zinc-800 rounded-lg text-[11px] font-black text-zinc-400 uppercase tracking-widest"
|
||||
class="px-6 py-3 bg-zinc-50/50 border border-zinc-200 rounded-lg text-[11px] font-black text-zinc-400 uppercase tracking-widest"
|
||||
>
|
||||
第 {{ pagination.page }} 页 / 共 {{ pagination.pages }} 页
|
||||
</div>
|
||||
<button
|
||||
:disabled="pagination.page >= pagination.pages || loading"
|
||||
class="p-3 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-400 hover:text-zinc-100 disabled:opacity-30 disabled:cursor-not-allowed transition-all active:scale-95"
|
||||
class="p-3 bg-white border border-zinc-200 rounded-lg text-zinc-400 hover:text-zinc-900 disabled:opacity-30 disabled:cursor-not-allowed transition-all active:scale-95"
|
||||
@click="changePage(pagination.page + 1)"
|
||||
>
|
||||
<ChevronRight :size="20" />
|
||||
@@ -293,10 +294,10 @@
|
||||
>
|
||||
<div
|
||||
v-if="showDeleteDialog"
|
||||
class="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm"
|
||||
class="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-white/80 backdrop-blur-sm"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-md bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-300"
|
||||
class="w-full max-w-md bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-300"
|
||||
@click.stop
|
||||
>
|
||||
<div class="flex flex-col items-center py-8 space-y-6 text-center px-8">
|
||||
@@ -306,16 +307,16 @@
|
||||
<Trash2 :size="28" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-lg font-bold text-zinc-100">确认从黑名单移除</h4>
|
||||
<h4 class="text-lg font-bold text-zinc-900">确认从黑名单移除</h4>
|
||||
<p class="text-xs text-zinc-500 leading-relaxed">
|
||||
确定要移除
|
||||
<span class="text-zinc-300 font-bold">"{{ deleteTargetItem?.value }}"</span>
|
||||
<span class="text-zinc-600 font-bold">"{{ deleteTargetItem?.value }}"</span>
|
||||
吗?移除后,该项目将恢复为正常状态。
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-3 w-full pt-4">
|
||||
<button
|
||||
class="flex-1 px-4 py-4 bg-zinc-800 hover:bg-zinc-700 text-zinc-400 text-[10px] font-black rounded-lg transition-all uppercase tracking-widest"
|
||||
class="flex-1 px-4 py-4 bg-zinc-100 hover:bg-zinc-200 text-zinc-400 text-[10px] font-black rounded-lg transition-all uppercase tracking-widest"
|
||||
@click="showDeleteDialog = false"
|
||||
>
|
||||
取消
|
||||
@@ -422,7 +423,7 @@ const getTypeLabel = (type: string) => {
|
||||
const getTypeIconClass = (type: string) => {
|
||||
switch (type) {
|
||||
case 'SONG':
|
||||
return 'bg-blue-600/10 text-blue-500 border-blue-500/20 shadow-lg shadow-blue-900/5'
|
||||
return 'bg-blue-50 text-blue-500 border-blue-200 shadow-lg shadow-blue-900/5'
|
||||
case 'KEYWORD':
|
||||
return 'bg-purple-600/10 text-purple-500 border-purple-500/20 shadow-lg shadow-purple-900/5'
|
||||
case 'LANGUAGE':
|
||||
@@ -430,22 +431,22 @@ const getTypeIconClass = (type: string) => {
|
||||
case 'GENRE':
|
||||
return 'bg-emerald-600/10 text-emerald-500 border-emerald-500/20 shadow-lg shadow-emerald-900/5'
|
||||
default:
|
||||
return 'bg-zinc-600/10 text-zinc-500 border-zinc-500/20'
|
||||
return 'bg-zinc-600/10 text-zinc-500 border-zinc-200'
|
||||
}
|
||||
}
|
||||
|
||||
const getTypeBadgeClass = (type: string) => {
|
||||
switch (type) {
|
||||
case 'SONG':
|
||||
return 'bg-blue-600/10 text-blue-400 border-blue-500/20'
|
||||
return 'bg-blue-50 text-blue-700 border-blue-200'
|
||||
case 'KEYWORD':
|
||||
return 'bg-purple-600/10 text-purple-400 border-purple-500/20'
|
||||
case 'LANGUAGE':
|
||||
return 'bg-amber-600/10 text-amber-400 border-amber-500/20'
|
||||
return 'bg-amber-600/10 text-amber-700 border-amber-500/20'
|
||||
case 'GENRE':
|
||||
return 'bg-emerald-600/10 text-emerald-400 border-emerald-500/20'
|
||||
return 'bg-emerald-600/10 text-emerald-700 border-emerald-500/20'
|
||||
default:
|
||||
return 'bg-zinc-600/10 text-zinc-400 border-zinc-500/20'
|
||||
return 'bg-zinc-600/10 text-zinc-400 border-zinc-200'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<div class="max-w-[1200px] mx-auto space-y-6 pb-24 px-2">
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">更新日志</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">更新日志</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1 font-medium">管理前台“更新日志”页面的发布内容</p>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
class="flex items-center gap-2 px-5 py-2 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-400 text-xs font-bold rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="flex items-center gap-2 px-5 py-2 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-400 text-xs font-bold rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
:disabled="loading"
|
||||
@click="load"
|
||||
>
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="flex flex-col items-center justify-center py-20">
|
||||
<div class="w-8 h-8 border-4 border-zinc-800 border-t-blue-500 rounded-full animate-spin mb-4" />
|
||||
<div class="w-8 h-8 border-4 border-zinc-200 border-t-blue-500 rounded-full animate-spin mb-4" />
|
||||
<p class="text-zinc-500 text-sm">加载中...</p>
|
||||
</div>
|
||||
|
||||
@@ -31,20 +31,20 @@
|
||||
<div
|
||||
v-for="item in entries"
|
||||
:key="item.id"
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-2xl p-5 flex flex-col md:flex-row md:items-center md:justify-between gap-4"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-2xl p-5 flex flex-col md:flex-row md:items-center md:justify-between gap-4"
|
||||
>
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span
|
||||
class="px-2.5 py-1 rounded-lg text-[10px] font-black uppercase tracking-widest"
|
||||
:class="item.published ? 'bg-emerald-500/10 text-emerald-300' : 'bg-zinc-800/50 text-zinc-400'"
|
||||
:class="item.published ? 'bg-emerald-500/10 text-emerald-700' : 'bg-zinc-100 text-zinc-600'"
|
||||
>
|
||||
{{ item.published ? '已发布' : '草稿' }}
|
||||
</span>
|
||||
<span class="px-2.5 py-1 rounded-lg bg-blue-500/10 text-blue-300 text-[10px] font-black">
|
||||
<span class="px-2.5 py-1 rounded-lg bg-blue-50 text-blue-700 text-[10px] font-black">
|
||||
v{{ item.version }}
|
||||
</span>
|
||||
<p class="text-sm font-black text-zinc-100 truncate">{{ item.title }}</p>
|
||||
<p class="text-sm font-black text-zinc-900 truncate">{{ item.title }}</p>
|
||||
</div>
|
||||
<p class="text-[10px] text-zinc-500 mt-2">
|
||||
{{ formatDate(item.publishedAt || item.createdAt) }}
|
||||
@@ -53,13 +53,13 @@
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="px-3 py-2 rounded-lg border border-zinc-700 text-zinc-300 hover:bg-zinc-900 text-xs font-bold"
|
||||
class="px-3 py-2 rounded-lg border border-zinc-200 text-zinc-600 hover:bg-white text-xs font-bold"
|
||||
@click="openEdit(item)"
|
||||
>
|
||||
<Pencil :size="14" class="inline-block mr-1" /> 编辑
|
||||
</button>
|
||||
<button
|
||||
class="px-3 py-2 rounded-lg border border-zinc-700 text-zinc-300 hover:bg-zinc-900 text-xs font-bold"
|
||||
class="px-3 py-2 rounded-lg border border-zinc-200 text-zinc-600 hover:bg-white text-xs font-bold"
|
||||
:disabled="savingId === item.id"
|
||||
@click="togglePublish(item)"
|
||||
>
|
||||
@@ -67,7 +67,7 @@
|
||||
{{ item.published ? '取消发布' : '发布' }}
|
||||
</button>
|
||||
<button
|
||||
class="px-3 py-2 rounded-lg border border-red-500/30 text-red-300 hover:bg-red-500/10 text-xs font-bold disabled:opacity-50"
|
||||
class="px-3 py-2 rounded-lg border border-red-200 text-red-300 hover:bg-red-500/10 text-xs font-bold disabled:opacity-50"
|
||||
:disabled="savingId === item.id"
|
||||
@click="remove(item)"
|
||||
>
|
||||
@@ -83,11 +83,11 @@
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="showModal" class="fixed inset-0 z-[80] bg-black/60 backdrop-blur-sm p-4 flex items-end sm:items-center justify-center">
|
||||
<div class="w-full max-w-2xl bg-zinc-950 border border-zinc-800 rounded-2xl overflow-hidden">
|
||||
<div class="px-5 py-4 border-b border-zinc-800 flex items-center justify-between">
|
||||
<p class="text-sm font-black text-zinc-100">{{ editingId ? '编辑更新日志' : '新建更新日志' }}</p>
|
||||
<div class="w-full max-w-2xl bg-white border border-zinc-200 rounded-2xl overflow-hidden">
|
||||
<div class="px-5 py-4 border-b border-zinc-200 flex items-center justify-between">
|
||||
<p class="text-sm font-black text-zinc-900">{{ editingId ? '编辑更新日志' : '新建更新日志' }}</p>
|
||||
<button
|
||||
class="h-9 w-9 rounded-lg border border-zinc-700 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-900 transition-colors"
|
||||
class="h-9 w-9 rounded-lg border border-zinc-200 text-zinc-400 hover:text-zinc-700 hover:bg-white transition-colors"
|
||||
@click="closeModal"
|
||||
>
|
||||
<X :size="16" class="mx-auto" />
|
||||
@@ -98,11 +98,11 @@
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-[10px] font-black text-zinc-600 uppercase tracking-widest px-1 block mb-2">版本号</label>
|
||||
<input v-model="form.version" type="text" class="w-full bg-zinc-900 border border-zinc-800 rounded-xl px-4 py-2.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all" placeholder="如:2.4.1" >
|
||||
<input v-model="form.version" type="text" class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all" placeholder="如:2.4.1" >
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-black text-zinc-600 uppercase tracking-widest px-1 block mb-2">标题</label>
|
||||
<input v-model="form.title" type="text" class="w-full bg-zinc-900 border border-zinc-800 rounded-xl px-4 py-2.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all" placeholder="如:投稿体验优化" >
|
||||
<input v-model="form.title" type="text" class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all" placeholder="如:投稿体验优化" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -111,23 +111,23 @@
|
||||
<textarea
|
||||
v-model="form.content"
|
||||
:rows="10"
|
||||
class="w-full bg-zinc-900 border border-zinc-800 rounded-xl px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all font-mono"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all font-mono"
|
||||
placeholder="支持纯文本(建议用分点)。"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-4 p-3 rounded-xl border border-zinc-800 bg-zinc-900/40">
|
||||
<div class="flex items-center justify-between gap-4 p-3 rounded-xl border border-zinc-200 bg-zinc-50/40">
|
||||
<div>
|
||||
<p class="text-xs font-bold text-zinc-200">立即发布</p>
|
||||
<p class="text-xs font-bold text-zinc-700">立即发布</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-0.5">发布后前台更新日志页面可见</p>
|
||||
</div>
|
||||
<input v-model="form.published" type="checkbox" class="w-5 h-5 rounded border-zinc-800 bg-zinc-900 accent-blue-600 cursor-pointer" >
|
||||
<input v-model="form.published" type="checkbox" class="w-5 h-5 rounded border-zinc-200 bg-white accent-blue-600 cursor-pointer" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-5 py-4 border-t border-zinc-800 flex items-center justify-end gap-3">
|
||||
<div class="px-5 py-4 border-t border-zinc-200 flex items-center justify-end gap-3">
|
||||
<button
|
||||
class="px-4 py-2 rounded-lg border border-zinc-700 text-zinc-300 hover:bg-zinc-900 text-sm font-bold"
|
||||
class="px-4 py-2 rounded-lg border border-zinc-200 text-zinc-600 hover:bg-white text-sm font-bold"
|
||||
@click="closeModal"
|
||||
>
|
||||
取消
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
class="flex flex-col items-center justify-center min-h-[60vh] space-y-6"
|
||||
>
|
||||
<div class="relative w-24 h-24">
|
||||
<div class="absolute inset-0 border-4 border-blue-500/20 rounded-full" />
|
||||
<div class="absolute inset-0 border-4 border-blue-200 rounded-full" />
|
||||
<div class="absolute inset-0 border-4 border-t-blue-500 rounded-full animate-spin" />
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<Activity class="text-blue-500 animate-pulse" :size="32" />
|
||||
@@ -32,7 +32,7 @@
|
||||
<h3 class="text-xl font-black text-white tracking-tight">数据加载失败</h3>
|
||||
<p class="text-sm text-zinc-500 mt-2 max-w-md">{{ error }}</p>
|
||||
<button
|
||||
class="mt-6 px-8 py-3 bg-zinc-900 border border-zinc-800 rounded-full text-sm font-black text-white hover:bg-zinc-800 transition-all flex items-center gap-2 mx-auto"
|
||||
class="mt-6 px-8 py-3 bg-white border border-zinc-200 rounded-full text-sm font-black text-white hover:bg-zinc-100 transition-all flex items-center gap-2 mx-auto"
|
||||
@click="refreshAllData"
|
||||
>
|
||||
<RefreshCw :size="16" />
|
||||
@@ -52,16 +52,16 @@
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
:disabled="isLoading"
|
||||
class="p-2.5 bg-zinc-900/50 border border-zinc-800 rounded-full text-zinc-400 hover:text-white transition-all group disabled:opacity-50"
|
||||
class="p-2.5 bg-zinc-50/50 border border-zinc-200 rounded-full text-zinc-400 hover:text-zinc-900 transition-all group disabled:opacity-50"
|
||||
@click="refreshAllData"
|
||||
>
|
||||
<RefreshCw :size="18" :class="{ 'animate-spin': isLoading }" />
|
||||
</button>
|
||||
<div
|
||||
class="px-3 py-1.5 bg-blue-500/10 border border-blue-500/20 rounded-full flex items-center gap-2"
|
||||
class="px-3 py-1.5 bg-blue-500/10 border border-blue-200 rounded-full flex items-center gap-2"
|
||||
>
|
||||
<div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
|
||||
<span class="text-[10px] font-black text-blue-400 uppercase tracking-widest"
|
||||
<span class="text-[10px] font-black text-blue-700 uppercase tracking-widest"
|
||||
>实时模式</span
|
||||
>
|
||||
</div>
|
||||
@@ -82,11 +82,11 @@
|
||||
<div
|
||||
v-for="(stat, i) in kpiStats"
|
||||
:key="i"
|
||||
class="group relative p-6 bg-zinc-900/40 border border-zinc-800/60 rounded-[2rem] overflow-hidden hover:border-zinc-700 transition-all hover:shadow-2xl hover:shadow-black/40"
|
||||
class="group relative p-6 bg-zinc-50/40 border border-zinc-200/60 rounded-[2rem] overflow-hidden hover:border-zinc-200 transition-all hover:shadow-2xl hover:shadow-black/40"
|
||||
>
|
||||
<div class="flex justify-between items-start">
|
||||
<div
|
||||
:class="`p-3 rounded-2xl bg-zinc-950 border border-zinc-800 text-zinc-400 group-hover:text-${stat.color}-400 group-hover:border-${stat.color}-500/30 transition-all`"
|
||||
:class="`p-3 rounded-2xl bg-white border border-zinc-200 text-zinc-400 group-hover:text-${stat.color}-400 group-hover:border-${stat.color}-500/30 transition-all`"
|
||||
>
|
||||
<component :is="stat.icon" :size="20" />
|
||||
</div>
|
||||
@@ -103,7 +103,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<h4 class="text-3xl font-black text-zinc-100 tracking-tighter">
|
||||
<h4 class="text-3xl font-black text-zinc-900 tracking-tighter">
|
||||
{{ formatNumber(stat.value) }}
|
||||
</h4>
|
||||
<p class="text-[10px] font-black text-zinc-600 uppercase tracking-[0.2em] mt-1">
|
||||
@@ -122,7 +122,7 @@
|
||||
<!-- 实时脉冲部分 -->
|
||||
<div class="lg:col-span-12 grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div
|
||||
class="p-8 bg-zinc-900/20 border border-zinc-800/40 rounded-3xl flex items-center justify-between relative overflow-hidden group cursor-help"
|
||||
class="p-8 bg-zinc-50/20 border border-zinc-200 rounded-3xl flex items-center justify-between relative overflow-hidden group cursor-help"
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
@@ -142,7 +142,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="p-8 bg-zinc-900/20 border border-zinc-800/40 rounded-3xl flex items-center justify-between relative overflow-hidden group"
|
||||
class="p-8 bg-zinc-50/20 border border-zinc-200 rounded-3xl flex items-center justify-between relative overflow-hidden group"
|
||||
>
|
||||
<div class="relative z-10">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
@@ -166,7 +166,7 @@
|
||||
|
||||
<!-- 趋势分析图表卡片 -->
|
||||
<div
|
||||
class="lg:col-span-8 bg-zinc-900/40 border border-zinc-800 rounded-[3rem] p-8 shadow-2xl overflow-hidden flex flex-col min-h-[500px]"
|
||||
class="lg:col-span-8 bg-zinc-50/40 border border-zinc-200 rounded-[3rem] p-8 shadow-2xl overflow-hidden flex flex-col min-h-[500px]"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-10">
|
||||
<div>
|
||||
@@ -196,7 +196,7 @@
|
||||
class="flex-1 flex flex-col items-center justify-center space-y-4"
|
||||
>
|
||||
<div
|
||||
class="w-12 h-12 border-4 border-blue-500/20 border-t-blue-500 rounded-full animate-spin"
|
||||
class="w-12 h-12 border-4 border-blue-200 border-t-blue-500 rounded-full animate-spin"
|
||||
/>
|
||||
<p class="text-xs font-black text-zinc-600 uppercase tracking-widest">
|
||||
正在加载趋势数据...
|
||||
@@ -213,7 +213,7 @@
|
||||
{{ panelStates.trends.error }}
|
||||
</p>
|
||||
<button
|
||||
class="px-6 py-2 bg-zinc-800 hover:bg-zinc-700 text-white text-[10px] font-black uppercase tracking-widest rounded-full transition-all"
|
||||
class="px-6 py-2 bg-zinc-100 hover:bg-zinc-200 text-white text-[10px] font-black uppercase tracking-widest rounded-full transition-all"
|
||||
@click="loadTrends"
|
||||
>
|
||||
立即重试
|
||||
@@ -233,10 +233,10 @@
|
||||
:style="{
|
||||
height: `${(item.count / Math.max(...trendData.map((d) => d.count), 1)) * 100}%`
|
||||
}"
|
||||
class="w-full bg-gradient-to-t from-blue-600/10 to-blue-500/40 rounded-t-xl group-hover:from-blue-600/30 group-hover:to-blue-400 transition-all border-x border-t border-blue-500/20 group-hover:border-blue-500/40 min-h-[4px]"
|
||||
class="w-full bg-gradient-to-t from-blue-600/10 to-blue-500/40 rounded-t-xl group-hover:from-blue-600/30 group-hover:to-blue-400 transition-all border-x border-t border-blue-200 group-hover:border-blue-500/40 min-h-[4px]"
|
||||
/>
|
||||
<div
|
||||
class="absolute left-1/2 -translate-x-1/2 -translate-y-full mb-2 text-[10px] font-black text-blue-400 opacity-0 group-hover:opacity-100 transition-all whitespace-nowrap pointer-events-none z-10"
|
||||
class="absolute left-1/2 -translate-x-1/2 -translate-y-full mb-2 text-[10px] font-black text-blue-700 opacity-0 group-hover:opacity-100 transition-all whitespace-nowrap pointer-events-none z-10"
|
||||
:style="{
|
||||
bottom: `${(item.count / Math.max(...trendData.map((d) => d.count), 1)) * 100}%`
|
||||
}"
|
||||
@@ -259,7 +259,7 @@
|
||||
|
||||
<!-- 热门歌曲排行榜 -->
|
||||
<div
|
||||
class="lg:col-span-4 bg-zinc-900/40 border border-zinc-800 rounded-[3rem] p-8 flex flex-col min-h-[500px]"
|
||||
class="lg:col-span-4 bg-zinc-50/40 border border-zinc-200 rounded-[3rem] p-8 flex flex-col min-h-[500px]"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<h3 class="text-xl font-bold flex items-center gap-3 text-white">
|
||||
@@ -292,7 +292,7 @@
|
||||
v-if="panelStates.topSongs.loading && topSongs.length === 0"
|
||||
class="flex-1 space-y-4"
|
||||
>
|
||||
<div v-for="i in 5" :key="i" class="h-20 bg-zinc-800/20 animate-pulse rounded-2xl" />
|
||||
<div v-for="i in 5" :key="i" class="h-20 bg-zinc-100/20 animate-pulse rounded-2xl" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="panelStates.topSongs.error && topSongs.length === 0"
|
||||
@@ -303,7 +303,7 @@
|
||||
{{ panelStates.topSongs.error }}
|
||||
</p>
|
||||
<button
|
||||
class="px-4 py-2 bg-zinc-800 text-[10px] font-black uppercase rounded-full hover:bg-zinc-700 text-white transition-colors"
|
||||
class="px-4 py-2 bg-zinc-100 text-[10px] font-black uppercase rounded-full hover:bg-zinc-200 text-white transition-colors"
|
||||
@click="loadTopSongs"
|
||||
>
|
||||
重试
|
||||
@@ -316,7 +316,7 @@
|
||||
<div
|
||||
v-for="(song, i) in topSongs.slice(0, 5)"
|
||||
:key="i"
|
||||
class="p-4 bg-zinc-950/50 border border-zinc-800/40 rounded-2xl flex items-center gap-4 group hover:bg-zinc-800/30 transition-all"
|
||||
class="p-4 bg-white/70 border border-zinc-200 rounded-2xl flex items-center gap-4 group hover:bg-zinc-100/30 transition-all"
|
||||
>
|
||||
<div
|
||||
:class="`w-8 h-8 rounded-lg flex items-center justify-center font-black text-xs ${
|
||||
@@ -326,14 +326,14 @@
|
||||
? 'bg-zinc-300 text-black'
|
||||
: i === 2
|
||||
? 'bg-amber-800 text-white'
|
||||
: 'text-zinc-600 border border-zinc-800'
|
||||
: 'text-zinc-600 border border-zinc-200'
|
||||
}`"
|
||||
>
|
||||
{{ i + 1 }}
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h4
|
||||
class="text-sm font-bold text-zinc-200 truncate group-hover:text-white transition-colors"
|
||||
class="text-sm font-bold text-zinc-700 truncate group-hover:text-zinc-900 transition-colors"
|
||||
>
|
||||
{{ song.title }}
|
||||
</h4>
|
||||
@@ -360,7 +360,7 @@
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<!-- 活跃用户排行榜 -->
|
||||
<div
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-[3rem] p-8 flex flex-col min-h-[500px]"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-[3rem] p-8 flex flex-col min-h-[500px]"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<h3 class="text-xl font-bold flex items-center gap-3 text-white">
|
||||
@@ -376,7 +376,7 @@
|
||||
v-if="panelStates.activeUsers.loading && activeUsers.length === 0"
|
||||
class="flex-1 space-y-4"
|
||||
>
|
||||
<div v-for="i in 4" :key="i" class="h-24 bg-zinc-800/20 animate-pulse rounded-3xl" />
|
||||
<div v-for="i in 4" :key="i" class="h-24 bg-zinc-100/20 animate-pulse rounded-3xl" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="panelStates.activeUsers.error && activeUsers.length === 0"
|
||||
@@ -387,7 +387,7 @@
|
||||
{{ panelStates.activeUsers.error }}
|
||||
</p>
|
||||
<button
|
||||
class="px-4 py-2 bg-zinc-800 text-[10px] font-black uppercase rounded-full hover:bg-zinc-700 text-white transition-colors"
|
||||
class="px-4 py-2 bg-zinc-100 text-[10px] font-black uppercase rounded-full hover:bg-zinc-200 text-white transition-colors"
|
||||
@click="loadActiveUsers"
|
||||
>
|
||||
重试
|
||||
@@ -401,31 +401,31 @@
|
||||
<div
|
||||
v-for="(user, i) in activeUsers.slice(0, 5)"
|
||||
:key="i"
|
||||
class="relative p-5 bg-zinc-950/30 border border-zinc-800/50 rounded-3xl overflow-hidden group"
|
||||
class="relative p-5 bg-white/60 border border-zinc-200 rounded-3xl overflow-hidden group"
|
||||
>
|
||||
<div class="flex items-center gap-4 relative z-10">
|
||||
<img
|
||||
v-if="user.avatarUrl && !failedAvatars[user.id]"
|
||||
:src="user.avatarUrl"
|
||||
:alt="user.name"
|
||||
class="w-12 h-12 rounded-2xl object-cover border border-zinc-700/50 shrink-0"
|
||||
class="w-12 h-12 rounded-2xl object-cover border border-zinc-200 shrink-0"
|
||||
@error="handleAvatarError($event, user)"
|
||||
>
|
||||
<div
|
||||
v-else
|
||||
class="w-12 h-12 rounded-2xl bg-zinc-800 flex items-center justify-center font-black text-zinc-500 group-hover:text-zinc-200 transition-colors shrink-0"
|
||||
class="w-12 h-12 rounded-2xl bg-zinc-100 flex items-center justify-center font-black text-zinc-500 group-hover:text-zinc-700 transition-colors shrink-0"
|
||||
>
|
||||
{{ user.name.charAt(0) }}
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h4 class="text-sm font-bold text-zinc-100 truncate">{{ user.name }}</h4>
|
||||
<h4 class="text-sm font-bold text-zinc-900 truncate">{{ user.name }}</h4>
|
||||
<p class="text-xs text-zinc-600 font-medium mt-1 truncate">
|
||||
{{ user.contributions }}首投稿 · {{ user.likes }}次点赞
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-right shrink-0">
|
||||
<span
|
||||
class="text-xl font-black text-zinc-300 group-hover:text-purple-400 transition-colors"
|
||||
class="text-xl font-black text-zinc-600 group-hover:text-purple-400 transition-colors"
|
||||
>{{ user.activityScore }}</span
|
||||
>
|
||||
<p class="text-[10px] font-black text-zinc-700 uppercase tracking-widest">
|
||||
@@ -451,7 +451,7 @@
|
||||
|
||||
<!-- 学期对比分析 -->
|
||||
<div
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-[3rem] p-8 flex flex-col min-h-[500px]"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-[3rem] p-8 flex flex-col min-h-[500px]"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<h3 class="text-xl font-bold flex items-center gap-3 text-white">
|
||||
@@ -470,7 +470,7 @@
|
||||
v-if="panelStates.semesterComparison.loading && semesterComparison.length === 0"
|
||||
class="flex-1 space-y-4"
|
||||
>
|
||||
<div v-for="i in 3" :key="i" class="h-32 bg-zinc-800/20 animate-pulse rounded-[2rem]" />
|
||||
<div v-for="i in 3" :key="i" class="h-32 bg-zinc-100/20 animate-pulse rounded-[2rem]" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="panelStates.semesterComparison.error && semesterComparison.length === 0"
|
||||
@@ -481,7 +481,7 @@
|
||||
{{ panelStates.semesterComparison.error }}
|
||||
</p>
|
||||
<button
|
||||
class="px-4 py-2 bg-zinc-800 text-[10px] font-black uppercase rounded-full hover:bg-zinc-700 text-white transition-colors"
|
||||
class="px-4 py-2 bg-zinc-100 text-[10px] font-black uppercase rounded-full hover:bg-zinc-200 text-white transition-colors"
|
||||
@click="loadSemesterComparison"
|
||||
>
|
||||
重试
|
||||
@@ -494,10 +494,10 @@
|
||||
<div
|
||||
v-for="(sem, i) in semesterComparison"
|
||||
:key="i"
|
||||
:class="`p-6 border rounded-[2rem] transition-all ${sem.isActive ? 'bg-emerald-500/5 border-emerald-500/20 shadow-lg shadow-emerald-500/5' : 'bg-zinc-950/20 border-zinc-800/60 opacity-60 hover:opacity-100'}`"
|
||||
:class="`p-6 border rounded-[2rem] transition-all ${sem.isActive ? 'bg-emerald-500/5 border-emerald-500/20 shadow-lg shadow-emerald-900/5' : 'bg-white/50 border-zinc-200/60 opacity-60 hover:opacity-100'}`"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="text-xs font-black text-zinc-300 uppercase tracking-widest">{{
|
||||
<span class="text-xs font-black text-zinc-600 uppercase tracking-widest">{{
|
||||
sem.semester
|
||||
}}</span>
|
||||
<span
|
||||
@@ -509,19 +509,19 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<h5 class="text-lg font-black text-zinc-100">{{ sem.totalSongs }}</h5>
|
||||
<h5 class="text-lg font-black text-zinc-900">{{ sem.totalSongs }}</h5>
|
||||
<p class="text-[9px] font-black text-zinc-600 uppercase tracking-tighter mt-1">
|
||||
歌曲总量
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="text-lg font-black text-zinc-100">{{ sem.totalSchedules }}</h5>
|
||||
<h5 class="text-lg font-black text-zinc-900">{{ sem.totalSchedules }}</h5>
|
||||
<p class="text-[9px] font-black text-zinc-600 uppercase tracking-tighter mt-1">
|
||||
排期总数
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="text-lg font-black text-zinc-100">{{ sem.totalRequests }}</h5>
|
||||
<h5 class="text-lg font-black text-zinc-900">{{ sem.totalRequests }}</h5>
|
||||
<p class="text-[9px] font-black text-zinc-600 uppercase tracking-tighter mt-1">
|
||||
获赞总数
|
||||
</p>
|
||||
@@ -547,16 +547,16 @@
|
||||
@mouseleave="handleTooltipMouseLeave"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 rounded-3xl shadow-2xl overflow-hidden min-w-[320px] max-w-[380px] backdrop-blur-xl bg-opacity-90 animate-in fade-in zoom-in duration-200"
|
||||
class="bg-white border border-zinc-200 rounded-3xl shadow-2xl overflow-hidden min-w-[320px] max-w-[380px] backdrop-blur-xl bg-opacity-90 animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div
|
||||
class="p-6 border-b border-zinc-800/50 bg-gradient-to-br from-blue-500/10 to-transparent max-h-[500px] overflow-y-auto custom-scrollbar"
|
||||
class="p-6 border-b border-zinc-200 bg-gradient-to-br from-blue-500/10 to-transparent max-h-[500px] overflow-y-auto custom-scrollbar"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="text-sm font-black text-white uppercase tracking-widest truncate max-h-[20px] overflow-hidden">活跃用户详情</h4>
|
||||
<div class="flex items-center gap-2 px-2 py-1 bg-blue-500/20 rounded-full shrink-0">
|
||||
<div class="w-1.5 h-1.5 bg-blue-500 rounded-full animate-pulse" />
|
||||
<span class="text-[10px] font-black text-blue-400"
|
||||
<span class="text-[10px] font-black text-blue-700"
|
||||
>{{ realtimeStats.activeUsers }} 在线</span
|
||||
>
|
||||
</div>
|
||||
@@ -569,28 +569,28 @@
|
||||
<div
|
||||
v-for="user in realtimeStats.activeUsersList.slice(0, 5)"
|
||||
:key="user.id"
|
||||
class="flex items-center gap-3 p-3 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl group hover:border-blue-500/30 transition-all"
|
||||
class="flex items-center gap-3 p-3 bg-white/70 border border-zinc-200 rounded-2xl group hover:border-blue-200 transition-all"
|
||||
>
|
||||
<img
|
||||
v-if="user.avatarUrl && !failedAvatars[user.id]"
|
||||
:src="user.avatarUrl"
|
||||
:alt="user.name"
|
||||
class="w-10 h-10 rounded-xl object-cover border border-zinc-700/50 shrink-0"
|
||||
class="w-10 h-10 rounded-xl object-cover border border-zinc-200 shrink-0"
|
||||
@error="handleAvatarError($event, user)"
|
||||
>
|
||||
<div
|
||||
v-else
|
||||
class="w-10 h-10 rounded-xl bg-zinc-800 flex items-center justify-center font-black text-zinc-500 group-hover:text-blue-400 transition-colors shrink-0"
|
||||
class="w-10 h-10 rounded-xl bg-zinc-100 flex items-center justify-center font-black text-zinc-500 group-hover:text-blue-700 transition-colors shrink-0"
|
||||
>
|
||||
{{ user.name.charAt(0) }}
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-xs font-bold text-zinc-200 truncate max-h-[16px] overflow-hidden">{{ user.name }}</div>
|
||||
<div class="text-xs font-bold text-zinc-700 truncate max-h-[16px] overflow-hidden">{{ user.name }}</div>
|
||||
<div class="text-[10px] text-zinc-500 font-medium truncate max-h-[14px] overflow-hidden">
|
||||
@{{ user.username }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[10px] font-black text-zinc-600 bg-zinc-900 px-2 py-1 rounded-lg shrink-0">
|
||||
<div class="text-[10px] font-black text-zinc-600 bg-white px-2 py-1 rounded-lg shrink-0">
|
||||
{{ user.lastActive }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -605,7 +605,7 @@
|
||||
<p class="text-xs font-black uppercase tracking-widest truncate max-h-[16px] overflow-hidden">暂无在线用户</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-4 bg-zinc-950/50 flex items-center justify-between">
|
||||
<div class="px-6 py-4 bg-white/70 flex items-center justify-between">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest truncate max-h-[14px] overflow-hidden"
|
||||
>实时数据同步中</span
|
||||
>
|
||||
@@ -1147,7 +1147,7 @@ const formatDateShort = (dateStr) => {
|
||||
width: 6px;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="max-w-[1200px] mx-auto space-y-8 pb-20 px-2">
|
||||
<!-- 页面标题 -->
|
||||
<div class="space-y-1">
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">数据库操作</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">数据库操作</h2>
|
||||
<p class="text-xs text-zinc-500 font-medium">
|
||||
执行系统底层维护任务,包括备份、恢复及全局数据重置
|
||||
</p>
|
||||
@@ -14,7 +14,7 @@
|
||||
v-for="card in cards"
|
||||
:key="card.id"
|
||||
:class="[
|
||||
'group relative bg-zinc-900/40 border border-zinc-800 rounded-2xl p-8 transition-all hover:border-zinc-700 hover:shadow-2xl hover:shadow-black/40',
|
||||
'group relative bg-zinc-50/40 border border-zinc-200 rounded-2xl p-8 transition-all hover:border-zinc-200 hover:shadow-2xl hover:shadow-black/40',
|
||||
card.isDanger ? 'hover:border-rose-500/20' : ''
|
||||
]"
|
||||
>
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div
|
||||
:class="[
|
||||
'p-3.5 rounded-2xl bg-zinc-950 border border-zinc-800 transition-all',
|
||||
'p-3.5 rounded-2xl bg-white border border-zinc-200 transition-all',
|
||||
card.isDanger
|
||||
? 'text-rose-500 border-rose-500/10'
|
||||
: `text-${card.color}-500 border-${card.color}-500/10 shadow-lg`
|
||||
@@ -38,7 +38,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex-1 space-y-2">
|
||||
<h3 class="text-lg font-bold text-zinc-100 group-hover:text-blue-400 transition-colors">
|
||||
<h3 class="text-lg font-bold text-zinc-900 group-hover:text-blue-700 transition-colors">
|
||||
{{ card.title }}
|
||||
</h3>
|
||||
<p class="text-xs text-zinc-500 leading-relaxed font-medium">
|
||||
@@ -48,10 +48,10 @@
|
||||
|
||||
<button
|
||||
:disabled="isLoading(card.id)"
|
||||
class="w-full py-3 rounded-xl text-xs font-black uppercase tracking-widest transition-all active:scale-95 bg-zinc-950 border border-zinc-800 text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100 hover:border-zinc-700"
|
||||
class="w-full py-3 rounded-xl text-xs font-black uppercase tracking-widest transition-all active:scale-95 bg-white border border-zinc-200 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-900 hover:border-zinc-200"
|
||||
:class="
|
||||
card.isDanger
|
||||
? 'bg-zinc-950 border border-rose-900/30 text-rose-500 hover:bg-rose-600 hover:text-white hover:border-rose-600 shadow-lg shadow-rose-900/5'
|
||||
? 'bg-white border border-rose-200 text-rose-600 hover:bg-rose-600 hover:text-zinc-900 hover:border-rose-600 shadow-lg shadow-rose-900/5'
|
||||
: ''
|
||||
"
|
||||
@click="openModal(card.id)"
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="bg-blue-600/5 border border-blue-500/10 rounded-xl p-5 flex items-start gap-4">
|
||||
<AlertCircle class="text-blue-500 shrink-0 mt-0.5 w-[18px] h-[18px]" />
|
||||
<div class="space-y-1">
|
||||
<p class="text-[11px] font-bold text-zinc-300">数据库维护建议</p>
|
||||
<p class="text-[11px] font-bold text-zinc-600">数据库维护建议</p>
|
||||
<p class="text-[10px] text-zinc-500 leading-relaxed">
|
||||
建议每周进行一次全量备份。在执行“恢复备份”或“重置数据库”前,请务必先创建一份当前的数据备份,以免误操作造成不可挽回的损失。
|
||||
</p>
|
||||
@@ -89,12 +89,12 @@
|
||||
>
|
||||
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm" @click="activeModal = 'none'" />
|
||||
<div
|
||||
class="relative bg-zinc-900 border border-zinc-800 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
class="relative bg-white border border-zinc-200 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight">创建数据库备份</h3>
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight">创建数据库备份</h3>
|
||||
<button
|
||||
class="p-2 hover:bg-zinc-800 rounded-xl transition-colors text-zinc-500 hover:text-zinc-200"
|
||||
class="p-2 hover:bg-zinc-100 rounded-xl transition-colors text-zinc-500 hover:text-zinc-700"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
<X class="w-5 h-5" />
|
||||
@@ -109,18 +109,18 @@
|
||||
<label
|
||||
v-for="(item, i) in backupOptions"
|
||||
:key="i"
|
||||
class="flex items-start gap-4 p-4 bg-zinc-950/50 border border-zinc-800 rounded-xl cursor-pointer hover:border-zinc-700 transition-all group"
|
||||
class="flex items-start gap-4 p-4 bg-white/70 border border-zinc-200 rounded-xl cursor-pointer hover:border-zinc-200 transition-all group"
|
||||
>
|
||||
<div class="shrink-0 mt-0.5">
|
||||
<input
|
||||
v-model="createForm[item.key]"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded border-zinc-800 bg-zinc-900 accent-blue-600"
|
||||
class="w-4 h-4 rounded border-zinc-200 bg-white accent-blue-600"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p
|
||||
class="text-xs font-bold text-zinc-200 group-hover:text-blue-400 transition-colors"
|
||||
class="text-xs font-bold text-zinc-700 group-hover:text-blue-700 transition-colors"
|
||||
>
|
||||
{{ item.label }}
|
||||
</p>
|
||||
@@ -135,9 +135,9 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-8 py-6 bg-zinc-950/50 border-t border-zinc-800 flex gap-3 justify-end">
|
||||
<div class="px-8 py-6 bg-white/70 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-300 transition-colors uppercase tracking-widest"
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-600 transition-colors uppercase tracking-widest"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
取消
|
||||
@@ -160,12 +160,12 @@
|
||||
>
|
||||
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm" @click="activeModal = 'none'" />
|
||||
<div
|
||||
class="relative bg-zinc-900 border border-zinc-800 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
class="relative bg-white border border-zinc-200 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight">恢复数据库备份</h3>
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight">恢复数据库备份</h3>
|
||||
<button
|
||||
class="p-2 hover:bg-zinc-800 rounded-xl transition-colors text-zinc-500 hover:text-zinc-200"
|
||||
class="p-2 hover:bg-zinc-100 rounded-xl transition-colors text-zinc-500 hover:text-zinc-700"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
<X class="w-5 h-5" />
|
||||
@@ -173,7 +173,7 @@
|
||||
</div>
|
||||
<div class="p-8 space-y-6">
|
||||
<div
|
||||
class="border-2 border-dashed border-zinc-800 rounded-2xl p-10 flex flex-col items-center justify-center text-center group hover:border-emerald-500/50 hover:bg-emerald-500/5 transition-all cursor-pointer"
|
||||
class="border-2 border-dashed border-zinc-200 rounded-2xl p-10 flex flex-col items-center justify-center text-center group hover:border-emerald-500/50 hover:bg-emerald-500/5 transition-all cursor-pointer"
|
||||
@click="$refs.fileInput.click()"
|
||||
@dragover.prevent
|
||||
@drop.prevent="handleFileDrop"
|
||||
@@ -181,7 +181,7 @@
|
||||
<Upload
|
||||
class="w-8 h-8 text-zinc-700 mb-4 group-hover:text-emerald-500 transition-colors"
|
||||
/>
|
||||
<h5 class="text-sm font-bold text-zinc-300">
|
||||
<h5 class="text-sm font-bold text-zinc-600">
|
||||
{{ selectedFile ? selectedFile.name : '点击选择或拖拽备份文件' }}
|
||||
</h5>
|
||||
<p class="text-[10px] text-zinc-600 font-bold uppercase mt-1 tracking-widest">
|
||||
@@ -205,15 +205,15 @@
|
||||
:class="[
|
||||
'p-4 border rounded-xl text-left transition-all',
|
||||
restoreForm.mode === 'merge'
|
||||
? 'bg-zinc-950 border-emerald-500/30'
|
||||
: 'bg-zinc-950 border-zinc-800 hover:border-zinc-700'
|
||||
? 'bg-white border-emerald-200'
|
||||
: 'bg-white border-zinc-200 hover:border-zinc-200'
|
||||
]"
|
||||
@click="restoreForm.mode = 'merge'"
|
||||
>
|
||||
<h6
|
||||
:class="[
|
||||
'text-xs font-bold',
|
||||
restoreForm.mode === 'merge' ? 'text-emerald-400' : 'text-zinc-500'
|
||||
restoreForm.mode === 'merge' ? 'text-emerald-700' : 'text-zinc-500'
|
||||
]"
|
||||
>
|
||||
增量模式
|
||||
@@ -224,15 +224,15 @@
|
||||
:class="[
|
||||
'p-4 border rounded-xl text-left transition-all',
|
||||
restoreForm.mode === 'replace'
|
||||
? 'bg-zinc-950 border-emerald-500/30'
|
||||
: 'bg-zinc-950 border-zinc-800 hover:border-zinc-700'
|
||||
? 'bg-white border-emerald-200'
|
||||
: 'bg-white border-zinc-200 hover:border-zinc-200'
|
||||
]"
|
||||
@click="restoreForm.mode = 'replace'"
|
||||
>
|
||||
<h6
|
||||
:class="[
|
||||
'text-xs font-bold',
|
||||
restoreForm.mode === 'replace' ? 'text-emerald-400' : 'text-zinc-500'
|
||||
restoreForm.mode === 'replace' ? 'text-emerald-700' : 'text-zinc-500'
|
||||
]"
|
||||
>
|
||||
覆盖模式
|
||||
@@ -244,7 +244,7 @@
|
||||
|
||||
<div
|
||||
v-if="restoreForm.mode === 'replace' && hasSuperAdminInBackup"
|
||||
class="p-4 bg-zinc-950 border border-zinc-800 rounded-xl"
|
||||
class="p-4 bg-white border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<label class="flex items-start gap-3 cursor-pointer">
|
||||
<input
|
||||
@@ -253,7 +253,7 @@
|
||||
class="mt-0.5 accent-emerald-500"
|
||||
>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-zinc-200">覆盖备份中的超级管理员账号数据</p>
|
||||
<p class="text-xs font-bold text-zinc-700">覆盖备份中的超级管理员账号数据</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-1">
|
||||
关闭时将保留当前超级管理员账号及其第三方绑定、2FA等关联数据
|
||||
</p>
|
||||
@@ -270,9 +270,9 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-8 py-6 bg-zinc-950/50 border-t border-zinc-800 flex gap-3 justify-end">
|
||||
<div class="px-8 py-6 bg-white/70 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-300 transition-colors uppercase tracking-widest"
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-600 transition-colors uppercase tracking-widest"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
取消
|
||||
@@ -295,12 +295,12 @@
|
||||
>
|
||||
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm" @click="activeModal = 'none'" />
|
||||
<div
|
||||
class="relative bg-zinc-900 border border-zinc-800 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
class="relative bg-white border border-zinc-200 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight">重置数据表序列</h3>
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight">重置数据表序列</h3>
|
||||
<button
|
||||
class="p-2 hover:bg-zinc-800 rounded-xl transition-colors text-zinc-500 hover:text-zinc-200"
|
||||
class="p-2 hover:bg-zinc-100 rounded-xl transition-colors text-zinc-500 hover:text-zinc-700"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
<X class="w-5 h-5" />
|
||||
@@ -318,14 +318,14 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-zinc-950/50 border border-zinc-800 rounded-2xl space-y-4">
|
||||
<div class="p-6 bg-white/70 border border-zinc-200 rounded-2xl space-y-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-lg bg-amber-500/10 text-amber-500 flex items-center justify-center"
|
||||
>
|
||||
<AlertCircle class="w-4 h-4" />
|
||||
</div>
|
||||
<h6 class="text-xs font-bold text-zinc-300 uppercase tracking-widest">
|
||||
<h6 class="text-xs font-bold text-zinc-600 uppercase tracking-widest">
|
||||
什么是重置序列?
|
||||
</h6>
|
||||
</div>
|
||||
@@ -336,9 +336,9 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-8 py-6 bg-zinc-950/50 border-t border-zinc-800 flex gap-3 justify-end">
|
||||
<div class="px-8 py-6 bg-white/70 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-300 transition-colors uppercase tracking-widest"
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-600 transition-colors uppercase tracking-widest"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
取消
|
||||
@@ -361,12 +361,12 @@
|
||||
>
|
||||
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm" @click="activeModal = 'none'" />
|
||||
<div
|
||||
class="relative bg-zinc-900 border border-zinc-800 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
class="relative bg-white border border-zinc-200 rounded-3xl w-full max-w-lg overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-200"
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800 flex items-center justify-between">
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-rose-500 tracking-tight">危险操作:重置数据库</h3>
|
||||
<button
|
||||
class="p-2 hover:bg-zinc-800 rounded-xl transition-colors text-zinc-500 hover:text-zinc-200"
|
||||
class="p-2 hover:bg-zinc-100 rounded-xl transition-colors text-zinc-500 hover:text-zinc-700"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
<X class="w-5 h-5" />
|
||||
@@ -382,7 +382,7 @@
|
||||
</h4>
|
||||
<p class="text-xs text-zinc-500 mt-2 font-medium leading-relaxed">
|
||||
重置操作将永久删除系统中的所有
|
||||
<span class="text-zinc-300 font-bold"
|
||||
<span class="text-zinc-600 font-bold"
|
||||
>歌曲、投稿记录、排期文件、通知、日志及除您以外的用户账号</span
|
||||
>。
|
||||
</p>
|
||||
@@ -395,7 +395,7 @@
|
||||
请输入以下代码以确认操作
|
||||
</label>
|
||||
<div
|
||||
class="bg-zinc-950 border border-rose-900/30 rounded-xl px-4 py-3 font-mono text-[10px] text-rose-400 text-center select-all"
|
||||
class="bg-white border border-rose-200 rounded-xl px-4 py-3 font-mono text-[10px] text-rose-700 text-center select-all"
|
||||
>
|
||||
{{ CONFIRM_CODE }}
|
||||
</div>
|
||||
@@ -403,20 +403,20 @@
|
||||
v-model="resetConfirmText"
|
||||
type="text"
|
||||
placeholder="在此输入上述代码..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-rose-500/40 text-center font-mono placeholder:text-zinc-700"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-rose-200 text-center font-mono placeholder:text-zinc-700"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3 pt-2">
|
||||
<button
|
||||
class="py-3 bg-zinc-900 hover:bg-zinc-800 text-zinc-500 text-xs font-black rounded-xl transition-all uppercase tracking-widest"
|
||||
class="py-3 bg-white hover:bg-zinc-100 text-zinc-500 text-xs font-black rounded-xl transition-all uppercase tracking-widest"
|
||||
@click="activeModal = 'none'"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
:disabled="resetConfirmText !== CONFIRM_CODE || resetLoading"
|
||||
class="py-3 rounded-xl text-xs font-black uppercase tracking-widest transition-all shadow-lg disabled:bg-zinc-800 disabled:text-zinc-600 disabled:cursor-not-allowed disabled:border-zinc-700"
|
||||
class="py-3 rounded-xl text-xs font-black uppercase tracking-widest transition-all shadow-lg disabled:bg-zinc-100 disabled:text-zinc-600 disabled:cursor-not-allowed disabled:border-zinc-200"
|
||||
:class="
|
||||
resetConfirmText === CONFIRM_CODE
|
||||
? 'bg-rose-600 hover:bg-rose-500 text-white shadow-rose-900/20 active:scale-95'
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<div class="bg-zinc-900/30 border border-zinc-800 rounded-[2rem] overflow-hidden">
|
||||
<div class="bg-zinc-50/30 border border-zinc-200 rounded-[2rem] overflow-hidden">
|
||||
<!-- 头部 -->
|
||||
<div class="flex items-center justify-between p-6 border-b border-zinc-800 bg-zinc-900/20">
|
||||
<div class="flex items-center justify-between p-6 border-b border-zinc-200 bg-zinc-50/20">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-2xl bg-blue-600/10 flex items-center justify-center border border-blue-500/20"
|
||||
class="w-10 h-10 rounded-2xl bg-blue-50 flex items-center justify-center border border-blue-200"
|
||||
>
|
||||
<Mail :size="20" class="text-blue-500" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm font-black text-zinc-100 uppercase tracking-widest">邮件模板管理</h3>
|
||||
<h3 class="text-sm font-black text-zinc-900 uppercase tracking-widest">邮件模板管理</h3>
|
||||
<p class="text-[10px] text-zinc-500 mt-0.5">自定义系统邮件的主题与内容,支持变量替换</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
v-if="selected"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-400 text-[11px] font-bold rounded-xl transition-all"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-400 text-[11px] font-bold rounded-xl transition-all"
|
||||
:disabled="saving"
|
||||
@click="doPreview"
|
||||
>
|
||||
@@ -35,14 +35,14 @@
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 min-h-[650px]">
|
||||
<!-- 左侧:模板列表 -->
|
||||
<div class="lg:col-span-3 border-r border-zinc-800 bg-zinc-900/10 flex flex-col">
|
||||
<div class="p-4 border-b border-zinc-800/50">
|
||||
<div class="lg:col-span-3 border-r border-zinc-200 bg-zinc-50/10 flex flex-col">
|
||||
<div class="p-4 border-b border-zinc-200">
|
||||
<div class="relative">
|
||||
<Search :size="14" class="absolute left-3 top-1/2 -translate-y-1/2 text-zinc-600" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索模板..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg pl-9 pr-3 py-2 text-[10px] text-zinc-400 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg pl-9 pr-3 py-2 text-[10px] text-zinc-400 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -53,8 +53,8 @@
|
||||
class="w-full group flex flex-col gap-1 p-3 rounded-2xl transition-all text-left border"
|
||||
:class="[
|
||||
selectedKey === t.key
|
||||
? 'bg-blue-600/10 border-blue-500/20'
|
||||
: 'bg-transparent border-transparent hover:bg-zinc-800/40'
|
||||
? 'bg-blue-50 border-blue-200'
|
||||
: 'bg-transparent border-transparent hover:bg-zinc-100/40'
|
||||
]"
|
||||
@click="select(t)"
|
||||
>
|
||||
@@ -63,8 +63,8 @@
|
||||
class="text-[11px] font-black tracking-tight transition-colors"
|
||||
:class="
|
||||
selectedKey === t.key
|
||||
? 'text-blue-400'
|
||||
: 'text-zinc-300 group-hover:text-zinc-100'
|
||||
? 'text-blue-700'
|
||||
: 'text-zinc-600 group-hover:text-zinc-900'
|
||||
"
|
||||
>
|
||||
{{ t.name }}
|
||||
@@ -72,7 +72,7 @@
|
||||
<div class="flex gap-1">
|
||||
<span
|
||||
v-if="t.isBuiltin && !t.isOverridden"
|
||||
class="px-1.5 py-0.5 rounded-md bg-zinc-800 text-[8px] font-black text-zinc-500 uppercase tracking-tighter"
|
||||
class="px-1.5 py-0.5 rounded-md bg-zinc-100 text-[8px] font-black text-zinc-500 uppercase tracking-tighter"
|
||||
>
|
||||
内置
|
||||
</span>
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 右侧:编辑器区域 -->
|
||||
<div class="lg:col-span-9 flex flex-col bg-zinc-950/20 overflow-hidden">
|
||||
<div class="lg:col-span-9 flex flex-col bg-white/50 overflow-hidden">
|
||||
<div v-if="selected" class="flex-1 overflow-y-auto custom-scrollbar p-8">
|
||||
<div class="max-w-4xl mx-auto space-y-8">
|
||||
<!-- 基础信息与主题 -->
|
||||
@@ -102,7 +102,7 @@
|
||||
<input
|
||||
v-model="form.name"
|
||||
type="text"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-3 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-3 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
@@ -116,7 +116,7 @@
|
||||
v-model="form.subject"
|
||||
type="text"
|
||||
placeholder="请输入邮件主题..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-3 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-3 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,7 +129,7 @@
|
||||
<Info :size="16" class="text-blue-500" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<h4 class="text-[10px] font-black text-blue-400 uppercase tracking-widest">
|
||||
<h4 class="text-[10px] font-black text-blue-700 uppercase tracking-widest">
|
||||
可用变量
|
||||
</h4>
|
||||
<p class="text-[11px] text-zinc-500 leading-relaxed font-mono">
|
||||
@@ -151,13 +151,13 @@
|
||||
>HTML 内容</label
|
||||
>
|
||||
<span
|
||||
class="px-2 py-0.5 rounded-md bg-zinc-900 text-[8px] font-black text-zinc-500 uppercase"
|
||||
class="px-2 py-0.5 rounded-md bg-white text-[8px] font-black text-zinc-500 uppercase"
|
||||
>Handlebars</span
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
v-if="selected.isOverridden"
|
||||
class="flex items-center gap-1.5 text-[10px] font-bold text-rose-500 hover:text-rose-400 transition-colors"
|
||||
class="flex items-center gap-1.5 text-[10px] font-bold text-rose-500 hover:text-rose-700 transition-colors"
|
||||
@click="restore"
|
||||
>
|
||||
<RotateCcw :size="12" /> 恢复默认配置
|
||||
@@ -165,14 +165,14 @@
|
||||
</div>
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="absolute right-4 top-4 p-2 rounded-lg bg-zinc-900/50 border border-zinc-800 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
class="absolute right-4 top-4 p-2 rounded-lg bg-zinc-50/50 border border-zinc-200 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<Code :size="14" class="text-zinc-500" />
|
||||
</div>
|
||||
<textarea
|
||||
v-model="form.html"
|
||||
rows="16"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl p-6 text-[11px] text-zinc-300 font-mono leading-relaxed focus:outline-none focus:border-blue-500/30 custom-scrollbar"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl p-6 text-[11px] text-zinc-600 font-mono leading-relaxed focus:outline-none focus:border-blue-200 custom-scrollbar"
|
||||
placeholder="<html>..."
|
||||
/>
|
||||
</div>
|
||||
@@ -184,7 +184,7 @@
|
||||
enter-from-class="transform translate-y-4 opacity-0"
|
||||
enter-to-class="transform translate-y-0 opacity-100"
|
||||
>
|
||||
<div v-if="previewHtml" class="space-y-4 pt-4 border-t border-zinc-800">
|
||||
<div v-if="previewHtml" class="space-y-4 pt-4 border-t border-zinc-200">
|
||||
<div class="flex items-center justify-between px-1">
|
||||
<label
|
||||
class="text-[10px] font-black text-zinc-600 uppercase tracking-widest flex items-center gap-2"
|
||||
@@ -193,13 +193,13 @@
|
||||
<span class="text-zinc-400 font-normal normal-case">{{ previewSubject }}</span>
|
||||
</label>
|
||||
<button
|
||||
class="text-[10px] font-bold text-zinc-500 hover:text-zinc-300"
|
||||
class="text-[10px] font-bold text-zinc-500 hover:text-zinc-600"
|
||||
@click="previewHtml = ''"
|
||||
>
|
||||
隐藏预览
|
||||
</button>
|
||||
</div>
|
||||
<div class="rounded-2xl overflow-hidden border border-zinc-800 bg-white shadow-2xl">
|
||||
<div class="rounded-2xl overflow-hidden border border-zinc-200 bg-white shadow-2xl">
|
||||
<iframe :srcdoc="previewHtml" class="w-full h-[500px] border-none" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -213,7 +213,7 @@
|
||||
class="flex-1 flex flex-col items-center justify-center text-zinc-700 space-y-4"
|
||||
>
|
||||
<div
|
||||
class="w-16 h-16 rounded-3xl bg-zinc-900/50 flex items-center justify-center border border-zinc-800/50"
|
||||
class="w-16 h-16 rounded-3xl bg-zinc-50/50 flex items-center justify-center border border-zinc-200"
|
||||
>
|
||||
<Mail :size="32" class="text-zinc-800" />
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h3 class="text-lg font-bold text-zinc-100 flex items-center gap-2">
|
||||
<CalendarDays class="w-5 h-5 text-blue-400" />
|
||||
<h3 class="text-lg font-bold text-zinc-900 flex items-center gap-2">
|
||||
<CalendarDays class="w-5 h-5 text-blue-700" />
|
||||
期望播出时段设置
|
||||
</h3>
|
||||
<button
|
||||
@@ -17,39 +17,39 @@
|
||||
<Loader2 class="w-6 h-6 animate-spin mx-auto text-zinc-500" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="dates.length === 0" class="text-center py-10 text-zinc-500 bg-zinc-900/50 rounded-xl border border-zinc-800">
|
||||
<div v-else-if="dates.length === 0" class="text-center py-10 text-zinc-500 bg-zinc-50/50 rounded-xl border border-zinc-200">
|
||||
暂未设置任何开放日期
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div v-for="date in dates" :key="date.id" class="bg-zinc-900 border border-zinc-800 rounded-xl p-4 flex flex-col gap-3">
|
||||
<div v-for="date in dates" :key="date.id" class="bg-white border border-zinc-200 rounded-xl p-4 flex flex-col gap-3">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="font-bold text-zinc-100 flex items-center gap-2">
|
||||
<div class="font-bold text-zinc-900 flex items-center gap-2">
|
||||
{{ date.date }}
|
||||
<span class="text-xs font-normal text-zinc-500 bg-zinc-800 px-2 py-0.5 rounded">{{ date.weekday }}</span>
|
||||
<span class="text-xs font-normal text-zinc-500 bg-zinc-100 px-2 py-0.5 rounded">{{ date.weekday }}</span>
|
||||
</div>
|
||||
<button @click="openDeleteDialog(date)" class="text-red-400 hover:text-red-300 p-1 transition-colors" title="删除">
|
||||
<Trash2 class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2.5 bg-zinc-950/50 p-3 rounded-lg border border-zinc-800/50">
|
||||
<div class="flex flex-col gap-2.5 bg-white/70 p-3 rounded-lg border border-zinc-200">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xs text-zinc-500">开放范围</div>
|
||||
<div class="text-xs font-mono text-blue-400 font-medium break-all text-right ml-4">{{ formatSequences(date.availableSequences) }}</div>
|
||||
<div class="text-xs font-mono text-blue-700 font-medium break-all text-right ml-4">{{ formatSequences(date.availableSequences) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xs text-zinc-500">序号状态</div>
|
||||
<div class="text-xs flex gap-2">
|
||||
<span class="text-emerald-500 bg-emerald-500/10 px-1.5 py-0.5 rounded border border-emerald-500/20">空闲 {{ getAvailableCount(date) }}</span>
|
||||
<span class="text-zinc-400 bg-zinc-800 px-1.5 py-0.5 rounded border border-zinc-700">共 {{ getTotalCount(date) }}</span>
|
||||
<span class="text-zinc-400 bg-zinc-100 px-1.5 py-0.5 rounded border border-zinc-200">共 {{ getTotalCount(date) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增详细占用情况列表 -->
|
||||
<div v-if="date.takenSequences && date.takenSequences.length > 0" class="mt-1 pt-3 border-t border-zinc-800">
|
||||
<div v-if="date.takenSequences && date.takenSequences.length > 0" class="mt-1 pt-3 border-t border-zinc-200">
|
||||
<div class="text-xs font-bold text-zinc-500 mb-2 flex items-center justify-between">
|
||||
<span>已占用明细</span>
|
||||
<span class="text-red-400">{{ date.takenSequences.length }} 个</span>
|
||||
@@ -61,12 +61,12 @@
|
||||
class="flex items-center gap-2 text-[11px]"
|
||||
>
|
||||
<span class="w-5 h-5 rounded flex items-center justify-center bg-red-500/10 text-red-500 font-bold border border-red-500/20 shrink-0">{{ seq }}</span>
|
||||
<span class="text-zinc-300 truncate" :title="date.takenDetails?.[seq]?.title">{{ date.takenDetails?.[seq]?.title || '未知歌曲' }}</span>
|
||||
<span class="text-zinc-600 truncate" :title="date.takenDetails?.[seq]?.title">{{ date.takenDetails?.[seq]?.title || '未知歌曲' }}</span>
|
||||
<span class="text-zinc-500 truncate" :title="date.takenDetails?.[seq]?.artist">- {{ date.takenDetails?.[seq]?.artist || '未知歌手' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="mt-2 pt-3 border-t border-zinc-800 text-xs text-emerald-500/70 font-bold">
|
||||
<div v-else class="mt-2 pt-3 border-t border-zinc-200 text-xs text-emerald-500/70 font-bold">
|
||||
所有序号目前空闲
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,22 +74,22 @@
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<div v-if="showModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||
<div class="bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl w-full max-w-lg p-6">
|
||||
<div class="bg-white border border-zinc-200 rounded-xl shadow-2xl w-full max-w-lg p-6">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-lg font-bold text-zinc-100">新增开放日期</h3>
|
||||
<h3 class="text-lg font-bold text-zinc-900">新增开放日期</h3>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="flex p-1 bg-zinc-950 rounded-lg mb-6">
|
||||
<div class="flex p-1 bg-white rounded-lg mb-6">
|
||||
<button
|
||||
@click="activeTab = 'auto'"
|
||||
:class="['flex-1 py-2 text-xs font-bold rounded-md transition-colors', activeTab === 'auto' ? 'bg-blue-600 text-white' : 'text-zinc-500 hover:text-zinc-300']"
|
||||
:class="['flex-1 py-2 text-xs font-bold rounded-md transition-colors', activeTab === 'auto' ? 'bg-blue-600 text-white' : 'text-zinc-500 hover:text-zinc-600']"
|
||||
>
|
||||
批量自动分配
|
||||
</button>
|
||||
<button
|
||||
@click="activeTab = 'manual'; nextTick(() => { setTimeout(() => { setStartSeq(manualFormData.startSeq); setEndSeq(manualFormData.endSeq) }, 100) })"
|
||||
:class="['flex-1 py-2 text-xs font-bold rounded-md transition-colors', activeTab === 'manual' ? 'bg-blue-600 text-white' : 'text-zinc-500 hover:text-zinc-300']"
|
||||
:class="['flex-1 py-2 text-xs font-bold rounded-md transition-colors', activeTab === 'manual' ? 'bg-blue-600 text-white' : 'text-zinc-500 hover:text-zinc-600']"
|
||||
>
|
||||
手动配置单日
|
||||
</button>
|
||||
@@ -104,22 +104,22 @@
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-zinc-500 mb-1">总序号数量</label>
|
||||
<input type="number" min="1" v-model="autoFormData.totalSequences" class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-3 py-2 text-zinc-200">
|
||||
<input type="number" min="1" v-model="autoFormData.totalSequences" class="w-full bg-white border border-zinc-200 rounded-lg px-3 py-2 text-zinc-700">
|
||||
<div class="flex items-center gap-2 mt-2">
|
||||
<button @click="autoFormData.totalSequences = 21" class="text-[10px] px-2 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded transition-colors">21</button>
|
||||
<button @click="autoFormData.totalSequences = 28" class="text-[10px] px-2 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded transition-colors">28</button>
|
||||
<button @click="autoFormData.totalSequences = 35" class="text-[10px] px-2 py-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded transition-colors">35</button>
|
||||
<button @click="autoFormData.totalSequences = 21" class="text-[10px] px-2 py-1 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 rounded transition-colors">21</button>
|
||||
<button @click="autoFormData.totalSequences = 28" class="text-[10px] px-2 py-1 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 rounded transition-colors">28</button>
|
||||
<button @click="autoFormData.totalSequences = 35" class="text-[10px] px-2 py-1 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 rounded transition-colors">35</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3 bg-blue-500/10 border border-blue-500/20 rounded-lg">
|
||||
<p class="text-xs text-blue-400 font-bold mb-1">自动分配预览:</p>
|
||||
<div class="p-3 bg-blue-500/10 border border-blue-200 rounded-lg">
|
||||
<p class="text-xs text-blue-700 font-bold mb-1">自动分配预览:</p>
|
||||
<p class="text-[10px] text-blue-300/80">
|
||||
<span v-if="autoFormData.dates.length === 0">请先选择日期</span>
|
||||
<span v-else>将会把 1 - {{ autoFormData.totalSequences }} 号平均分配到选中的 {{ autoFormData.dates.length }} 天中。每天约 {{ Math.ceil(autoFormData.totalSequences / autoFormData.dates.length) }} 个序号。</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button @click="showModal = false" class="flex-1 py-2 bg-zinc-800 text-zinc-300 rounded-lg text-sm font-bold">取消</button>
|
||||
<button @click="showModal = false" class="flex-1 py-2 bg-zinc-100 text-zinc-600 rounded-lg text-sm font-bold">取消</button>
|
||||
<button @click="saveAuto" :disabled="saving || autoFormData.dates.length === 0" class="flex-1 py-2 bg-blue-600 text-white rounded-lg text-sm font-bold disabled:opacity-50">批量保存</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,24 +133,24 @@
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-zinc-500 mb-1">开放的空闲序号</label>
|
||||
|
||||
<div class="flex flex-col items-center bg-zinc-950 border border-zinc-800 rounded-xl p-4">
|
||||
<div class="flex flex-col items-center bg-white border border-zinc-200 rounded-xl p-4">
|
||||
<!-- 模拟 iOS 滚轮 (这里用简化的网格选择代替复杂的滚轮组件以保证稳定性和易用性) -->
|
||||
<div class="text-sm font-bold text-zinc-400 mb-3 w-full flex justify-between items-center">
|
||||
<span>选择开放范围</span>
|
||||
<span class="text-blue-400">{{ manualFormData.startSeq }} - {{ manualFormData.endSeq }}</span>
|
||||
<span class="text-blue-700">{{ manualFormData.startSeq }} - {{ manualFormData.endSeq }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 items-center w-full justify-center py-4">
|
||||
<div class="flex flex-col items-center flex-1">
|
||||
<span class="text-[10px] text-zinc-500 mb-2">起始序号</span>
|
||||
<div ref="startScrollRef" class="relative w-full h-32 bg-zinc-900 border border-zinc-700 rounded-lg overflow-hidden snap-y snap-mandatory hide-scrollbar" style="scroll-behavior: smooth;" @scroll="handleStartScroll">
|
||||
<div class="absolute top-1/2 left-0 right-0 h-10 -mt-5 bg-blue-500/10 border-y border-blue-500/30 pointer-events-none z-10"></div>
|
||||
<div ref="startScrollRef" class="relative w-full h-32 bg-white border border-zinc-200 rounded-lg overflow-hidden snap-y snap-mandatory hide-scrollbar" style="scroll-behavior: smooth;" @scroll="handleStartScroll">
|
||||
<div class="absolute top-1/2 left-0 right-0 h-10 -mt-5 bg-blue-500/10 border-y border-blue-200 pointer-events-none z-10"></div>
|
||||
<div class="h-[44px]"></div> <!-- Padding top -->
|
||||
<div
|
||||
v-for="n in 100"
|
||||
:key="'start-'+n"
|
||||
class="h-10 flex items-center justify-center text-lg font-bold snap-center cursor-pointer transition-colors"
|
||||
:class="manualFormData.startSeq === n ? 'text-blue-400' : 'text-zinc-500 hover:text-zinc-300'"
|
||||
:class="manualFormData.startSeq === n ? 'text-blue-700' : 'text-zinc-500 hover:text-zinc-600'"
|
||||
@click="setStartSeq(n)"
|
||||
:data-val="n"
|
||||
>
|
||||
@@ -164,14 +164,14 @@
|
||||
|
||||
<div class="flex flex-col items-center flex-1">
|
||||
<span class="text-[10px] text-zinc-500 mb-2">结束序号</span>
|
||||
<div ref="endScrollRef" class="relative w-full h-32 bg-zinc-900 border border-zinc-700 rounded-lg overflow-hidden snap-y snap-mandatory hide-scrollbar" style="scroll-behavior: smooth;" @scroll="handleEndScroll">
|
||||
<div class="absolute top-1/2 left-0 right-0 h-10 -mt-5 bg-blue-500/10 border-y border-blue-500/30 pointer-events-none z-10"></div>
|
||||
<div ref="endScrollRef" class="relative w-full h-32 bg-white border border-zinc-200 rounded-lg overflow-hidden snap-y snap-mandatory hide-scrollbar" style="scroll-behavior: smooth;" @scroll="handleEndScroll">
|
||||
<div class="absolute top-1/2 left-0 right-0 h-10 -mt-5 bg-blue-500/10 border-y border-blue-200 pointer-events-none z-10"></div>
|
||||
<div class="h-[44px]"></div> <!-- Padding top -->
|
||||
<div
|
||||
v-for="n in 100"
|
||||
:key="'end-'+n"
|
||||
class="h-10 flex items-center justify-center text-lg font-bold snap-center cursor-pointer transition-colors"
|
||||
:class="manualFormData.endSeq === n ? 'text-blue-400' : 'text-zinc-500 hover:text-zinc-300'"
|
||||
:class="manualFormData.endSeq === n ? 'text-blue-700' : 'text-zinc-500 hover:text-zinc-600'"
|
||||
@click="setEndSeq(n)"
|
||||
:data-val="n"
|
||||
>
|
||||
@@ -185,7 +185,7 @@
|
||||
<!-- 如果有特定跳过的序号,保留文本框以供进阶编辑 -->
|
||||
<div class="w-full mt-4">
|
||||
<div
|
||||
class="flex justify-between items-center text-[10px] text-zinc-500 cursor-pointer py-2 hover:text-zinc-300 transition-colors"
|
||||
class="flex justify-between items-center text-[10px] text-zinc-500 cursor-pointer py-2 hover:text-zinc-600 transition-colors"
|
||||
@click="showAdvancedManual = !showAdvancedManual"
|
||||
>
|
||||
<span>进阶:自定义跳过某些序号</span>
|
||||
@@ -193,7 +193,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="showAdvancedManual" class="mt-2">
|
||||
<textarea v-model="manualFormData.sequences" rows="2" placeholder="例如: 1,2,3,5 (跳过了4)" class="w-full bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2 text-xs text-zinc-300 resize-none focus:outline-none focus:border-blue-500"></textarea>
|
||||
<textarea v-model="manualFormData.sequences" rows="2" placeholder="例如: 1,2,3,5 (跳过了4)" class="w-full bg-white border border-zinc-200 rounded-lg px-3 py-2 text-xs text-zinc-600 resize-none focus:outline-none focus:border-blue-500"></textarea>
|
||||
<p class="text-[10px] text-zinc-600 mt-1">手动编辑后将以此处文本框内容为准</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,7 +202,7 @@
|
||||
<p class="text-[10px] text-zinc-600 mt-2">注意:手动配置会覆盖该日期原有的设置</p>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button @click="showModal = false" class="flex-1 py-2 bg-zinc-800 text-zinc-300 rounded-lg text-sm font-bold">取消</button>
|
||||
<button @click="showModal = false" class="flex-1 py-2 bg-zinc-100 text-zinc-600 rounded-lg text-sm font-bold">取消</button>
|
||||
<button @click="saveManual" :disabled="saving || !manualFormData.date" class="flex-1 py-2 bg-blue-600 text-white rounded-lg text-sm font-bold disabled:opacity-50">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="max-w-[1400px] mx-auto space-y-8 pb-20">
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">向用户发送通知</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">向用户发送通知</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1">即时推送系统公告、活动信息或重要提醒至指定群体</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-start">
|
||||
<!-- 左侧:编辑区 -->
|
||||
<div class="lg:col-span-7 space-y-6">
|
||||
<div class="bg-zinc-900/30 border border-zinc-800 rounded-[2rem] p-8 shadow-xl">
|
||||
<div class="bg-zinc-50/30 border border-zinc-200 rounded-[2rem] p-8 shadow-xl">
|
||||
<div class="space-y-6">
|
||||
<!-- 标题 -->
|
||||
<div class="space-y-2">
|
||||
@@ -21,7 +21,7 @@
|
||||
v-model="form.title"
|
||||
type="text"
|
||||
placeholder="请输入通知标题"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-5 py-3.5 text-sm focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200 placeholder:text-zinc-800"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-5 py-3.5 text-sm focus:outline-none focus:border-blue-200 transition-all text-zinc-700 placeholder:text-zinc-500"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -33,12 +33,12 @@
|
||||
<textarea
|
||||
v-model="form.content"
|
||||
placeholder="请输入通知详情内容..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-5 py-4 text-sm focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200 placeholder:text-zinc-800 min-h-[160px] resize-none"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-5 py-4 text-sm focus:outline-none focus:border-blue-200 transition-all text-zinc-700 placeholder:text-zinc-500 min-h-[160px] resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 范围选择 -->
|
||||
<div class="space-y-3 pt-4 border-t border-zinc-800/50">
|
||||
<div class="space-y-3 pt-4 border-t border-zinc-200">
|
||||
<label class="text-[10px] font-black text-zinc-600 uppercase tracking-[0.2em] px-1"
|
||||
>通知范围</label
|
||||
>
|
||||
@@ -50,7 +50,7 @@
|
||||
'flex items-center gap-2 px-4 py-2.5 rounded-xl text-[11px] font-black transition-all border',
|
||||
form.scope === opt.id
|
||||
? 'bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-900/20'
|
||||
: 'bg-zinc-950 border-zinc-800 text-zinc-500 hover:border-zinc-700 hover:text-zinc-300'
|
||||
: 'bg-white border-zinc-200 text-zinc-500 hover:border-zinc-200 hover:text-zinc-600'
|
||||
]"
|
||||
@click="form.scope = opt.id"
|
||||
>
|
||||
@@ -120,7 +120,7 @@
|
||||
</div>
|
||||
|
||||
<div v-else-if="form.scope === 'MULTI_CLASS'" key="multi" class="space-y-4">
|
||||
<div class="p-6 bg-zinc-950/50 border border-zinc-800 border-dashed rounded-2xl">
|
||||
<div class="p-6 bg-white/70 border border-zinc-200 border-dashed rounded-2xl">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mb-4">
|
||||
<CustomSelect
|
||||
v-model="multiClassForm.grade"
|
||||
@@ -139,7 +139,7 @@
|
||||
/>
|
||||
<button
|
||||
:disabled="!canAddClass"
|
||||
class="px-4 bg-zinc-800 hover:bg-zinc-700 disabled:opacity-50 disabled:cursor-not-allowed text-zinc-400 font-bold rounded-xl text-xs transition-all"
|
||||
class="px-4 bg-zinc-100 hover:bg-zinc-200 disabled:opacity-50 disabled:cursor-not-allowed text-zinc-400 font-bold rounded-xl text-xs transition-all"
|
||||
@click="addClassToSelection"
|
||||
>
|
||||
添加
|
||||
@@ -151,7 +151,7 @@
|
||||
<div
|
||||
v-for="(cls, index) in form.selectedClasses"
|
||||
:key="index"
|
||||
class="flex items-center gap-2 px-3 py-1.5 bg-zinc-900 border border-zinc-800 rounded-lg group"
|
||||
class="flex items-center gap-2 px-3 py-1.5 bg-white border border-zinc-200 rounded-lg group"
|
||||
>
|
||||
<span class="text-[10px] font-bold text-zinc-400"
|
||||
>{{ cls.grade }} {{ cls.class }}</span
|
||||
@@ -188,7 +188,7 @@
|
||||
v-model="userSearchQuery"
|
||||
type="text"
|
||||
placeholder="搜索并选择用户 (姓名、用户名或ID)..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl pl-12 pr-4 py-3 text-sm focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl pl-12 pr-4 py-3 text-sm focus:outline-none focus:border-blue-200 transition-all text-zinc-700"
|
||||
@input="onUserSearchInput"
|
||||
>
|
||||
</div>
|
||||
@@ -196,9 +196,9 @@
|
||||
<!-- 搜索结果 -->
|
||||
<div
|
||||
v-if="showUserSearchResults && userSearchResults.length > 0"
|
||||
class="bg-zinc-950 border border-zinc-800 rounded-2xl overflow-hidden max-h-[300px] overflow-y-auto custom-scrollbar"
|
||||
class="bg-white border border-zinc-200 rounded-2xl overflow-hidden max-h-[300px] overflow-y-auto custom-scrollbar"
|
||||
>
|
||||
<div class="px-4 py-2 border-b border-zinc-800/60 bg-zinc-900/40">
|
||||
<div class="px-4 py-2 border-b border-zinc-200/60 bg-zinc-50/40">
|
||||
<span class="text-[9px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
>搜索结果 ({{ userSearchResults.length }})</span
|
||||
>
|
||||
@@ -206,10 +206,10 @@
|
||||
<div
|
||||
v-for="user in userSearchResults"
|
||||
:key="user.id"
|
||||
class="flex items-center justify-between p-4 border-b border-zinc-800/30 last:border-0 hover:bg-zinc-800/30 transition-all"
|
||||
class="flex items-center justify-between p-4 border-b border-zinc-200/30 last:border-0 hover:bg-zinc-100/30 transition-all"
|
||||
>
|
||||
<div>
|
||||
<h5 class="text-sm font-bold text-zinc-200">
|
||||
<h5 class="text-sm font-bold text-zinc-700">
|
||||
{{ user.name || user.username }}
|
||||
</h5>
|
||||
<div class="flex items-center gap-2 mt-0.5">
|
||||
@@ -228,7 +228,7 @@
|
||||
</div>
|
||||
<button
|
||||
:disabled="isUserSelected(user.id)"
|
||||
class="px-3 py-1.5 bg-zinc-800 hover:bg-blue-600 hover:text-white disabled:opacity-50 disabled:cursor-not-allowed text-zinc-400 text-[10px] font-black rounded-lg transition-all uppercase"
|
||||
class="px-3 py-1.5 bg-zinc-100 hover:bg-blue-600 hover:text-zinc-900 disabled:opacity-50 disabled:cursor-not-allowed text-zinc-400 text-[10px] font-black rounded-lg transition-all uppercase"
|
||||
@click="addUserToSelection(user)"
|
||||
>
|
||||
{{ isUserSelected(user.id) ? '已选择' : '选择' }}
|
||||
@@ -253,10 +253,10 @@
|
||||
<div
|
||||
v-for="(user, index) in form.selectedUsers"
|
||||
:key="user.id"
|
||||
class="flex items-center gap-2 px-3 py-1.5 bg-zinc-900 border border-zinc-800 rounded-lg group"
|
||||
class="flex items-center gap-2 px-3 py-1.5 bg-white border border-zinc-200 rounded-lg group"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[10px] font-bold text-zinc-300 leading-none">{{
|
||||
<span class="text-[10px] font-bold text-zinc-600 leading-none">{{
|
||||
user.name || user.username
|
||||
}}</span>
|
||||
<span class="text-[8px] text-zinc-600">@{{ user.username }}</span>
|
||||
@@ -302,10 +302,10 @@
|
||||
<button
|
||||
:disabled="loading || !isFormValid"
|
||||
:class="[
|
||||
'flex items-center gap-2 px-10 py-4 bg-zinc-950 border border-zinc-800 text-xs font-black rounded-2xl transition-all uppercase tracking-[0.2em] shadow-lg',
|
||||
'flex items-center gap-2 px-10 py-4 bg-white border border-zinc-200 text-xs font-black rounded-2xl transition-all uppercase tracking-[0.2em] shadow-lg',
|
||||
loading || !isFormValid
|
||||
? 'text-zinc-800 cursor-not-allowed'
|
||||
: 'text-zinc-400 hover:text-zinc-100 hover:border-zinc-600 hover:bg-zinc-900'
|
||||
: 'text-zinc-400 hover:text-zinc-900 hover:border-zinc-300 hover:bg-white'
|
||||
]"
|
||||
@click="sendNotification"
|
||||
>
|
||||
@@ -320,17 +320,17 @@
|
||||
<!-- 右侧:预览区 -->
|
||||
<div class="lg:col-span-5 space-y-6 lg:sticky lg:top-8">
|
||||
<div
|
||||
class="bg-zinc-900/30 border border-zinc-800 rounded-[2rem] p-8 shadow-xl flex flex-col h-full"
|
||||
class="bg-zinc-50/30 border border-zinc-200 rounded-[2rem] p-8 shadow-xl flex flex-col h-full"
|
||||
>
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest mb-6 flex items-center gap-2"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest mb-6 flex items-center gap-2"
|
||||
>
|
||||
<Eye :size="16" class="text-blue-500" /> 通知预览
|
||||
</h3>
|
||||
|
||||
<div class="flex-1 flex flex-col items-center justify-center p-4">
|
||||
<div
|
||||
class="w-full max-w-[320px] bg-[#0c0c0e] border border-zinc-800 rounded-3xl overflow-hidden shadow-2xl relative"
|
||||
class="w-full max-w-[320px] bg-white border border-zinc-200 rounded-3xl overflow-hidden shadow-2xl relative"
|
||||
>
|
||||
<!-- 顶部装饰 -->
|
||||
<div class="h-1.5 bg-blue-600 w-full" />
|
||||
@@ -339,7 +339,7 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="w-8 h-8 rounded-xl bg-blue-600/10 text-blue-500 flex items-center justify-center border border-blue-500/20"
|
||||
class="w-8 h-8 rounded-xl bg-blue-50 text-blue-500 flex items-center justify-center border border-blue-200"
|
||||
>
|
||||
<Bell :size="14" />
|
||||
</div>
|
||||
@@ -356,7 +356,7 @@
|
||||
<h4
|
||||
:class="[
|
||||
'text-sm font-black transition-colors',
|
||||
form.title ? 'text-zinc-100' : 'text-zinc-800 italic'
|
||||
form.title ? 'text-zinc-900' : 'text-zinc-800 italic'
|
||||
]"
|
||||
>
|
||||
{{ form.title || '通知标题' }}
|
||||
@@ -371,7 +371,7 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="pt-4 border-t border-zinc-800/50 flex items-center justify-between">
|
||||
<div class="pt-4 border-t border-zinc-200 flex items-center justify-between">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<Users :size="12" class="text-zinc-700" />
|
||||
<span class="text-[9px] font-black text-zinc-600 uppercase tracking-wider">
|
||||
@@ -379,7 +379,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
class="p-1.5 text-blue-500 hover:bg-blue-600/10 rounded-lg transition-all"
|
||||
class="p-1.5 text-blue-500 hover:bg-blue-50 rounded-lg transition-all"
|
||||
>
|
||||
<MessageSquare :size="14" />
|
||||
</button>
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
<div
|
||||
v-for="(stat, i) in statCards"
|
||||
:key="i"
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6 relative overflow-hidden group hover:border-zinc-700 transition-all shadow-lg shadow-black/20"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-2xl p-6 relative overflow-hidden group hover:border-zinc-200 transition-all shadow-lg shadow-zinc-900/5"
|
||||
>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div
|
||||
:class="[
|
||||
'p-3 rounded-xl border',
|
||||
stat.color === 'blue'
|
||||
? 'bg-blue-500/10 text-blue-500 border-blue-500/20'
|
||||
? 'bg-blue-500/10 text-blue-500 border-blue-200'
|
||||
: stat.color === 'emerald'
|
||||
? 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20'
|
||||
: stat.color === 'pink'
|
||||
? 'bg-pink-500/10 text-pink-500 border-pink-500/20'
|
||||
: 'bg-zinc-500/10 text-zinc-500 border-zinc-500/20'
|
||||
: 'bg-zinc-100 text-zinc-500 border-zinc-200'
|
||||
]"
|
||||
>
|
||||
<component :is="stat.icon" :size="24" />
|
||||
@@ -26,7 +26,7 @@
|
||||
v-if="stat.trend"
|
||||
:class="[
|
||||
'flex items-center gap-1 text-[11px] font-bold',
|
||||
stat.trendDown ? 'text-red-400' : 'text-emerald-400'
|
||||
stat.trendDown ? 'text-red-400' : 'text-emerald-700'
|
||||
]"
|
||||
>
|
||||
<TrendingDown v-if="stat.trendDown" :size="12" />
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-zinc-500 text-sm font-medium">{{ stat.label }}</p>
|
||||
<h4 class="text-3xl font-bold tracking-tight text-zinc-100">{{ stat.value }}</h4>
|
||||
<h4 class="text-3xl font-bold tracking-tight text-zinc-900">{{ stat.value }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,14 +44,14 @@
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
||||
<!-- 最近活动 -->
|
||||
<div
|
||||
class="lg:col-span-5 bg-zinc-900/40 border border-zinc-800 rounded-3xl overflow-hidden flex flex-col shadow-lg shadow-black/20"
|
||||
class="lg:col-span-5 bg-zinc-50/40 border border-zinc-200 rounded-3xl overflow-hidden flex flex-col shadow-lg shadow-zinc-900/5"
|
||||
>
|
||||
<div class="px-6 py-5 border-b border-zinc-800 flex items-center justify-between">
|
||||
<div class="px-6 py-5 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2">
|
||||
<Activity :size="18" class="text-blue-500" /> 最近活动
|
||||
</h3>
|
||||
<button
|
||||
class="p-2 text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="p-2 text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
:class="{ 'animate-spin': loadingActivities }"
|
||||
@click="refreshActivities"
|
||||
>
|
||||
@@ -79,7 +79,7 @@
|
||||
<div
|
||||
v-for="(activity, idx) in recentActivities"
|
||||
:key="idx"
|
||||
class="flex items-start gap-4 p-4 rounded-2xl hover:bg-zinc-800/40 transition-all cursor-pointer group"
|
||||
class="flex items-start gap-4 p-4 rounded-2xl hover:bg-zinc-100/40 transition-all cursor-pointer group"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
@@ -91,7 +91,7 @@
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h5
|
||||
class="font-bold text-sm text-zinc-200 group-hover:text-blue-400 transition-colors"
|
||||
class="font-bold text-sm text-zinc-700 group-hover:text-blue-700 transition-colors"
|
||||
>
|
||||
{{ activity.title }}
|
||||
</h5>
|
||||
@@ -110,9 +110,9 @@
|
||||
|
||||
<!-- 系统状态 -->
|
||||
<div
|
||||
class="lg:col-span-4 bg-zinc-900/40 border border-zinc-800 rounded-xl overflow-hidden flex flex-col shadow-lg shadow-black/20"
|
||||
class="lg:col-span-4 bg-zinc-50/40 border border-zinc-200 rounded-xl overflow-hidden flex flex-col shadow-lg shadow-zinc-900/5"
|
||||
>
|
||||
<div class="px-6 py-5 border-b border-zinc-800 flex items-center justify-between">
|
||||
<div class="px-6 py-5 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2">
|
||||
<ShieldCheck :size="18" class="text-emerald-500" /> 系统状态
|
||||
</h3>
|
||||
@@ -144,16 +144,16 @@
|
||||
/>
|
||||
<span class="text-xs font-semibold text-zinc-400">{{ status.label }}</span>
|
||||
</div>
|
||||
<span class="text-xs font-bold text-zinc-200">{{ status.value }}</span>
|
||||
<span class="text-xs font-bold text-zinc-700">{{ status.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速操作 -->
|
||||
<div
|
||||
class="lg:col-span-3 bg-zinc-900/40 border border-zinc-800 rounded-3xl overflow-hidden flex flex-col shadow-lg shadow-black/20"
|
||||
class="lg:col-span-3 bg-zinc-50/40 border border-zinc-200 rounded-3xl overflow-hidden flex flex-col shadow-lg shadow-zinc-900/5"
|
||||
>
|
||||
<div class="px-6 py-5 border-b border-zinc-800">
|
||||
<div class="px-6 py-5 border-b border-zinc-200">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2">
|
||||
<Zap :size="18" class="text-yellow-500" /> 快速操作
|
||||
</h3>
|
||||
@@ -166,7 +166,7 @@
|
||||
'w-full flex items-center gap-3 px-5 py-4 rounded-lg border font-bold text-sm transition-all text-left group',
|
||||
action.primary
|
||||
? 'bg-blue-600 border-blue-500 text-white shadow-xl shadow-blue-900/20 hover:bg-blue-500'
|
||||
: 'bg-zinc-950/40 border-zinc-800 text-zinc-400 hover:border-zinc-600 hover:text-zinc-200'
|
||||
: 'bg-white/80 border-zinc-200 text-zinc-400 hover:border-zinc-300 hover:text-zinc-700'
|
||||
]"
|
||||
@click="navigateTo(action.id)"
|
||||
>
|
||||
@@ -302,9 +302,9 @@ const getActivityStyle = (type) => {
|
||||
const styles = {
|
||||
song: { icon: Music, bg: 'bg-purple-500/10 text-purple-500 border-purple-500/20' },
|
||||
user: { icon: Users, bg: 'bg-pink-500/10 text-pink-500 border-pink-500/20' },
|
||||
schedule: { icon: Calendar, bg: 'bg-blue-500/10 text-blue-500 border-blue-500/20' }
|
||||
schedule: { icon: Calendar, bg: 'bg-blue-500/10 text-blue-500 border-blue-200' }
|
||||
}
|
||||
return styles[type] || { icon: Activity, bg: 'bg-zinc-500/10 text-zinc-500 border-zinc-500/20' }
|
||||
return styles[type] || { icon: Activity, bg: 'bg-zinc-100 text-zinc-500 border-zinc-200' }
|
||||
}
|
||||
|
||||
const loadStats = async () => {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<!-- 页面标题 -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">播出时段管理</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">播出时段管理</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1">
|
||||
定义校园广播的常规播放窗口,用户仅可在这些时间段内进行点歌或查看排期
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<div
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-xl px-4 py-2 flex items-center gap-3"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-xl px-4 py-2 flex items-center gap-3"
|
||||
>
|
||||
<span class="text-[10px] font-black text-zinc-500 uppercase tracking-widest"
|
||||
>启用播出时段选择</span
|
||||
@@ -18,7 +18,7 @@
|
||||
<button
|
||||
:class="[
|
||||
'relative w-10 h-5 rounded-full transition-colors',
|
||||
enablePlayTimeSelection ? 'bg-blue-600' : 'bg-zinc-800'
|
||||
enablePlayTimeSelection ? 'bg-blue-600' : 'bg-zinc-100'
|
||||
]"
|
||||
@click="toggleGlobalEnabled"
|
||||
>
|
||||
@@ -30,16 +30,16 @@
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="playTimes.length > 1" class="flex items-center gap-2 bg-zinc-900/40 border border-zinc-800 rounded-xl px-3 py-2">
|
||||
<div v-if="playTimes.length > 1" class="flex items-center gap-2 bg-zinc-50/40 border border-zinc-200 rounded-xl px-3 py-2">
|
||||
<span class="text-[10px] font-black text-zinc-500 uppercase tracking-widest">批量</span>
|
||||
<button
|
||||
class="px-3 py-1.5 text-[10px] font-black bg-emerald-600/20 text-emerald-400 border border-emerald-500/30 rounded-lg hover:bg-emerald-600/30 transition-all"
|
||||
class="px-3 py-1.5 text-[10px] font-black bg-emerald-600/20 text-emerald-700 border border-emerald-200 rounded-lg hover:bg-emerald-600/30 transition-all"
|
||||
@click="batchToggleAll(true)"
|
||||
>
|
||||
全部启用
|
||||
</button>
|
||||
<button
|
||||
class="px-3 py-1.5 text-[10px] font-black bg-zinc-800 text-zinc-400 border border-zinc-700 rounded-lg hover:bg-zinc-700 transition-all"
|
||||
class="px-3 py-1.5 text-[10px] font-black bg-zinc-100 text-zinc-400 border border-zinc-200 rounded-lg hover:bg-zinc-200 transition-all"
|
||||
@click="batchToggleAll(false)"
|
||||
>
|
||||
全部禁用
|
||||
@@ -58,7 +58,7 @@
|
||||
<div class="bg-blue-600/5 border border-blue-500/10 rounded-xl p-5 flex items-start gap-4">
|
||||
<Info class="text-blue-500 shrink-0 mt-0.5" :size="18" />
|
||||
<div class="space-y-1">
|
||||
<p class="text-xs font-bold text-zinc-300">关于时段限制</p>
|
||||
<p class="text-xs font-bold text-zinc-600">关于时段限制</p>
|
||||
<p class="text-[11px] text-zinc-500 leading-relaxed">
|
||||
启用全局时段选择后,系统将仅在定义的时段内开放点歌功能。如果未设置开始或结束时间,系统将视为该边界不设限。
|
||||
</p>
|
||||
@@ -68,7 +68,7 @@
|
||||
<!-- 加载状态 -->
|
||||
<div
|
||||
v-if="loading"
|
||||
class="flex flex-col items-center justify-center py-20 space-y-4 bg-zinc-900/20 border border-zinc-800/50 rounded-xl"
|
||||
class="flex flex-col items-center justify-center py-20 space-y-4 bg-zinc-50/20 border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<div
|
||||
class="w-10 h-10 border-4 border-blue-600/20 border-t-blue-600 rounded-full animate-spin"
|
||||
@@ -83,7 +83,7 @@
|
||||
>
|
||||
<AlertCircle class="text-red-500 shrink-0 mt-0.5" :size="18" />
|
||||
<div class="space-y-1">
|
||||
<p class="text-xs font-bold text-zinc-300">获取数据失败</p>
|
||||
<p class="text-xs font-bold text-zinc-600">获取数据失败</p>
|
||||
<p class="text-[11px] text-zinc-500 leading-relaxed">{{ error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,13 +91,13 @@
|
||||
<!-- 空状态 -->
|
||||
<div
|
||||
v-else-if="playTimes.length === 0"
|
||||
class="flex flex-col items-center justify-center py-20 space-y-6 bg-zinc-900/20 border border-zinc-800/50 rounded-xl"
|
||||
class="flex flex-col items-center justify-center py-20 space-y-6 bg-zinc-50/20 border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<div class="p-6 rounded-xl bg-zinc-950 border border-zinc-800 text-zinc-700">
|
||||
<div class="p-6 rounded-xl bg-white border border-zinc-200 text-zinc-700">
|
||||
<Clock :size="48" />
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<h3 class="text-lg font-bold text-zinc-100">暂无播出时段</h3>
|
||||
<h3 class="text-lg font-bold text-zinc-900">暂无播出时段</h3>
|
||||
<p class="text-xs text-zinc-500">点击“添加播出时段”按钮创建第一个播出时段</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,15 +109,15 @@
|
||||
v-for="playTime in playTimes"
|
||||
:key="playTime.id"
|
||||
:class="[
|
||||
'group relative bg-zinc-900/30 border rounded-xl p-8 transition-all hover:shadow-2xl hover:shadow-black/40',
|
||||
playTime.enabled ? 'border-zinc-800/80' : 'border-zinc-800/40 opacity-60'
|
||||
'group relative bg-zinc-50/30 border rounded-xl p-8 transition-all hover:shadow-2xl hover:shadow-black/40',
|
||||
playTime.enabled ? 'border-zinc-200/80' : 'border-zinc-200 opacity-60'
|
||||
]"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-6">
|
||||
<div
|
||||
:class="[
|
||||
'p-3 rounded-xl bg-zinc-950 border border-zinc-800 transition-all',
|
||||
playTime.enabled ? 'text-blue-500 border-blue-500/20' : 'text-zinc-700'
|
||||
'p-3 rounded-xl bg-white border border-zinc-200 transition-all',
|
||||
playTime.enabled ? 'text-blue-500 border-blue-200' : 'text-zinc-700'
|
||||
]"
|
||||
>
|
||||
<Clock :size="20" />
|
||||
@@ -128,7 +128,7 @@
|
||||
'px-2 py-0.5 rounded text-[9px] font-black uppercase tracking-widest border transition-all',
|
||||
playTime.enabled
|
||||
? 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20'
|
||||
: 'bg-zinc-800/50 text-zinc-600 border-zinc-700/50'
|
||||
: 'bg-zinc-100/50 text-zinc-600 border-zinc-200'
|
||||
]"
|
||||
>
|
||||
{{ playTime.enabled ? '已启用' : '已禁用' }}
|
||||
@@ -142,7 +142,7 @@
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h4
|
||||
class="text-lg font-black text-zinc-100 group-hover:text-blue-400 transition-colors"
|
||||
class="text-lg font-black text-zinc-900 group-hover:text-blue-700 transition-colors"
|
||||
>
|
||||
{{ playTime.name }}
|
||||
</h4>
|
||||
@@ -150,7 +150,7 @@
|
||||
class="flex items-center gap-2 mt-1.5 text-blue-500/80 font-black tracking-tighter"
|
||||
>
|
||||
<span class="text-xl">{{ playTime.startTime || '不限' }}</span>
|
||||
<div class="w-4 h-[2px] bg-zinc-800" />
|
||||
<div class="w-4 h-[2px] bg-zinc-100" />
|
||||
<span class="text-xl">{{ playTime.endTime || '不限' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,23 +160,23 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 pt-6 border-t border-zinc-800/50 flex items-center justify-between">
|
||||
<div class="mt-8 pt-6 border-t border-zinc-200 flex items-center justify-between">
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="p-2.5 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-500 hover:text-emerald-400 hover:border-emerald-500/30 transition-all"
|
||||
class="p-2.5 bg-white border border-zinc-200 rounded-lg text-zinc-500 hover:text-emerald-700 hover:border-emerald-200 transition-all"
|
||||
title="复制此时段"
|
||||
@click="duplicatePlayTime(playTime)"
|
||||
>
|
||||
<Copy :size="14" />
|
||||
</button>
|
||||
<button
|
||||
class="p-2.5 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-500 hover:text-blue-400 hover:border-blue-500/30 transition-all"
|
||||
class="p-2.5 bg-white border border-zinc-200 rounded-lg text-zinc-500 hover:text-blue-700 hover:border-blue-200 transition-all"
|
||||
@click="editPlayTime(playTime)"
|
||||
>
|
||||
<Edit2 :size="14" />
|
||||
</button>
|
||||
<button
|
||||
class="p-2.5 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-500 hover:text-red-400 hover:border-red-500/30 transition-all"
|
||||
class="p-2.5 bg-white border border-zinc-200 rounded-lg text-zinc-500 hover:text-red-400 hover:border-red-200 transition-all"
|
||||
@click="confirmDelete(playTime)"
|
||||
>
|
||||
<Trash2 :size="14" />
|
||||
@@ -186,7 +186,7 @@
|
||||
:class="[
|
||||
'flex items-center gap-2 px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all',
|
||||
playTime.enabled
|
||||
? 'bg-zinc-800 text-zinc-400 hover:text-zinc-200'
|
||||
? 'bg-zinc-100 text-zinc-400 hover:text-zinc-700'
|
||||
: 'bg-emerald-600 text-white shadow-lg shadow-emerald-900/20'
|
||||
]"
|
||||
@click="togglePlayTimeStatus(playTime)"
|
||||
@@ -207,15 +207,15 @@
|
||||
>
|
||||
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm" @click="cancelForm" />
|
||||
<div
|
||||
class="relative w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden"
|
||||
class="relative w-full max-w-lg bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden"
|
||||
>
|
||||
<div class="p-8">
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight">
|
||||
{{ editingPlayTime ? '编辑播出时段' : '添加播出时段' }}
|
||||
</h3>
|
||||
<button
|
||||
class="p-2 text-zinc-500 hover:text-zinc-200 transition-colors"
|
||||
class="p-2 text-zinc-500 hover:text-zinc-700 transition-colors"
|
||||
@click="cancelForm"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -231,7 +231,7 @@
|
||||
v-for="p in playTimePresets"
|
||||
:key="p.name"
|
||||
type="button"
|
||||
class="px-3 py-2 bg-zinc-950 border border-zinc-800 rounded-lg text-[11px] font-bold text-zinc-400 hover:text-zinc-200 hover:border-zinc-700 hover:bg-zinc-900 transition-all"
|
||||
class="px-3 py-2 bg-white border border-zinc-200 rounded-lg text-[11px] font-bold text-zinc-400 hover:text-zinc-700 hover:border-zinc-200 hover:bg-white transition-all"
|
||||
@click="applyPreset(p)"
|
||||
>
|
||||
<span class="block">{{ p.name }}</span>
|
||||
@@ -248,7 +248,7 @@
|
||||
v-model="formData.name"
|
||||
type="text"
|
||||
placeholder="例如: 午间广播"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-5 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-5 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
<input
|
||||
v-model="formData.startTime"
|
||||
type="time"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-5 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 appearance-none"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-5 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 appearance-none"
|
||||
>
|
||||
<Clock
|
||||
class="absolute right-4 top-1/2 -translate-y-1/2 text-zinc-700 pointer-events-none"
|
||||
@@ -278,7 +278,7 @@
|
||||
<input
|
||||
v-model="formData.endTime"
|
||||
type="time"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-5 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 appearance-none"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-5 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 appearance-none"
|
||||
>
|
||||
<Clock
|
||||
class="absolute right-4 top-1/2 -translate-y-1/2 text-zinc-700 pointer-events-none"
|
||||
@@ -298,7 +298,7 @@
|
||||
<textarea
|
||||
v-model="formData.description"
|
||||
placeholder="请输入时段描述信息..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-5 py-4 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 min-h-[100px] resize-none"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-5 py-4 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 min-h-[100px] resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -306,10 +306,10 @@
|
||||
<input
|
||||
v-model="formData.enabled"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded border-zinc-800 bg-zinc-950 accent-blue-600"
|
||||
class="w-4 h-4 rounded border-zinc-200 bg-white accent-blue-600"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-bold text-zinc-300 group-hover:text-blue-400 transition-colors"
|
||||
class="text-xs font-bold text-zinc-600 group-hover:text-blue-700 transition-colors"
|
||||
>启用此播出时段</span
|
||||
>
|
||||
</label>
|
||||
@@ -323,9 +323,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-8 py-6 bg-zinc-950/50 border-t border-zinc-800 flex justify-end gap-3">
|
||||
<div class="px-8 py-6 bg-white/70 border-t border-zinc-200 flex justify-end gap-3">
|
||||
<button
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-300"
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-600"
|
||||
@click="cancelForm"
|
||||
>
|
||||
取消
|
||||
@@ -350,7 +350,7 @@
|
||||
@click="showDeleteConfirm = false"
|
||||
/>
|
||||
<div
|
||||
class="relative w-full max-w-sm bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden"
|
||||
class="relative w-full max-w-sm bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden"
|
||||
>
|
||||
<div class="p-8">
|
||||
<div class="flex flex-col items-center py-4 space-y-6">
|
||||
@@ -360,7 +360,7 @@
|
||||
<AlertCircle :size="32" />
|
||||
</div>
|
||||
<div class="text-center space-y-2 px-4">
|
||||
<h4 class="text-lg font-bold text-zinc-100">
|
||||
<h4 class="text-lg font-bold text-zinc-900">
|
||||
确定要删除播出时段 "{{ playTimeToDelete?.name }}" 吗?
|
||||
</h4>
|
||||
<p class="text-xs text-zinc-500 leading-relaxed">
|
||||
@@ -369,7 +369,7 @@
|
||||
</div>
|
||||
<div class="flex gap-3 w-full pt-4">
|
||||
<button
|
||||
class="flex-1 px-4 py-3 bg-zinc-950 border border-zinc-800 text-zinc-500 text-xs font-black rounded-lg transition-all hover:bg-zinc-800"
|
||||
class="flex-1 px-4 py-3 bg-white border border-zinc-200 text-zinc-500 text-xs font-black rounded-lg transition-all hover:bg-zinc-100"
|
||||
@click="showDeleteConfirm = false"
|
||||
>
|
||||
取消
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
<Transition name="scale">
|
||||
<div
|
||||
v-if="show"
|
||||
class="bg-zinc-900 border border-zinc-800 rounded-2xl shadow-2xl w-full max-w-sm overflow-hidden flex flex-col"
|
||||
class="bg-white border border-zinc-200 rounded-2xl shadow-2xl w-full max-w-sm overflow-hidden flex flex-col"
|
||||
@click.stop
|
||||
>
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-800">
|
||||
<h3 class="text-sm font-black text-zinc-100 uppercase tracking-widest">生成歌单卡片</h3>
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-200">
|
||||
<h3 class="text-sm font-black text-zinc-900 uppercase tracking-widest">生成歌单卡片</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
||||
@@ -29,22 +29,22 @@
|
||||
v-model="cardTitle"
|
||||
type="text"
|
||||
placeholder="例如: 2026-03-21 校园午间广播"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-3 py-2 text-sm text-zinc-300 focus:outline-none focus:border-blue-500/50 transition-colors"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-3 py-2 text-sm text-zinc-600 focus:outline-none focus:border-blue-500/50 transition-colors"
|
||||
@input="drawCanvas"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Canvas 预览 -->
|
||||
<div class="relative w-full aspect-[2/3] max-w-[280px] bg-zinc-950 rounded-xl overflow-hidden border border-zinc-800 shadow-inner flex items-center justify-center">
|
||||
<div class="relative w-full aspect-[2/3] max-w-[280px] bg-white rounded-xl overflow-hidden border border-zinc-200 shadow-inner flex items-center justify-center">
|
||||
<canvas ref="canvasRef" width="400" :height="canvasHeight" class="w-full h-full object-contain bg-white"></canvas>
|
||||
</div>
|
||||
|
||||
<p class="text-[10px] text-zinc-500 text-center">宽度 400 像素,高度自适应,适合在手机端查看和分享</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 border-t border-zinc-800 bg-zinc-900/50 flex gap-3">
|
||||
<div class="p-4 border-t border-zinc-200 bg-zinc-50/50 flex gap-3">
|
||||
<button
|
||||
class="flex-1 py-2.5 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-bold rounded-xl transition-colors uppercase tracking-wider"
|
||||
class="flex-1 py-2.5 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 text-xs font-bold rounded-xl transition-colors uppercase tracking-wider"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
取消
|
||||
@@ -210,7 +210,7 @@ const drawCanvas = () => {
|
||||
|
||||
// 绘制副标题(日期)
|
||||
ctx.font = '14px "Inter", "system-ui", sans-serif'
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.75)'
|
||||
ctx.fillText(new Date().toLocaleDateString('zh-CN'), 200, 80)
|
||||
|
||||
// 绘制列表背景
|
||||
@@ -278,7 +278,7 @@ const drawCanvas = () => {
|
||||
|
||||
// 绘制底部水印
|
||||
if (bgImageObj.value) {
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.6)'
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.55)'
|
||||
} else {
|
||||
ctx.fillStyle = '#CBD5E1'
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<div class="flex flex-col space-y-8 pb-12 lg:pb-20 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h2 class="text-2xl font-bold tracking-tight text-zinc-100">导入酷狗</h2>
|
||||
<h2 class="text-2xl font-bold tracking-tight text-zinc-900">导入酷狗</h2>
|
||||
<p class="text-sm text-zinc-500">选择排期日期,生成对应的广播排期卡片,用于扫描导入酷狗音乐</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-8">
|
||||
<!-- 设置面板 -->
|
||||
<div class="w-full flex flex-col gap-6">
|
||||
<div class="bg-zinc-900/40 border border-zinc-800 rounded-xl p-6 space-y-6 shadow-lg">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2 text-zinc-100 shrink-0">
|
||||
<div class="bg-zinc-50/40 border border-zinc-200 rounded-xl p-6 space-y-6 shadow-lg">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2 text-zinc-900 shrink-0">
|
||||
<Layout class="w-4 h-4 text-blue-500" /> 选择日期
|
||||
</h3>
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
<!-- 预览区 -->
|
||||
<div class="w-full flex flex-col gap-4">
|
||||
<template v-if="Object.keys(schedulesByDate).length > 0">
|
||||
<div v-for="(schedules, date) in schedulesByDate" :key="date" class="bg-zinc-900/40 border border-zinc-800 rounded-xl p-6 shadow-lg mb-4">
|
||||
<div v-for="(schedules, date) in schedulesByDate" :key="date" class="bg-zinc-50/40 border border-zinc-200 rounded-xl p-6 shadow-lg mb-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-bold text-zinc-100">{{ date }} 排期预览 ({{ schedules.length }} 首)</h3>
|
||||
<h3 class="text-lg font-bold text-zinc-900">{{ date }} 排期预览 ({{ schedules.length }} 首)</h3>
|
||||
<button
|
||||
:disabled="schedules.length === 0"
|
||||
class="px-4 py-2 bg-emerald-600 hover:bg-emerald-500 text-white text-xs font-bold rounded-xl shadow-lg shadow-emerald-900/20 transition-colors flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@@ -52,10 +52,10 @@
|
||||
</div>
|
||||
|
||||
<div v-if="schedules.length > 0" class="space-y-3">
|
||||
<div v-for="(schedule, index) in schedules" :key="schedule.id || index" class="flex items-center gap-3 p-3 bg-zinc-950/50 border border-zinc-800/50 rounded-lg">
|
||||
<div v-for="(schedule, index) in schedules" :key="schedule.id || index" class="flex items-center gap-3 p-3 bg-white/70 border border-zinc-200 rounded-lg">
|
||||
<span class="text-blue-500 font-bold w-6">{{ index + 1 }}.</span>
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<p class="text-sm text-zinc-200 font-medium truncate">{{ schedule.song?.title || schedule.title }}</p>
|
||||
<p class="text-sm text-zinc-700 font-medium truncate">{{ schedule.song?.title || schedule.title }}</p>
|
||||
<p class="text-xs text-zinc-500 truncate">{{ schedule.song?.artist || schedule.artist }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,7 +65,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="!loading && selectedDates.length > 0" class="flex flex-col items-center justify-center py-20 bg-zinc-900/20 border border-zinc-800/50 rounded-xl border-dashed">
|
||||
<div v-else-if="!loading && selectedDates.length > 0" class="flex flex-col items-center justify-center py-20 bg-zinc-50/20 border border-zinc-200 rounded-xl border-dashed">
|
||||
<CalendarDays class="w-12 h-12 text-zinc-700 mb-4" />
|
||||
<p class="text-zinc-500">所选日期均没有排期歌曲</p>
|
||||
</div>
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<!-- 顶部标题和全局开关 -->
|
||||
<div class="flex flex-col xl:flex-row xl:items-end justify-between gap-6">
|
||||
<div class="space-y-1">
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">投稿开放管理</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">投稿开放管理</h2>
|
||||
<p class="text-xs text-zinc-500">管理全校师生点歌投稿的开放时间、限制数量及全局状态控制</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-4 bg-zinc-900/40 border border-zinc-800 p-2 rounded-3xl"
|
||||
class="flex flex-wrap items-center gap-4 bg-zinc-50/40 border border-zinc-200 p-2 rounded-3xl"
|
||||
>
|
||||
<div class="flex items-center gap-3 px-3 border-r border-zinc-800/50 mr-1">
|
||||
<div class="flex items-center gap-3 px-3 border-r border-zinc-200 mr-1">
|
||||
<span class="text-[10px] font-black text-zinc-500 uppercase tracking-widest"
|
||||
>当前状态</span
|
||||
>
|
||||
@@ -31,7 +31,7 @@
|
||||
<button
|
||||
:class="[
|
||||
'relative w-10 h-5 rounded-full transition-colors',
|
||||
enableRequest ? 'bg-blue-600' : 'bg-zinc-800'
|
||||
enableRequest ? 'bg-blue-600' : 'bg-zinc-100'
|
||||
]"
|
||||
@click="toggleGlobalRequest"
|
||||
>
|
||||
@@ -44,14 +44,14 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 px-2 border-l border-zinc-800/50">
|
||||
<div class="flex items-center gap-3 px-2 border-l border-zinc-200">
|
||||
<span class="text-[10px] font-black text-zinc-400 uppercase tracking-widest"
|
||||
>启用时段限制</span
|
||||
>
|
||||
<button
|
||||
:class="[
|
||||
'relative w-10 h-5 rounded-full transition-colors',
|
||||
enableRequestTimeLimitation ? 'bg-purple-600' : 'bg-zinc-800'
|
||||
enableRequestTimeLimitation ? 'bg-purple-600' : 'bg-zinc-100'
|
||||
]"
|
||||
@click="toggleTimeLimitation"
|
||||
>
|
||||
@@ -70,17 +70,17 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<!-- 添加时段按钮卡片 -->
|
||||
<button
|
||||
class="group relative h-full min-h-[220px] bg-zinc-900/20 border-2 border-dashed border-zinc-800 rounded-3xl flex flex-col items-center justify-center gap-4 hover:bg-blue-600/5 hover:border-blue-500/40 transition-all active:scale-95"
|
||||
class="group relative h-full min-h-[220px] bg-zinc-50/20 border-2 border-dashed border-zinc-200 rounded-3xl flex flex-col items-center justify-center gap-4 hover:bg-blue-600/5 hover:border-blue-500/40 transition-all active:scale-95"
|
||||
@click="showAddForm = true"
|
||||
>
|
||||
<div
|
||||
class="w-14 h-14 rounded-full bg-zinc-900 border border-zinc-800 flex items-center justify-center text-zinc-500 group-hover:text-blue-500 group-hover:bg-blue-600/10 group-hover:border-blue-500/20 transition-all"
|
||||
class="w-14 h-14 rounded-full bg-white border border-zinc-200 flex items-center justify-center text-zinc-500 group-hover:text-blue-500 group-hover:bg-blue-50 group-hover:border-blue-200 transition-all"
|
||||
>
|
||||
<Plus :size="28" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h4
|
||||
class="text-sm font-black text-zinc-400 group-hover:text-blue-400 transition-colors uppercase tracking-widest"
|
||||
class="text-sm font-black text-zinc-400 group-hover:text-blue-700 transition-colors uppercase tracking-widest"
|
||||
>
|
||||
添加投稿时段
|
||||
</h4>
|
||||
@@ -101,16 +101,16 @@
|
||||
v-for="slot in requestTimes"
|
||||
:key="slot.id"
|
||||
:class="[
|
||||
'group bg-zinc-900/40 border rounded-3xl p-8 space-y-6 transition-all hover:shadow-2xl hover:shadow-black/40',
|
||||
slot.enabled && !slot.past ? 'border-zinc-800' : 'border-zinc-800/40 opacity-60'
|
||||
'group bg-zinc-50/40 border rounded-3xl p-8 space-y-6 transition-all hover:shadow-2xl hover:shadow-black/40',
|
||||
slot.enabled && !slot.past ? 'border-zinc-200' : 'border-zinc-200 opacity-60'
|
||||
]"
|
||||
>
|
||||
<div class="flex items-start justify-between">
|
||||
<div
|
||||
:class="[
|
||||
'p-3.5 rounded-2xl bg-zinc-950 border border-zinc-800 text-zinc-400 transition-colors',
|
||||
'p-3.5 rounded-2xl bg-white border border-zinc-200 text-zinc-400 transition-colors',
|
||||
slot.enabled && !slot.past
|
||||
? 'text-blue-500 border-blue-500/20 shadow-lg shadow-blue-900/10'
|
||||
? 'text-blue-500 border-blue-200 shadow-lg shadow-blue-900/10'
|
||||
: ''
|
||||
]"
|
||||
>
|
||||
@@ -124,7 +124,7 @@
|
||||
? 'bg-red-500/10 text-red-500 border-red-500/20'
|
||||
: slot.enabled
|
||||
? 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20'
|
||||
: 'bg-zinc-800/50 text-zinc-600 border-zinc-700/50'
|
||||
: 'bg-zinc-100/50 text-zinc-600 border-zinc-200'
|
||||
]"
|
||||
>
|
||||
{{ slot.past ? '已过期' : slot.enabled ? '已启用' : '已禁用' }}
|
||||
@@ -135,7 +135,7 @@
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h4
|
||||
class="text-lg font-black text-zinc-100 flex items-baseline gap-2 group-hover:text-blue-400 transition-colors"
|
||||
class="text-lg font-black text-zinc-900 flex items-baseline gap-2 group-hover:text-blue-700 transition-colors"
|
||||
>
|
||||
{{ slot.name }}
|
||||
<span class="text-xs font-bold text-zinc-600 tracking-tight"
|
||||
@@ -150,7 +150,7 @@
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-zinc-500 font-bold">
|
||||
<div class="w-3 h-[2px] bg-zinc-800 ml-1.5" />
|
||||
<div class="w-3 h-[2px] bg-zinc-100 ml-1.5" />
|
||||
<span class="text-[10px] uppercase tracking-tighter truncate">{{
|
||||
slot.endTime || '不限'
|
||||
}}</span>
|
||||
@@ -179,7 +179,7 @@
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="h-1.5 w-full bg-zinc-950 rounded-full overflow-hidden border border-zinc-800/50"
|
||||
class="h-1.5 w-full bg-white rounded-full overflow-hidden border border-zinc-200"
|
||||
>
|
||||
<div
|
||||
class="h-full transition-all duration-500"
|
||||
@@ -196,17 +196,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-6 border-t border-zinc-800/50 flex items-center justify-between">
|
||||
<div class="pt-6 border-t border-zinc-200 flex items-center justify-between">
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
v-show="!slot.past"
|
||||
class="p-2.5 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-500 hover:text-blue-400 hover:border-blue-500/30 transition-all"
|
||||
class="p-2.5 bg-white border border-zinc-200 rounded-xl text-zinc-500 hover:text-blue-700 hover:border-blue-200 transition-all"
|
||||
@click="editRequestTime(slot)"
|
||||
>
|
||||
<Edit2 :size="14" />
|
||||
</button>
|
||||
<button
|
||||
class="p-2.5 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-500 hover:text-red-400 hover:border-red-500/30 transition-all"
|
||||
class="p-2.5 bg-white border border-zinc-200 rounded-xl text-zinc-500 hover:text-red-400 hover:border-red-200 transition-all"
|
||||
@click="confirmDelete(slot)"
|
||||
>
|
||||
<Trash2 :size="14" />
|
||||
@@ -217,7 +217,7 @@
|
||||
:class="[
|
||||
'flex items-center gap-2 px-5 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest transition-all',
|
||||
slot.enabled
|
||||
? 'bg-zinc-800 text-zinc-400 hover:text-zinc-200'
|
||||
? 'bg-zinc-100 text-zinc-400 hover:text-zinc-700'
|
||||
: 'bg-emerald-600 text-white shadow-lg shadow-emerald-900/20'
|
||||
]"
|
||||
@click="toggleRequestTimeStatus(slot)"
|
||||
@@ -232,12 +232,12 @@
|
||||
|
||||
<!-- 统计概览 -->
|
||||
<div
|
||||
class="bg-zinc-900/20 border border-zinc-800 rounded-3xl p-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"
|
||||
class="bg-zinc-50/20 border border-zinc-200 rounded-3xl p-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"
|
||||
>
|
||||
<div v-for="(stat, i) in stats" :key="i" class="flex items-center gap-4 group">
|
||||
<div
|
||||
:class="[
|
||||
'w-12 h-12 rounded-2xl bg-zinc-950 border border-zinc-800 flex items-center justify-center text-zinc-500 transition-colors',
|
||||
'w-12 h-12 rounded-2xl bg-white border border-zinc-200 flex items-center justify-center text-zinc-500 transition-colors',
|
||||
`group-hover:text-${stat.color}-500`
|
||||
]"
|
||||
>
|
||||
@@ -247,7 +247,7 @@
|
||||
<p class="text-[10px] font-black text-zinc-600 uppercase tracking-widest">
|
||||
{{ stat.label }}
|
||||
</p>
|
||||
<h5 class="text-xl font-black text-zinc-200">{{ stat.value }}</h5>
|
||||
<h5 class="text-xl font-black text-zinc-700">{{ stat.value }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -263,17 +263,17 @@
|
||||
>
|
||||
<div
|
||||
v-if="showAddForm || editingRequestTime"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-white/80 backdrop-blur-sm"
|
||||
@click.self="cancelForm"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-2xl bg-zinc-900 border border-zinc-800 rounded-3xl shadow-2xl overflow-hidden"
|
||||
class="w-full max-w-2xl bg-white border border-zinc-200 rounded-3xl shadow-2xl overflow-hidden"
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800/50 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100">
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900">
|
||||
{{ editingRequestTime ? '编辑投稿开放时段' : '添加投稿开放时段' }}
|
||||
</h3>
|
||||
<button class="text-zinc-500 hover:text-zinc-300 transition-colors" @click="cancelForm">
|
||||
<button class="text-zinc-500 hover:text-zinc-600 transition-colors" @click="cancelForm">
|
||||
<X :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -287,7 +287,7 @@
|
||||
v-model="formData.name"
|
||||
type="text"
|
||||
placeholder="例如: 2025冬季常规点歌"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-5 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-5 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -301,7 +301,7 @@
|
||||
v-for="p in requestTimeQuickPresets"
|
||||
:key="p.label"
|
||||
type="button"
|
||||
class="px-3 py-2 bg-zinc-950 border border-zinc-800 rounded-xl text-[11px] font-bold text-zinc-400 hover:text-zinc-200 hover:border-zinc-700 hover:bg-zinc-900 transition-all whitespace-nowrap"
|
||||
class="px-3 py-2 bg-white border border-zinc-200 rounded-xl text-[11px] font-bold text-zinc-400 hover:text-zinc-700 hover:border-zinc-200 hover:bg-white transition-all whitespace-nowrap"
|
||||
@click="applyRequestTimePreset(p)"
|
||||
>
|
||||
{{ p.label }}
|
||||
@@ -318,12 +318,12 @@
|
||||
<input
|
||||
v-model="formData.startDate"
|
||||
type="date"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-4 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-4 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
<input
|
||||
v-model="formData.startTimeOfDay"
|
||||
type="time"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-4 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-4 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
<p class="text-[9px] text-zinc-600 px-1">
|
||||
@@ -338,12 +338,12 @@
|
||||
<input
|
||||
v-model="formData.endDate"
|
||||
type="date"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-4 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-4 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
<input
|
||||
v-model="formData.endTimeOfDay"
|
||||
type="time"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-4 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-4 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
<p class="text-[9px] text-zinc-600 px-1">
|
||||
@@ -361,7 +361,7 @@
|
||||
<input
|
||||
v-model="formData.expected"
|
||||
type="number"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-5 py-3.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-5 py-3.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
<div
|
||||
class="absolute right-4 top-1/2 -translate-y-1/2 flex items-center gap-1.5 text-zinc-600 font-bold text-[10px]"
|
||||
@@ -377,11 +377,11 @@
|
||||
<input
|
||||
v-model="formData.enabled"
|
||||
type="checkbox"
|
||||
class="w-4.5 h-4.5 rounded-lg border-zinc-800 bg-zinc-950 accent-blue-600"
|
||||
class="w-4.5 h-4.5 rounded-lg border-zinc-200 bg-white accent-blue-600"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="text-xs font-bold text-zinc-300 group-hover:text-blue-400 transition-colors"
|
||||
class="text-xs font-bold text-zinc-600 group-hover:text-blue-700 transition-colors"
|
||||
>启用此投稿时段</span
|
||||
>
|
||||
<p class="text-[9px] text-zinc-600 font-medium">
|
||||
@@ -399,7 +399,7 @@
|
||||
<textarea
|
||||
v-model="formData.description"
|
||||
placeholder="请输入针对此开放周期的特殊规则或介绍信息..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-5 py-4 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 min-h-[100px] resize-none"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-5 py-4 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 min-h-[100px] resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -412,9 +412,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-8 py-6 bg-zinc-900/50 border-t border-zinc-800/50 flex gap-3 justify-end">
|
||||
<div class="px-8 py-6 bg-zinc-50/50 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-300"
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-600"
|
||||
@click="cancelForm"
|
||||
>
|
||||
取消
|
||||
@@ -442,11 +442,11 @@
|
||||
>
|
||||
<div
|
||||
v-if="showDeleteConfirm"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-white/80 backdrop-blur-sm"
|
||||
@click.self="showDeleteConfirm = false"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-md bg-zinc-900 border border-zinc-800 rounded-3xl shadow-2xl overflow-hidden p-8"
|
||||
class="w-full max-w-md bg-white border border-zinc-200 rounded-3xl shadow-2xl overflow-hidden p-8"
|
||||
>
|
||||
<div class="flex flex-col items-center space-y-6">
|
||||
<div
|
||||
@@ -455,17 +455,17 @@
|
||||
<Trash2 :size="28" />
|
||||
</div>
|
||||
<div class="text-center space-y-2 px-4">
|
||||
<h4 class="text-lg font-bold text-zinc-100">
|
||||
<h4 class="text-lg font-bold text-zinc-900">
|
||||
确定要彻底移除 "{{ RequestTimeToDelete?.name }}" 吗?
|
||||
</h4>
|
||||
<p class="text-xs text-zinc-500 leading-relaxed">
|
||||
删除时段后,原本属于此周期的投稿关联记录可能失效。建议先尝试
|
||||
<span class="text-zinc-300 font-bold">禁用</span> 该时段。
|
||||
<span class="text-zinc-600 font-bold">禁用</span> 该时段。
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-3 w-full pt-4">
|
||||
<button
|
||||
class="flex-1 px-4 py-3 bg-zinc-900 hover:bg-zinc-800 text-zinc-500 text-xs font-black rounded-2xl transition-all"
|
||||
class="flex-1 px-4 py-3 bg-white hover:bg-zinc-100 text-zinc-500 text-xs font-black rounded-2xl transition-all"
|
||||
@click="showDeleteConfirm = false"
|
||||
>
|
||||
保留时段
|
||||
@@ -485,9 +485,9 @@
|
||||
|
||||
<!-- 说明部分 -->
|
||||
<div class="flex flex-col md:flex-row gap-6">
|
||||
<div class="flex-1 bg-zinc-900/30 border border-zinc-800 rounded-[2rem] p-6 space-y-4">
|
||||
<div class="flex-1 bg-zinc-50/30 border border-zinc-200 rounded-[2rem] p-6 space-y-4">
|
||||
<h4
|
||||
class="text-xs font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2"
|
||||
class="text-xs font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2"
|
||||
>
|
||||
<CheckCircle2 :size="14" class="text-emerald-500" /> 逻辑优先级说明
|
||||
</h4>
|
||||
@@ -495,16 +495,16 @@
|
||||
<li class="flex gap-2">
|
||||
<span class="text-zinc-700 font-black">1.</span>
|
||||
<span
|
||||
><span class="text-zinc-300">全局开关</span>
|
||||
><span class="text-zinc-600">全局开关</span>
|
||||
具有最高优先级,关闭后所有投稿均不可用。</span
|
||||
>
|
||||
</li>
|
||||
<li class="flex gap-2">
|
||||
<span class="text-zinc-700 font-black">2.</span>
|
||||
<span
|
||||
>开启 <span class="text-zinc-300">时段限制</span> 后,系统会检查当前时间是否处于任一
|
||||
<span class="text-zinc-300">已启用</span> 且未达到
|
||||
<span class="text-zinc-300">计划数</span> 的时段内。</span
|
||||
>开启 <span class="text-zinc-600">时段限制</span> 后,系统会检查当前时间是否处于任一
|
||||
<span class="text-zinc-600">已启用</span> 且未达到
|
||||
<span class="text-zinc-600">计划数</span> 的时段内。</span
|
||||
>
|
||||
</li>
|
||||
<li class="flex gap-2">
|
||||
@@ -514,9 +514,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 bg-zinc-900/30 border border-zinc-800 rounded-[2rem] p-6 space-y-4">
|
||||
<div class="flex-1 bg-zinc-50/30 border border-zinc-200 rounded-[2rem] p-6 space-y-4">
|
||||
<h4
|
||||
class="text-xs font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2"
|
||||
class="text-xs font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2"
|
||||
>
|
||||
<XCircle :size="14" class="text-rose-500" /> 注意事项
|
||||
</h4>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="roles-manager">
|
||||
<div class="admin-section-header">
|
||||
<div>
|
||||
<h2 class="text-xl font-black text-zinc-100 flex items-center gap-2">
|
||||
<h2 class="text-xl font-black text-zinc-900 flex items-center gap-2">
|
||||
<Shield class="w-6 h-6 text-indigo-400" />
|
||||
权限组管理
|
||||
</h2>
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<div v-else-if="error" class="py-12 text-center text-red-400">
|
||||
{{ error }}
|
||||
<button class="mt-4 px-4 py-2 bg-zinc-800 rounded-lg hover:bg-zinc-700" @click="fetchRoles">
|
||||
<button class="mt-4 px-4 py-2 bg-zinc-100 rounded-lg hover:bg-zinc-200" @click="fetchRoles">
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
@@ -37,15 +37,15 @@
|
||||
<div
|
||||
v-for="(roleConfig, roleKey) in roles"
|
||||
:key="roleKey"
|
||||
class="role-card p-6 bg-zinc-900/50 border border-zinc-800 rounded-2xl"
|
||||
class="role-card p-6 bg-zinc-50/50 border border-zinc-200 rounded-2xl"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-xl bg-zinc-800 flex items-center justify-center text-zinc-400">
|
||||
<div class="w-10 h-10 rounded-xl bg-zinc-100 flex items-center justify-center text-zinc-400">
|
||||
<Shield class="w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-bold text-zinc-200">{{ roleConfig.displayName }}</h3>
|
||||
<h3 class="font-bold text-zinc-700">{{ roleConfig.displayName }}</h3>
|
||||
<p class="text-xs text-zinc-500 font-mono mt-0.5">{{ roleKey }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,7 +56,7 @@
|
||||
class="hidden"
|
||||
:disabled="roleKey === 'SUPER_ADMIN'"
|
||||
/>
|
||||
<div class="w-10 h-6 bg-zinc-800 rounded-full relative transition-colors" :class="{ 'bg-indigo-500': roleConfig.canAccessAdmin }">
|
||||
<div class="w-10 h-6 bg-zinc-100 rounded-full relative transition-colors" :class="{ 'bg-indigo-500': roleConfig.canAccessAdmin }">
|
||||
<div class="absolute w-4 h-4 bg-white rounded-full top-1 transition-transform" :class="roleConfig.canAccessAdmin ? 'left-5' : 'left-1'" />
|
||||
</div>
|
||||
<span class="text-xs font-bold text-zinc-400">允许进后台</span>
|
||||
@@ -67,20 +67,20 @@
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h4 class="text-xs font-bold text-zinc-500 uppercase tracking-wider">可访问页面</h4>
|
||||
<span class="text-[10px] font-black px-2 py-0.5 rounded-md bg-zinc-800/60 text-zinc-400 border border-zinc-800">
|
||||
<span class="text-[10px] font-black px-2 py-0.5 rounded-md bg-zinc-100/60 text-zinc-400 border border-zinc-200">
|
||||
已启用 {{ roleConfig.pages.length }} 项
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="px-3 py-1.5 text-[11px] font-black rounded-lg bg-zinc-800/60 border border-zinc-800 text-zinc-300 hover:bg-zinc-800 transition-colors"
|
||||
class="px-3 py-1.5 text-[11px] font-black rounded-lg bg-zinc-100/60 border border-zinc-200 text-zinc-600 hover:bg-zinc-100 transition-colors"
|
||||
:disabled="roleKey === 'SUPER_ADMIN'"
|
||||
@click="selectAll(roleKey)"
|
||||
>
|
||||
全选
|
||||
</button>
|
||||
<button
|
||||
class="px-3 py-1.5 text-[11px] font-black rounded-lg bg-zinc-800/60 border border-zinc-800 text-zinc-300 hover:bg-zinc-800 transition-colors"
|
||||
class="px-3 py-1.5 text-[11px] font-black rounded-lg bg-zinc-100/60 border border-zinc-200 text-zinc-600 hover:bg-zinc-100 transition-colors"
|
||||
:disabled="roleKey === 'SUPER_ADMIN'"
|
||||
@click="clearAll(roleKey)"
|
||||
>
|
||||
@@ -109,7 +109,7 @@
|
||||
v-for="page in section.pages"
|
||||
:key="page.id"
|
||||
class="flex items-center gap-2 p-2 rounded-xl border cursor-pointer transition-all"
|
||||
:class="roleConfig.pages.includes(page.id) ? 'bg-indigo-500/10 border-indigo-500/30 text-indigo-200 shadow-[0_0_0_1px_rgba(99,102,241,0.15)]' : 'bg-zinc-800/30 border-zinc-800 text-zinc-500 hover:bg-zinc-800/70'"
|
||||
:class="roleConfig.pages.includes(page.id) ? 'bg-indigo-500/10 border-indigo-500/30 text-indigo-200 shadow-[0_0_0_1px_rgba(99,102,241,0.15)]' : 'bg-zinc-100/30 border-zinc-200 text-zinc-500 hover:bg-zinc-100/70'"
|
||||
>
|
||||
<input
|
||||
v-model="roleConfig.pages"
|
||||
@@ -125,7 +125,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="mt-6 py-8 text-center border border-dashed border-zinc-800 rounded-xl">
|
||||
<div v-else class="mt-6 py-8 text-center border border-dashed border-zinc-200 rounded-xl">
|
||||
<p class="text-sm text-zinc-600">该角色不允许进入后台</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<div
|
||||
class="backdrop-blur-md p-6 rounded-xl border border-white/10 bg-slate-800/70 shadow-2xl max-w-[400px] mx-auto text-zinc-100 transition-transform duration-200 hover:-translate-y-0.5 hover:shadow-[0_15px_30px_rgba(0,0,0,0.2)]"
|
||||
class="backdrop-blur-md p-6 rounded-xl border border-zinc-200 bg-slate-800/70 shadow-2xl max-w-[400px] mx-auto text-zinc-900 transition-transform duration-200 hover:-translate-y-0.5 hover:shadow-[0_15px_30px_rgba(0,0,0,0.2)]"
|
||||
>
|
||||
<h3 class="mb-6 pb-2 border-b border-white/10 text-zinc-100 font-bold text-lg">
|
||||
<h3 class="mb-6 pb-2 border-b border-zinc-200 text-zinc-900 font-bold text-lg">
|
||||
为歌曲 "{{ song?.title }}" 创建排期
|
||||
</h3>
|
||||
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<div class="mb-4">
|
||||
<label class="block mb-2 font-medium text-zinc-100" for="playDate">播放日期</label>
|
||||
<label class="block mb-2 font-medium text-zinc-900" for="playDate">播放日期</label>
|
||||
<input
|
||||
id="playDate"
|
||||
v-model="playDate"
|
||||
class="w-full p-3 border border-white/10 rounded-lg text-base bg-[#0f172a99] text-zinc-100 outline-none transition-colors duration-150 focus:border-indigo-500 focus:shadow-[0_0_0_2px_rgba(99,102,241,0.25)]"
|
||||
class="w-full p-3 border border-zinc-200 rounded-lg text-base bg-white/90 text-zinc-900 outline-none transition-colors duration-150 focus:border-indigo-500 focus:shadow-[0_0_0_2px_rgba(99,102,241,0.25)]"
|
||||
required
|
||||
type="date"
|
||||
>
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
<div class="flex justify-between gap-4 mt-6">
|
||||
<button
|
||||
class="flex-1 p-3 border border-white/10 rounded-lg text-base cursor-pointer transition-all duration-200 bg-white/10 text-zinc-100 hover:bg-white/15"
|
||||
class="flex-1 p-3 border border-zinc-200 rounded-lg text-base cursor-pointer transition-all duration-200 bg-white/10 text-zinc-900 hover:bg-white/15"
|
||||
type="button"
|
||||
@click="$emit('cancel')"
|
||||
>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<!-- 播出时段标签组 + 自动排期策略 -->
|
||||
<div
|
||||
class="space-y-3 bg-zinc-900/30 border border-zinc-800 rounded-xl p-3 md:p-4"
|
||||
class="space-y-3 bg-zinc-50/30 border border-zinc-200 rounded-xl p-3 md:p-4"
|
||||
>
|
||||
<!-- 播出时段:标签按钮组(直接点击切换) -->
|
||||
<div v-if="playTimeEnabled && playTimes.length > 0" class="space-y-2">
|
||||
@@ -25,7 +25,7 @@
|
||||
'px-4 py-2 rounded-xl text-xs font-black border transition-all whitespace-nowrap',
|
||||
selectedPlayTime === ''
|
||||
? 'bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-900/20'
|
||||
: 'bg-zinc-950 border-zinc-800 text-zinc-400 hover:text-zinc-200 hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 text-zinc-400 hover:text-zinc-700 hover:border-zinc-200'
|
||||
]"
|
||||
@click="handlePlayTimeSelect('')"
|
||||
>
|
||||
@@ -43,10 +43,10 @@
|
||||
selectedPlayTime === String(pt.id)
|
||||
? pt.enabled
|
||||
? 'bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-900/20'
|
||||
: 'bg-zinc-700 border-zinc-600 text-zinc-300'
|
||||
: 'bg-zinc-200 border-zinc-300 text-zinc-600'
|
||||
: pt.enabled
|
||||
? 'bg-zinc-950 border-zinc-800 text-zinc-400 hover:text-zinc-200 hover:border-zinc-700'
|
||||
: 'bg-zinc-950/40 border-zinc-800/40 text-zinc-700 cursor-not-allowed'
|
||||
? 'bg-white border-zinc-200 text-zinc-400 hover:text-zinc-700 hover:border-zinc-200'
|
||||
: 'bg-white/80 border-zinc-200 text-zinc-700 cursor-not-allowed'
|
||||
]"
|
||||
@click="pt.enabled && handlePlayTimeSelect(String(pt.id))"
|
||||
>
|
||||
@@ -61,7 +61,7 @@
|
||||
</span>
|
||||
<span
|
||||
v-if="!pt.enabled"
|
||||
class="ml-1 px-1.5 py-0.5 text-[9px] bg-zinc-800 rounded text-zinc-500"
|
||||
class="ml-1 px-1.5 py-0.5 text-[9px] bg-zinc-100 rounded text-zinc-500"
|
||||
>已禁用</span>
|
||||
</span>
|
||||
</button>
|
||||
@@ -82,27 +82,27 @@
|
||||
<!-- 快速统计信息 -->
|
||||
<div
|
||||
v-if="playTimeEnabled && playTimes.length > 0"
|
||||
class="hidden md:flex items-center gap-3 bg-zinc-950/60 border border-zinc-800 rounded-xl px-4 py-2"
|
||||
class="hidden md:flex items-center gap-3 bg-white/80 border border-zinc-200 rounded-xl px-4 py-2"
|
||||
>
|
||||
<div class="flex-1 flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-lg bg-blue-600/20 text-blue-400 flex items-center justify-center">
|
||||
<div class="w-8 h-8 rounded-lg bg-blue-600/20 text-blue-700 flex items-center justify-center">
|
||||
<ListMusic :size="14" />
|
||||
</div>
|
||||
<div class="flex flex-col leading-tight">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest">当前列表</span>
|
||||
<span class="text-sm font-black text-zinc-200">
|
||||
<span class="text-sm font-black text-zinc-700">
|
||||
{{ localScheduledSongs.length }} 首 / {{ playTimeScheduledCount }} 首
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-px h-8 bg-zinc-800" />
|
||||
<div class="w-px h-8 bg-zinc-100" />
|
||||
<div class="flex-1 flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-lg bg-purple-600/20 text-purple-400 flex items-center justify-center">
|
||||
<Inbox :size="14" />
|
||||
</div>
|
||||
<div class="flex flex-col leading-tight">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest">待排歌曲</span>
|
||||
<span class="text-sm font-black text-zinc-200">
|
||||
<span class="text-sm font-black text-zinc-700">
|
||||
{{ filteredUnscheduledSongs.length }} 首
|
||||
</span>
|
||||
</div>
|
||||
@@ -119,7 +119,7 @@
|
||||
|
||||
<div v-else>
|
||||
<div
|
||||
class="lg:hidden sticky -top-4 -mx-4 -mt-4 z-20 flex gap-2 p-2 bg-zinc-950/90 backdrop-blur-md border-b border-zinc-800 shadow-xl mb-4 pt-4 min-h-[64px]"
|
||||
class="lg:hidden sticky -top-4 -mx-4 -mt-4 z-20 flex gap-2 p-2 bg-white/90 backdrop-blur-md border-b border-zinc-200 shadow-xl mb-4 pt-4 min-h-[64px]"
|
||||
>
|
||||
<button
|
||||
:class="[
|
||||
@@ -131,7 +131,7 @@
|
||||
<ListMusic class="w-4 h-4" />
|
||||
<span class="flex items-center gap-1.5"
|
||||
>待排歌曲
|
||||
<span class="px-1.5 py-0.5 bg-zinc-800 text-[10px] rounded text-zinc-400">{{
|
||||
<span class="px-1.5 py-0.5 bg-zinc-100 text-[10px] rounded text-zinc-400">{{
|
||||
filteredUnscheduledSongs.length
|
||||
}}</span></span
|
||||
>
|
||||
@@ -146,7 +146,7 @@
|
||||
<PlaySquare class="w-4 h-4" />
|
||||
<span class="flex items-center gap-1.5"
|
||||
>播放列表
|
||||
<span class="px-1.5 py-0.5 bg-zinc-800 text-[10px] rounded text-zinc-400">{{
|
||||
<span class="px-1.5 py-0.5 bg-zinc-100 text-[10px] rounded text-zinc-400">{{
|
||||
localScheduledSongs.length
|
||||
}}</span></span
|
||||
>
|
||||
@@ -166,11 +166,11 @@
|
||||
@drop.stop.prevent="handleReturnToDraggable"
|
||||
>
|
||||
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-2 md:gap-3 px-1 min-h-[52px]">
|
||||
<h3 class="hidden lg:block text-lg font-black tracking-tight text-zinc-100 uppercase">
|
||||
<h3 class="hidden lg:block text-lg font-black tracking-tight text-zinc-900 uppercase">
|
||||
待排歌曲
|
||||
</h3>
|
||||
<div
|
||||
class="flex w-full lg:w-auto gap-2 p-1.5 bg-zinc-900/50 rounded-xl border border-zinc-800"
|
||||
class="flex w-full lg:w-auto gap-2 p-1.5 bg-zinc-50/50 rounded-xl border border-zinc-200"
|
||||
>
|
||||
<button
|
||||
v-for="tab in [
|
||||
@@ -182,7 +182,7 @@
|
||||
:class="[
|
||||
'flex-1 lg:flex-none px-3 md:px-4 py-2 rounded-lg text-[10px] md:text-xs font-black uppercase tracking-widest transition-all',
|
||||
activeTab === tab.id
|
||||
? 'bg-zinc-800 text-blue-400 shadow-md border border-blue-500/20'
|
||||
? 'bg-zinc-100 text-blue-700 shadow-md border border-blue-200'
|
||||
: 'text-zinc-600 hover:text-zinc-400'
|
||||
]"
|
||||
@click="activeTab = tab.id"
|
||||
@@ -193,9 +193,9 @@
|
||||
</div>
|
||||
|
||||
<!-- 筛选区 - 移动端折叠 -->
|
||||
<div class="bg-zinc-900/40 border border-zinc-800 rounded-2xl shadow-xl">
|
||||
<div class="bg-zinc-50/40 border border-zinc-200 rounded-2xl shadow-xl">
|
||||
<div
|
||||
class="p-3 md:p-4 flex items-center justify-between lg:hidden border-b border-zinc-800/50 rounded-t-2xl"
|
||||
class="p-3 md:p-4 flex items-center justify-between lg:hidden border-b border-zinc-200 rounded-t-2xl"
|
||||
@click="mobileFiltersOpen = !mobileFiltersOpen"
|
||||
>
|
||||
<div class="flex items-center gap-2 text-zinc-400">
|
||||
@@ -222,7 +222,7 @@
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="搜索歌曲、艺术家..."
|
||||
class="w-full pl-9 pr-4 py-2 bg-zinc-950 border border-zinc-800 rounded-xl text-xs focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200"
|
||||
class="w-full pl-9 pr-4 py-2 bg-white border border-zinc-200 rounded-xl text-xs focus:outline-none focus:border-blue-200 transition-all text-zinc-700"
|
||||
>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
@@ -254,14 +254,14 @@
|
||||
'draggable-songs flex-1 border-2 border-dashed rounded-[2rem] p-2 md:p-3 min-h-[400px] transition-colors duration-200',
|
||||
isDraggableOver
|
||||
? 'border-blue-500 bg-blue-500/5'
|
||||
: 'border-zinc-800/80 bg-zinc-900/20'
|
||||
: 'border-zinc-200/80 bg-zinc-50/20'
|
||||
]"
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="song in filteredUnscheduledSongs"
|
||||
:key="song.id"
|
||||
class="draggable-song relative group bg-zinc-900 border border-zinc-800/50 rounded-xl p-3 hover:border-zinc-700 transition-all select-none"
|
||||
class="draggable-song relative group bg-white border border-zinc-200 rounded-xl p-3 hover:border-zinc-200 transition-all select-none"
|
||||
draggable="true"
|
||||
@dragend="dragEnd"
|
||||
@dragstart="dragStart($event, song)"
|
||||
@@ -273,7 +273,7 @@
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- 封面图片 -->
|
||||
<div
|
||||
class="relative w-12 h-12 rounded-lg overflow-hidden bg-zinc-800 flex-shrink-0 border border-zinc-700/50 cursor-pointer hover:opacity-80 transition-opacity"
|
||||
class="relative w-12 h-12 rounded-lg overflow-hidden bg-zinc-100 flex-shrink-0 border border-zinc-200 cursor-pointer hover:opacity-80 transition-opacity"
|
||||
@click.stop="playSong(song)"
|
||||
>
|
||||
<img
|
||||
@@ -294,10 +294,10 @@
|
||||
|
||||
<div class="flex-1 min-w-0 flex flex-col gap-0.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<h4 class="font-bold text-zinc-100 text-sm truncate">
|
||||
<h4 class="font-bold text-zinc-900 text-sm truncate">
|
||||
<span
|
||||
v-if="false"
|
||||
class="text-zinc-100 flex items-center gap-1 w-full text-left"
|
||||
class="text-zinc-900 flex items-center gap-1 w-full text-left"
|
||||
>
|
||||
<span class="truncate">{{ song.title }}</span>
|
||||
</span>
|
||||
@@ -305,7 +305,7 @@
|
||||
</h4>
|
||||
<button
|
||||
v-if="song.hasSubmissionNote && song.submissionNote"
|
||||
class="inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-500/30 bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 transition-all flex-shrink-0"
|
||||
class="inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-200 bg-blue-500/10 text-blue-700 hover:bg-blue-500/20 transition-all flex-shrink-0"
|
||||
title="查看备注留言"
|
||||
@click.stop="openSubmissionRemark(song)"
|
||||
>
|
||||
@@ -342,7 +342,7 @@
|
||||
<!-- 普通模式:投票数 -->
|
||||
<div
|
||||
v-if="activeTab !== 'replay'"
|
||||
class="flex items-center gap-1 text-[10px] font-bold text-zinc-500 bg-zinc-950/50 px-2 py-1 rounded-md border border-zinc-800/50"
|
||||
class="flex items-center gap-1 text-[10px] font-bold text-zinc-500 bg-white/70 px-2 py-1 rounded-md border border-zinc-200"
|
||||
>
|
||||
<Heart class="w-3 h-3 text-red-500/50" />
|
||||
{{ song.voteCount || 0 }}
|
||||
@@ -351,7 +351,7 @@
|
||||
<!-- 重播模式:查看按钮 -->
|
||||
<button
|
||||
v-if="activeTab === 'replay'"
|
||||
class="px-3 py-1.5 rounded-lg bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/20 text-[10px] font-bold transition-colors"
|
||||
class="px-3 py-1.5 rounded-lg bg-blue-500/10 hover:bg-blue-500/20 text-blue-700 border border-blue-200 text-[10px] font-bold transition-colors"
|
||||
@click.stop="openReplayModal(song)"
|
||||
>
|
||||
查看
|
||||
@@ -369,7 +369,7 @@
|
||||
|
||||
<!-- 移动端添加按钮 -->
|
||||
<button
|
||||
class="lg:hidden p-2 rounded-full bg-blue-600/20 text-blue-400 hover:bg-blue-600/30 active:scale-95 transition-all flex-shrink-0"
|
||||
class="lg:hidden p-2 rounded-full bg-blue-600/20 text-blue-700 hover:bg-blue-600/30 active:scale-95 transition-all flex-shrink-0"
|
||||
@click.stop="addSongToSchedule(song)"
|
||||
>
|
||||
<Plus class="w-5 h-5" />
|
||||
@@ -377,7 +377,7 @@
|
||||
|
||||
<!-- 菜单按钮 -->
|
||||
<div
|
||||
class="p-1.5 rounded-lg bg-zinc-950 border border-zinc-800 text-zinc-600 cursor-grab active:cursor-grabbing hover:text-zinc-400 transition-colors"
|
||||
class="p-1.5 rounded-lg bg-white border border-zinc-200 text-zinc-600 cursor-grab active:cursor-grabbing hover:text-zinc-400 transition-colors"
|
||||
>
|
||||
<MoreVertical class="w-4 h-4" />
|
||||
</div>
|
||||
@@ -422,81 +422,81 @@
|
||||
<div
|
||||
class="hidden lg:flex flex-col xl:flex-row xl:items-center justify-between gap-3 md:gap-4 px-1 min-h-[56px]"
|
||||
>
|
||||
<h3 class="text-lg font-black tracking-tight text-zinc-100 uppercase">播放顺序</h3>
|
||||
<h3 class="text-lg font-black tracking-tight text-zinc-900 uppercase">播放顺序</h3>
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-1.5 md:gap-2 p-1.5 bg-zinc-900/50 border border-zinc-800/50 rounded-2xl"
|
||||
class="flex flex-wrap items-center gap-1.5 md:gap-2 p-1.5 bg-zinc-50/50 border border-zinc-200 rounded-2xl"
|
||||
>
|
||||
<div class="flex gap-1 md:gap-1.5">
|
||||
<button
|
||||
:disabled="
|
||||
!hasChanges && localScheduledSongs.length === 0 && !hasUnpublishedDrafts
|
||||
"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 hover:bg-zinc-800 text-zinc-500 hover:text-zinc-200 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="p-2 bg-white border border-zinc-200 hover:bg-zinc-100 text-zinc-500 hover:text-zinc-700 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="saveDraft"
|
||||
>
|
||||
<Save class="w-3.5 h-3.5" />
|
||||
<span
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-800 text-[9px] text-zinc-300 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-700"
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-100 text-[9px] text-zinc-600 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-200"
|
||||
>保存草稿</span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
:disabled="localScheduledSongs.length === 0"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 hover:bg-zinc-800 text-zinc-500 hover:text-zinc-200 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="p-2 bg-white border border-zinc-200 hover:bg-zinc-100 text-zinc-500 hover:text-zinc-700 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="openDownloadDialog"
|
||||
>
|
||||
<Download class="w-3.5 h-3.5" />
|
||||
<span
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-800 text-[9px] text-zinc-300 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-700"
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-100 text-[9px] text-zinc-600 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-200"
|
||||
>下载歌曲</span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
:disabled="localScheduledSongs.length === 0"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 hover:bg-zinc-800 text-zinc-500 hover:text-blue-400 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="p-2 bg-white border border-zinc-200 hover:bg-zinc-100 text-zinc-500 hover:text-blue-700 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="showCardGenerator = true"
|
||||
>
|
||||
<ImageIcon class="w-3.5 h-3.5" />
|
||||
<span
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-800 text-[9px] text-zinc-300 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-700"
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-100 text-[9px] text-zinc-600 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-200"
|
||||
>生成图片</span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
:disabled="localScheduledSongs.length === 0"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 hover:bg-zinc-800 text-zinc-500 hover:text-emerald-500 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="p-2 bg-white border border-zinc-200 hover:bg-zinc-100 text-zinc-500 hover:text-emerald-500 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="markAllAsPlayed"
|
||||
>
|
||||
<CheckCircle2 class="w-3.5 h-3.5" />
|
||||
<span
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-800 text-[9px] text-zinc-300 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-700"
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-100 text-[9px] text-zinc-600 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-200"
|
||||
>全部已播放</span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
:disabled="localScheduledSongs.length === 0"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 hover:bg-zinc-800 text-zinc-500 hover:text-cyan-400 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="p-2 bg-white border border-zinc-200 hover:bg-zinc-100 text-zinc-500 hover:text-cyan-400 rounded-xl transition-all group relative disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
title="构建播放队列"
|
||||
@click="confirmBuildPlayQueue"
|
||||
>
|
||||
<Music2 class="w-3.5 h-3.5" />
|
||||
<span
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-800 text-[9px] text-zinc-300 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-700"
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-100 text-[9px] text-zinc-600 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-200"
|
||||
>构建播放队列</span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 hover:bg-zinc-800 text-zinc-500 hover:text-purple-400 rounded-xl transition-all group relative"
|
||||
class="p-2 bg-white border border-zinc-200 hover:bg-zinc-100 text-zinc-500 hover:text-purple-400 rounded-xl transition-all group relative"
|
||||
@click="openMoveDateDialog"
|
||||
>
|
||||
<ArrowRight class="w-3.5 h-3.5" />
|
||||
<span
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-800 text-[9px] text-zinc-300 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-700"
|
||||
class="absolute -top-10 left-1/2 -translate-x-1/2 px-2 py-1 bg-zinc-100 text-[9px] text-zinc-600 rounded opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap border border-zinc-200"
|
||||
>迁移日期</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<div class="h-6 w-[1px] bg-zinc-800 mx-1" />
|
||||
<div class="h-6 w-[1px] bg-zinc-100 mx-1" />
|
||||
<button
|
||||
class="flex items-center gap-1.5 px-3 md:px-4 py-2 bg-yellow-500/10 hover:bg-yellow-500/20 text-yellow-500 text-[10px] md:text-xs font-black rounded-xl border border-yellow-500/20 transition-all uppercase tracking-widest active:scale-95 disabled:opacity-50"
|
||||
title="一键将当前待排歌曲自动排入"
|
||||
@@ -525,7 +525,7 @@
|
||||
ref="sequenceList"
|
||||
:class="[
|
||||
'sequence-list flex-1 border-2 border-dashed rounded-[2rem] p-2 md:p-3 min-h-[400px] transition-colors duration-200',
|
||||
isSequenceOver ? 'border-blue-500 bg-blue-500/5' : 'border-zinc-800/80 bg-zinc-900/20'
|
||||
isSequenceOver ? 'border-blue-500 bg-blue-500/5' : 'border-zinc-200/80 bg-zinc-50/20'
|
||||
]"
|
||||
@dragleave="handleSequenceDragLeave"
|
||||
@dragover.prevent="handleDragOver"
|
||||
@@ -545,9 +545,9 @@
|
||||
v-for="(schedule, index) in localScheduledSongs"
|
||||
:key="schedule.id"
|
||||
:class="[
|
||||
'scheduled-song relative group bg-zinc-900 border border-zinc-800/50 rounded-xl p-3 hover:border-zinc-700 transition-all select-none',
|
||||
'scheduled-song relative group bg-white border border-zinc-200 rounded-xl p-3 hover:border-zinc-200 transition-all select-none',
|
||||
dragOverIndex === index ? 'border-t-2 border-t-blue-500' : '',
|
||||
schedule.isDraft ? 'border-amber-500/30 bg-amber-500/5' : ''
|
||||
schedule.isDraft ? 'border-amber-200 bg-amber-500/5' : ''
|
||||
]"
|
||||
:data-schedule-id="schedule.id"
|
||||
draggable="true"
|
||||
@@ -563,17 +563,17 @@
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="flex flex-col items-center justify-center w-10 h-10 rounded-lg bg-zinc-950/50 border border-zinc-800 text-zinc-500 font-black text-xs flex-shrink-0"
|
||||
class="flex flex-col items-center justify-center w-10 h-10 rounded-lg bg-white/70 border border-zinc-200 text-zinc-500 font-black text-xs flex-shrink-0"
|
||||
>
|
||||
<span class="text-[8px] text-zinc-600 uppercase leading-none mb-0.5">POS</span>
|
||||
<span class="text-sm text-zinc-300 leading-none">{{
|
||||
<span class="text-sm text-zinc-600 leading-none">{{
|
||||
index + 1 < 10 ? '0' + (index + 1) : index + 1
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<!-- 封面图片 -->
|
||||
<div
|
||||
class="relative w-10 h-10 rounded-lg overflow-hidden bg-zinc-800 flex-shrink-0 border border-zinc-700/50 cursor-pointer hover:opacity-80 transition-opacity"
|
||||
class="relative w-10 h-10 rounded-lg overflow-hidden bg-zinc-100 flex-shrink-0 border border-zinc-200 cursor-pointer hover:opacity-80 transition-opacity"
|
||||
@click.stop="playSong(schedule.song)"
|
||||
>
|
||||
<img
|
||||
@@ -594,12 +594,12 @@
|
||||
|
||||
<div class="flex-1 min-w-0 flex flex-col gap-0.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<h4 class="font-bold text-zinc-200 text-sm truncate">
|
||||
<h4 class="font-bold text-zinc-700 text-sm truncate">
|
||||
{{ schedule.song.title }}
|
||||
</h4>
|
||||
<button
|
||||
v-if="schedule.song.hasSubmissionNote && schedule.song.submissionNote"
|
||||
class="inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-500/30 bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 transition-all flex-shrink-0"
|
||||
class="inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-200 bg-blue-500/10 text-blue-700 hover:bg-blue-500/20 transition-all flex-shrink-0"
|
||||
title="查看备注留言"
|
||||
@click.stop="openSubmissionRemark(schedule.song)"
|
||||
>
|
||||
@@ -608,7 +608,7 @@
|
||||
<!-- 重播标识 -->
|
||||
<span
|
||||
v-if="schedule.song.replayRequestCount > 0"
|
||||
class="px-1.5 py-0.5 rounded text-[9px] font-bold bg-blue-500/10 text-blue-500 border border-blue-500/20 uppercase tracking-wider flex items-center gap-1"
|
||||
class="px-1.5 py-0.5 rounded text-[9px] font-bold bg-blue-500/10 text-blue-500 border border-blue-200 uppercase tracking-wider flex items-center gap-1"
|
||||
title="重播歌曲"
|
||||
>
|
||||
<Icon name="repeat" :size="10" />
|
||||
@@ -682,7 +682,7 @@
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="p-1.5 rounded-lg bg-zinc-950 border border-zinc-800 text-zinc-600 cursor-grab active:cursor-grabbing hover:text-zinc-400 transition-colors"
|
||||
class="p-1.5 rounded-lg bg-white border border-zinc-200 text-zinc-600 cursor-grab active:cursor-grabbing hover:text-zinc-400 transition-colors"
|
||||
>
|
||||
<MoreVertical class="w-4 h-4" />
|
||||
</div>
|
||||
@@ -696,42 +696,42 @@
|
||||
|
||||
<!-- 移动端底部操作栏 -->
|
||||
<div
|
||||
class="lg:hidden fixed bottom-0 left-0 right-0 z-40 p-3 bg-zinc-950/90 backdrop-blur-xl border-t border-zinc-800 flex items-center gap-3 pb-6"
|
||||
class="lg:hidden fixed bottom-0 left-0 right-0 z-40 p-3 bg-white/90 backdrop-blur-xl border-t border-zinc-200 flex items-center gap-3 pb-6"
|
||||
>
|
||||
<div class="w-[240px] overflow-x-auto scrollbar-hide">
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<button
|
||||
class="w-11 h-11 shrink-0 bg-zinc-900 border border-zinc-800 text-zinc-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
class="w-11 h-11 shrink-0 bg-white border border-zinc-200 text-zinc-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
@click="openDownloadDialog"
|
||||
>
|
||||
<Download class="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
class="w-11 h-11 shrink-0 bg-zinc-900 border border-zinc-800 text-zinc-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
class="w-11 h-11 shrink-0 bg-white border border-zinc-200 text-zinc-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
@click="showCardGenerator = true"
|
||||
>
|
||||
<ImageIcon class="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
class="w-11 h-11 shrink-0 bg-zinc-900 border border-zinc-800 text-zinc-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
class="w-11 h-11 shrink-0 bg-white border border-zinc-200 text-zinc-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
@click="saveDraft"
|
||||
>
|
||||
<Save class="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
class="w-11 h-11 shrink-0 bg-zinc-900 border border-zinc-800 text-emerald-500 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
class="w-11 h-11 shrink-0 bg-white border border-zinc-200 text-emerald-500 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
@click="markAllAsPlayed"
|
||||
>
|
||||
<CheckCircle2 class="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
class="w-11 h-11 shrink-0 bg-zinc-900 border border-zinc-800 text-purple-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
class="w-11 h-11 shrink-0 bg-white border border-zinc-200 text-purple-400 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
@click="openMoveDateDialog"
|
||||
>
|
||||
<ArrowRight class="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
class="w-11 h-11 shrink-0 bg-zinc-900 border border-zinc-800 text-blue-500 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
class="w-11 h-11 shrink-0 bg-white border border-zinc-200 text-blue-500 rounded-xl flex items-center justify-center active:scale-95 transition-all"
|
||||
title="仅发布排期"
|
||||
@click="publishSchedule"
|
||||
>
|
||||
@@ -785,13 +785,13 @@
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl w-full max-w-sm overflow-hidden"
|
||||
class="bg-white border border-zinc-200 rounded-xl shadow-2xl w-full max-w-sm overflow-hidden"
|
||||
@click.stop
|
||||
>
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-800">
|
||||
<h3 class="text-sm font-black text-zinc-100 uppercase tracking-widest">迁移排期日期</h3>
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-200">
|
||||
<h3 class="text-sm font-black text-zinc-900 uppercase tracking-widest">迁移排期日期</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
@click="showMoveDateDialog = false"
|
||||
>
|
||||
<CloseIcon class="w-5 h-5" />
|
||||
@@ -801,12 +801,12 @@
|
||||
<div class="text-xs text-zinc-500">当前日期:{{ selectedDate }}</div>
|
||||
<input
|
||||
v-model="moveTargetDate"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-3 text-zinc-200 focus:outline-none focus:border-purple-500 transition-colors"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-3 text-zinc-700 focus:outline-none focus:border-purple-500 transition-colors"
|
||||
type="date"
|
||||
>
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
class="flex-1 py-3 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-bold rounded-xl transition-colors uppercase tracking-wider"
|
||||
class="flex-1 py-3 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 text-xs font-bold rounded-xl transition-colors uppercase tracking-wider"
|
||||
@click="showMoveDateDialog = false"
|
||||
>
|
||||
取消
|
||||
@@ -829,11 +829,11 @@
|
||||
@click="closeReplayModal"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 rounded-2xl shadow-2xl w-full max-w-md overflow-hidden"
|
||||
class="bg-white border border-zinc-200 rounded-2xl shadow-2xl w-full max-w-md overflow-hidden"
|
||||
@click.stop
|
||||
>
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-800">
|
||||
<h3 class="text-sm font-black text-zinc-100 uppercase tracking-widest">
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-200">
|
||||
<h3 class="text-sm font-black text-zinc-900 uppercase tracking-widest">
|
||||
{{ replayModalTitle }} - 重播申请详情
|
||||
</h3>
|
||||
<div class="flex items-center gap-3">
|
||||
@@ -847,7 +847,7 @@
|
||||
拒绝申请
|
||||
</button>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
@click="closeReplayModal"
|
||||
>
|
||||
<CloseIcon class="w-5 h-5" />
|
||||
@@ -863,12 +863,12 @@
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-lg bg-zinc-900 border border-zinc-800 flex items-center justify-center text-zinc-500 group-hover:text-blue-400 transition-colors"
|
||||
class="w-8 h-8 rounded-lg bg-white border border-zinc-200 flex items-center justify-center text-zinc-500 group-hover:text-blue-700 transition-colors"
|
||||
>
|
||||
<User class="w-3.5 h-3.5" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-zinc-200">{{ req.name }}</span>
|
||||
<span class="text-sm font-bold text-zinc-700">{{ req.name }}</span>
|
||||
<span v-if="req.grade" class="text-[10px] text-zinc-500"
|
||||
>{{ req.grade }}{{ req.class ? ` ${req.class}` : '' }}</span
|
||||
>
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
class="flex flex-col space-y-8 pb-12 lg:pb-20 animate-in fade-in slide-in-from-bottom-4 duration-500"
|
||||
>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h2 class="text-2xl font-bold tracking-tight text-zinc-100">打印排期</h2>
|
||||
<h2 class="text-2xl font-bold tracking-tight text-zinc-900">打印排期</h2>
|
||||
<p class="text-sm text-zinc-500">自定义打印设置,预览并导出排期表</p>
|
||||
</div>
|
||||
|
||||
<!-- 权限检查 -->
|
||||
<div
|
||||
v-if="!canPrintSchedule"
|
||||
class="flex flex-col items-center justify-center py-20 bg-zinc-900/50 rounded-xl border border-zinc-800"
|
||||
class="flex flex-col items-center justify-center py-20 bg-zinc-50/50 rounded-xl border border-zinc-200"
|
||||
>
|
||||
<div
|
||||
class="w-16 h-16 bg-red-500/10 rounded-full flex items-center justify-center text-red-500 mb-4"
|
||||
@@ -21,7 +21,7 @@
|
||||
<path d="m9 9 6 6" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-zinc-100">权限不足</h3>
|
||||
<h3 class="text-lg font-bold text-zinc-900">权限不足</h3>
|
||||
<p class="text-zinc-500 mt-2">您没有打印排期的权限,请联系管理员获取相应权限。</p>
|
||||
</div>
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
<!-- 设置面板 -->
|
||||
<div class="lg:col-span-4 flex flex-col gap-6 h-auto">
|
||||
<div
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-xl p-6 space-y-6 shadow-lg flex flex-col h-auto"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-xl p-6 space-y-6 shadow-lg flex flex-col h-auto"
|
||||
>
|
||||
<h3 class="text-lg font-bold flex items-center gap-2 text-zinc-100 shrink-0">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2 text-zinc-900 shrink-0">
|
||||
<Layout class="w-4 h-4 text-blue-500" /> 打印设置
|
||||
</h3>
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
:class="[
|
||||
'py-2.5 rounded-lg text-sm font-bold transition-all',
|
||||
settings.orientation === 'portrait'
|
||||
? 'border border-blue-500/30 bg-blue-600/10 text-blue-400 shadow-sm'
|
||||
: 'border border-zinc-800 bg-zinc-950 text-zinc-500 hover:text-zinc-300'
|
||||
? 'border border-blue-200 bg-blue-50 text-blue-700 shadow-sm'
|
||||
: 'border border-zinc-200 bg-white text-zinc-500 hover:text-zinc-600'
|
||||
]"
|
||||
@click="settings.orientation = 'portrait'"
|
||||
>
|
||||
@@ -70,8 +70,8 @@
|
||||
:class="[
|
||||
'py-2.5 rounded-lg text-sm font-bold transition-all',
|
||||
settings.orientation === 'landscape'
|
||||
? 'border border-blue-500/30 bg-blue-600/10 text-blue-400 shadow-sm'
|
||||
: 'border border-zinc-800 bg-zinc-950 text-zinc-500 hover:text-zinc-300'
|
||||
? 'border border-blue-200 bg-blue-50 text-blue-700 shadow-sm'
|
||||
: 'border border-zinc-200 bg-white text-zinc-500 hover:text-zinc-600'
|
||||
]"
|
||||
@click="settings.orientation = 'landscape'"
|
||||
>
|
||||
@@ -89,13 +89,13 @@
|
||||
<input
|
||||
v-model="settings.startDate"
|
||||
type="date"
|
||||
class="bg-zinc-950 border border-zinc-800 rounded-lg px-3 py-2 text-sm text-zinc-300 focus:outline-none focus:border-blue-500/50 transition-colors"
|
||||
class="bg-white border border-zinc-200 rounded-lg px-3 py-2 text-sm text-zinc-600 focus:outline-none focus:border-blue-500/50 transition-colors"
|
||||
max="9999-12-31"
|
||||
>
|
||||
<input
|
||||
v-model="settings.endDate"
|
||||
type="date"
|
||||
class="bg-zinc-950 border border-zinc-800 rounded-lg px-3 py-2 text-sm text-zinc-300 focus:outline-none focus:border-blue-500/50 transition-colors"
|
||||
class="bg-white border border-zinc-200 rounded-lg px-3 py-2 text-sm text-zinc-600 focus:outline-none focus:border-blue-500/50 transition-colors"
|
||||
max="9999-12-31"
|
||||
>
|
||||
</div>
|
||||
@@ -103,7 +103,7 @@
|
||||
<button
|
||||
v-for="range in dateRanges"
|
||||
:key="range.value"
|
||||
class="px-2 py-1 bg-zinc-800/50 hover:bg-zinc-800 text-[10px] text-zinc-400 rounded-md transition-colors border border-zinc-800"
|
||||
class="px-2 py-1 bg-zinc-100/50 hover:bg-zinc-100 text-[10px] text-zinc-400 rounded-md transition-colors border border-zinc-200"
|
||||
@click="setDateRange(range.value)"
|
||||
>
|
||||
{{ range.label }}
|
||||
@@ -127,7 +127,7 @@
|
||||
'w-5 h-5 rounded flex items-center justify-center border transition-all',
|
||||
settings[option.key]
|
||||
? 'bg-blue-600 border-blue-600'
|
||||
: 'bg-zinc-950 border-zinc-800 group-hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 group-hover:border-zinc-200'
|
||||
]"
|
||||
>
|
||||
<CheckCircle2 v-if="settings[option.key]" class="w-3 h-3 text-white" />
|
||||
@@ -137,7 +137,7 @@
|
||||
:class="[
|
||||
'text-sm font-medium transition-colors',
|
||||
settings[option.key]
|
||||
? 'text-zinc-200'
|
||||
? 'text-zinc-700'
|
||||
: 'text-zinc-500 group-hover:text-zinc-400'
|
||||
]"
|
||||
>{{ option.label }}</span
|
||||
@@ -153,7 +153,7 @@
|
||||
'w-5 h-5 rounded flex items-center justify-center border transition-all',
|
||||
settings.showSchoolLogo
|
||||
? 'bg-blue-600 border-blue-600'
|
||||
: 'bg-zinc-950 border-zinc-800 group-hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 group-hover:border-zinc-200'
|
||||
]"
|
||||
>
|
||||
<CheckCircle2 v-if="settings.showSchoolLogo" class="w-3 h-3 text-white" />
|
||||
@@ -163,7 +163,7 @@
|
||||
:class="[
|
||||
'text-sm font-medium transition-colors',
|
||||
settings.showSchoolLogo
|
||||
? 'text-zinc-200'
|
||||
? 'text-zinc-700'
|
||||
: 'text-zinc-500 group-hover:text-zinc-400'
|
||||
]"
|
||||
>学校Logo</span
|
||||
@@ -180,15 +180,15 @@
|
||||
<textarea
|
||||
v-model="settings.remark"
|
||||
placeholder="请输入备注信息(可选)"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm focus:outline-none text-zinc-300 min-h-[80px] resize-none focus:border-blue-500/50 transition-colors"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm focus:outline-none text-zinc-600 min-h-[80px] resize-none focus:border-blue-500/50 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="space-y-2 pt-4 border-t border-zinc-800 shrink-0">
|
||||
<div class="space-y-2 pt-4 border-t border-zinc-200 shrink-0">
|
||||
<button
|
||||
class="w-full flex items-center justify-center gap-2 py-3 bg-zinc-800/80 hover:bg-zinc-700 text-zinc-300 text-sm font-bold rounded-lg border border-zinc-700 transition-all"
|
||||
class="w-full flex items-center justify-center gap-2 py-3 bg-zinc-100/80 hover:bg-zinc-200 text-zinc-600 text-sm font-bold rounded-lg border border-zinc-200 transition-all"
|
||||
@click="refreshPreview"
|
||||
>
|
||||
<RefreshCw class="w-4 h-4" /> 刷新预览
|
||||
@@ -223,10 +223,10 @@
|
||||
<!-- 预览区域 -->
|
||||
<div class="lg:col-span-8 flex flex-col gap-4 h-[700px] lg:h-0 lg:min-h-full mb-8 lg:mb-0">
|
||||
<div
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-xl overflow-hidden flex flex-col h-full shadow-lg"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-xl overflow-hidden flex flex-col h-full shadow-lg"
|
||||
>
|
||||
<div class="px-6 py-5 border-b border-zinc-800 flex items-center justify-between">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2 text-zinc-100">
|
||||
<div class="px-6 py-5 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-lg font-bold flex items-center gap-2 text-zinc-900">
|
||||
<AlignLeft class="w-4.5 h-4.5 text-zinc-500" /> 预览区域
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 text-xs font-bold">
|
||||
@@ -245,7 +245,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-1 bg-zinc-950/50 p-6 md:p-12 overflow-auto custom-scrollbar flex items-start"
|
||||
class="flex-1 bg-white/70 p-6 md:p-12 overflow-auto custom-scrollbar flex items-start"
|
||||
>
|
||||
<!-- 纸张预览 -->
|
||||
<div
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">学期管理</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">学期管理</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1">
|
||||
定义系统的教学周期,点歌、排期及统计数据将基于当前活跃学期进行归档
|
||||
</p>
|
||||
@@ -134,7 +134,7 @@
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="bg-zinc-900/30 border border-zinc-800 border-dashed rounded-3xl p-20 flex flex-col items-center justify-center text-center gap-4"
|
||||
class="bg-zinc-50/30 border border-zinc-200 border-dashed rounded-3xl p-20 flex flex-col items-center justify-center text-center gap-4"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -158,7 +158,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-zinc-900/20 border border-zinc-800 rounded-3xl flex items-start gap-4">
|
||||
<div class="p-6 bg-zinc-50/20 border border-zinc-200 rounded-3xl flex items-start gap-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
@@ -214,7 +214,7 @@
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-if="loading && semesters.length === 0"
|
||||
class="text-center py-20 bg-zinc-900/10 border border-zinc-800 border-dashed rounded-[2rem]"
|
||||
class="text-center py-20 bg-zinc-50/10 border border-zinc-200 border-dashed rounded-[2rem]"
|
||||
>
|
||||
<p class="text-xs font-bold text-zinc-600">加载中...</p>
|
||||
</div>
|
||||
@@ -225,8 +225,8 @@
|
||||
class="group flex items-center justify-between p-6 rounded-[2rem] border transition-all"
|
||||
:class="
|
||||
sem.isActive
|
||||
? 'bg-blue-600/5 border-blue-500/30'
|
||||
: 'bg-zinc-900/40 border-zinc-800 hover:border-zinc-700'
|
||||
? 'bg-blue-600/5 border-blue-200'
|
||||
: 'bg-zinc-50/40 border-zinc-200 hover:border-zinc-200'
|
||||
"
|
||||
>
|
||||
<div class="flex items-center gap-5">
|
||||
@@ -235,7 +235,7 @@
|
||||
:class="
|
||||
sem.isActive
|
||||
? 'bg-blue-600 text-white shadow-lg shadow-blue-900/20'
|
||||
: 'bg-zinc-800 text-zinc-600 border border-zinc-700'
|
||||
: 'bg-zinc-100 text-zinc-600 border border-zinc-200'
|
||||
"
|
||||
>
|
||||
<svg
|
||||
@@ -258,7 +258,7 @@
|
||||
<h5
|
||||
class="font-bold transition-colors"
|
||||
:class="
|
||||
sem.isActive ? 'text-blue-400' : 'text-zinc-200 group-hover:text-blue-400'
|
||||
sem.isActive ? 'text-blue-700' : 'text-zinc-700 group-hover:text-blue-700'
|
||||
"
|
||||
>
|
||||
{{ sem.name }}
|
||||
@@ -293,7 +293,7 @@
|
||||
<template v-else>
|
||||
<button
|
||||
:disabled="loading"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-400 hover:text-white text-[10px] font-black rounded-xl border border-zinc-700 transition-all uppercase tracking-widest disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-100 hover:bg-zinc-200 text-zinc-400 hover:text-zinc-900 text-[10px] font-black rounded-xl border border-zinc-200 transition-all uppercase tracking-widest disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="setActive(sem.id)"
|
||||
>
|
||||
<svg
|
||||
@@ -342,7 +342,7 @@
|
||||
|
||||
<div
|
||||
v-if="semesters.length === 0 && !loading"
|
||||
class="text-center py-20 bg-zinc-900/10 border border-zinc-800 border-dashed rounded-[2rem]"
|
||||
class="text-center py-20 bg-zinc-50/10 border border-zinc-200 border-dashed rounded-[2rem]"
|
||||
>
|
||||
<p class="text-xs font-bold text-zinc-600">暂无学期数据</p>
|
||||
</div>
|
||||
@@ -356,14 +356,14 @@
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-md bg-zinc-900 rounded-3xl border border-zinc-800 shadow-2xl overflow-hidden"
|
||||
class="w-full max-w-md bg-white rounded-3xl border border-zinc-200 shadow-2xl overflow-hidden"
|
||||
@click.stop
|
||||
>
|
||||
<div class="p-8 space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-white tracking-tight">添加新学期</h3>
|
||||
<button
|
||||
class="p-2 text-zinc-500 hover:text-white transition-colors"
|
||||
class="p-2 text-zinc-500 hover:text-zinc-900 transition-colors"
|
||||
@click="closeAddModal"
|
||||
>
|
||||
<svg
|
||||
@@ -392,7 +392,7 @@
|
||||
v-model="newSemester.name"
|
||||
type="text"
|
||||
placeholder="例如: 2025学年第一学期"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl px-5 py-4 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 font-bold transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl px-5 py-4 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 font-bold transition-all"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -400,11 +400,11 @@
|
||||
<input
|
||||
v-model="newSemester.isActive"
|
||||
type="checkbox"
|
||||
class="w-4.5 h-4.5 rounded-lg border-zinc-800 bg-zinc-950 accent-blue-600 transition-all"
|
||||
class="w-4.5 h-4.5 rounded-lg border-zinc-200 bg-white accent-blue-600 transition-all"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="text-xs font-bold text-zinc-300 group-hover:text-blue-400 transition-colors"
|
||||
class="text-xs font-bold text-zinc-600 group-hover:text-blue-700 transition-colors"
|
||||
>设为当前活跃学期</span
|
||||
>
|
||||
<p class="text-[10px] text-zinc-600 font-medium">
|
||||
@@ -413,7 +413,7 @@
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="p-4 bg-zinc-950 border border-zinc-800 rounded-2xl flex items-start gap-3">
|
||||
<div class="p-4 bg-white border border-zinc-200 rounded-2xl flex items-start gap-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
@@ -437,9 +437,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-zinc-800/50 border-t border-zinc-800 flex gap-3 justify-end">
|
||||
<div class="p-6 bg-zinc-100/50 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
@click="closeAddModal"
|
||||
>
|
||||
取消
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<aside
|
||||
:class="[
|
||||
'fixed inset-y-0 left-0 z-50 w-64 bg-[#09090b] border-r border-zinc-800 transform transition-transform duration-300 ease-in-out lg:translate-x-0',
|
||||
'fixed inset-y-0 left-0 z-50 w-64 bg-[#ffffff] border-r border-zinc-200 shadow-sm transform transition-transform duration-300 ease-in-out lg:translate-x-0',
|
||||
isOpen ? 'translate-x-0' : '-translate-x-full'
|
||||
]"
|
||||
>
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<!-- 品牌文字 -->
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="font-bold text-lg text-zinc-100 leading-none tracking-tight">VoiceHub</h1>
|
||||
<h1 class="font-bold text-lg text-zinc-900 leading-none tracking-tight">VoiceHub</h1>
|
||||
<p
|
||||
class="text-[10px] text-zinc-500 mt-1.5 uppercase tracking-widest font-bold leading-none"
|
||||
>
|
||||
@@ -49,8 +49,8 @@
|
||||
:class="[
|
||||
'w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-[13px] font-bold transition-all group border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/80 focus-visible:ring-inset',
|
||||
activeTab === item.id
|
||||
? 'bg-blue-600/10 text-blue-400 border-blue-500/20'
|
||||
: 'text-zinc-500 hover:text-zinc-200 hover:bg-zinc-800/40 border-transparent',
|
||||
? 'bg-blue-50 text-blue-700 border-blue-200'
|
||||
: 'text-zinc-600 hover:text-zinc-900 hover:bg-zinc-100 border-transparent',
|
||||
getTourClass(item.id)
|
||||
]"
|
||||
@click="onNavigate(item.id)"
|
||||
@@ -61,8 +61,8 @@
|
||||
:size="18"
|
||||
:class="
|
||||
activeTab === item.id
|
||||
? 'text-blue-400'
|
||||
: 'text-zinc-500 group-hover:text-zinc-300'
|
||||
? 'text-blue-700'
|
||||
: 'text-zinc-500 group-hover:text-zinc-700'
|
||||
"
|
||||
/>
|
||||
<span class="truncate">{{ item.label }}</span>
|
||||
@@ -78,26 +78,26 @@
|
||||
</nav>
|
||||
|
||||
<!-- 用户信息及退出登录 -->
|
||||
<div class="mt-4 pt-4 border-t border-zinc-800">
|
||||
<div class="mt-4 pt-4 border-t border-zinc-200">
|
||||
<div
|
||||
class="flex items-center gap-3 p-3 rounded-lg bg-zinc-900/50 border border-zinc-800/50 hover:bg-zinc-800/30 transition-colors"
|
||||
class="flex items-center gap-3 p-3 rounded-lg bg-zinc-50 border border-zinc-200 hover:bg-zinc-100 transition-colors"
|
||||
>
|
||||
<!-- 用户头像/首字母 -->
|
||||
<img
|
||||
v-if="currentUser?.avatar && !avatarError"
|
||||
:src="currentUser.avatar"
|
||||
class="w-10 h-10 rounded-lg object-cover border border-zinc-700 shrink-0"
|
||||
class="w-10 h-10 rounded-lg object-cover border border-zinc-200 shrink-0"
|
||||
@error="avatarError = true"
|
||||
>
|
||||
<div
|
||||
v-else
|
||||
class="w-10 h-10 rounded-lg bg-zinc-800 flex items-center justify-center text-zinc-400 font-bold border border-zinc-700 shrink-0"
|
||||
class="w-10 h-10 rounded-lg bg-zinc-100 flex items-center justify-center text-zinc-600 font-bold border border-zinc-200 shrink-0"
|
||||
>
|
||||
{{ (currentUser?.name || '管').charAt(0) }}
|
||||
</div>
|
||||
<!-- 用户详细信息 -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-xs font-black truncate text-zinc-100">
|
||||
<p class="text-xs font-black truncate text-zinc-900">
|
||||
{{ currentUser?.name || '管理员' }}
|
||||
</p>
|
||||
<p
|
||||
@@ -108,7 +108,7 @@
|
||||
</div>
|
||||
<!-- 退出按钮 -->
|
||||
<button
|
||||
class="p-2 text-zinc-600 hover:text-red-400 hover:bg-red-400/10 rounded-lg transition-all"
|
||||
class="p-2 text-zinc-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-all"
|
||||
title="退出登录"
|
||||
@click="$emit('logout')"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">站点配置</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">站点配置</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1 font-medium">
|
||||
管理站点全局属性、视觉识别、点歌逻辑及系统安全策略
|
||||
</p>
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
:disabled="loading || saving"
|
||||
class="flex items-center gap-2 px-5 py-2 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-400 text-xs font-bold rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="flex items-center gap-2 px-5 py-2 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-400 text-xs font-bold rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="resetForm"
|
||||
>
|
||||
<RotateCcw :size="14" /> 重置
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<div v-if="loading" class="flex flex-col items-center justify-center py-20">
|
||||
<div
|
||||
class="w-8 h-8 border-4 border-zinc-800 border-t-blue-500 rounded-full animate-spin mb-4"
|
||||
class="w-8 h-8 border-4 border-zinc-200 border-t-blue-500 rounded-full animate-spin mb-4"
|
||||
/>
|
||||
<p class="text-zinc-500 text-sm">加载配置中...</p>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
<!-- 基础信息 -->
|
||||
<section :class="cardClass">
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-800 pb-4"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-200 pb-4"
|
||||
>
|
||||
<Globe :size="16" class="text-blue-500" /> 基础信息
|
||||
</h3>
|
||||
@@ -100,7 +100,7 @@
|
||||
<!-- 视觉识别 -->
|
||||
<section :class="cardClass">
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-800 pb-4"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-200 pb-4"
|
||||
>
|
||||
<ImageIcon :size="16" class="text-purple-500" /> 视觉识别
|
||||
</h3>
|
||||
@@ -137,10 +137,10 @@
|
||||
|
||||
<!-- 投稿须知 -->
|
||||
<section
|
||||
class="lg:col-span-2 bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6 space-y-6"
|
||||
class="lg:col-span-2 bg-zinc-50/40 border border-zinc-200 rounded-2xl p-6 space-y-6"
|
||||
>
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-800 pb-4"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-200 pb-4"
|
||||
>
|
||||
<FileText :size="16" class="text-emerald-500" /> 投稿须知
|
||||
</h3>
|
||||
@@ -155,75 +155,75 @@
|
||||
<!-- 投稿逻辑设置 -->
|
||||
<section :class="cardClass">
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-800 pb-4"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-200 pb-4"
|
||||
>
|
||||
<Settings2 :size="16" class="text-amber-500" /> 投稿逻辑设置
|
||||
</h3>
|
||||
<div class="space-y-6">
|
||||
<div
|
||||
class="flex items-center justify-between p-3 bg-zinc-950/50 border border-zinc-800 rounded-xl"
|
||||
class="flex items-center justify-between p-3 bg-white/70 border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-zinc-200">启用联合投稿</p>
|
||||
<p class="text-xs font-bold text-zinc-700">启用联合投稿</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-0.5">允许用户添加联合投稿人并发起协作投稿</p>
|
||||
</div>
|
||||
<input
|
||||
v-model="formData.enableCollaborativeSubmission"
|
||||
type="checkbox"
|
||||
class="w-5 h-5 rounded border-zinc-800 bg-zinc-900 accent-blue-600 cursor-pointer"
|
||||
class="w-5 h-5 rounded border-zinc-200 bg-white accent-blue-600 cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between p-3 bg-zinc-950/50 border border-zinc-800 rounded-xl"
|
||||
class="flex items-center justify-between p-3 bg-white/70 border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-zinc-200">启用投稿备注留言</p>
|
||||
<p class="text-xs font-bold text-zinc-700">启用投稿备注留言</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-0.5">允许用户在投稿时附加公开或仅管理员可见的备注</p>
|
||||
</div>
|
||||
<input
|
||||
v-model="formData.enableSubmissionRemarks"
|
||||
type="checkbox"
|
||||
class="w-5 h-5 rounded border-zinc-800 bg-zinc-900 accent-blue-600 cursor-pointer"
|
||||
class="w-5 h-5 rounded border-zinc-200 bg-white accent-blue-600 cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between p-3 bg-zinc-950/50 border border-zinc-800 rounded-xl"
|
||||
class="flex items-center justify-between p-3 bg-white/70 border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-zinc-200">启用重播申请</p>
|
||||
<p class="text-xs font-bold text-zinc-700">启用重播申请</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-0.5">允许用户对本学期已播放过的歌曲再次申请</p>
|
||||
</div>
|
||||
<input
|
||||
v-model="formData.enableReplayRequests"
|
||||
type="checkbox"
|
||||
class="w-5 h-5 rounded border-zinc-800 bg-zinc-900 accent-blue-600 cursor-pointer"
|
||||
class="w-5 h-5 rounded border-zinc-200 bg-white accent-blue-600 cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
class="flex items-center justify-between p-3 bg-zinc-950/50 border border-zinc-800 rounded-xl"
|
||||
class="flex items-center justify-between p-3 bg-white/70 border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-zinc-200">启用投稿限额</p>
|
||||
<p class="text-xs font-bold text-zinc-700">启用投稿限额</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-0.5">限制单个用户的点歌频率</p>
|
||||
</div>
|
||||
<input
|
||||
v-model="formData.enableSubmissionLimit"
|
||||
type="checkbox"
|
||||
class="w-5 h-5 rounded border-zinc-800 bg-zinc-900 accent-blue-600 cursor-pointer"
|
||||
class="w-5 h-5 rounded border-zinc-200 bg-white accent-blue-600 cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-if="formData.enableSubmissionLimit" class="space-y-4">
|
||||
<div class="grid grid-cols-3 gap-2 p-1 bg-zinc-950 border border-zinc-800 rounded-xl">
|
||||
<div class="grid grid-cols-3 gap-2 p-1 bg-white border border-zinc-200 rounded-xl">
|
||||
<button
|
||||
:class="[
|
||||
'py-2 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all',
|
||||
currentLimitType === 'daily'
|
||||
? 'bg-zinc-800 text-blue-400 shadow-sm'
|
||||
? 'bg-zinc-100 text-blue-700 shadow-sm'
|
||||
: 'text-zinc-600 hover:text-zinc-400'
|
||||
]"
|
||||
@click="handleLimitTypeChange('daily')"
|
||||
@@ -234,7 +234,7 @@
|
||||
:class="[
|
||||
'py-2 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all',
|
||||
currentLimitType === 'weekly'
|
||||
? 'bg-zinc-800 text-blue-400 shadow-sm'
|
||||
? 'bg-zinc-100 text-blue-700 shadow-sm'
|
||||
: 'text-zinc-600 hover:text-zinc-400'
|
||||
]"
|
||||
@click="handleLimitTypeChange('weekly')"
|
||||
@@ -245,7 +245,7 @@
|
||||
:class="[
|
||||
'py-2 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all',
|
||||
currentLimitType === 'monthly'
|
||||
? 'bg-zinc-800 text-blue-400 shadow-sm'
|
||||
? 'bg-zinc-100 text-blue-700 shadow-sm'
|
||||
: 'text-zinc-600 hover:text-zinc-400'
|
||||
]"
|
||||
@click="handleLimitTypeChange('monthly')"
|
||||
@@ -279,23 +279,23 @@
|
||||
<!-- 安全与隐私设置 -->
|
||||
<section :class="cardClass">
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-800 pb-4"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2 border-b border-zinc-200 pb-4"
|
||||
>
|
||||
<Shield :size="16" class="text-rose-500" /> 安全与隐私设置
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800 rounded-xl space-y-4">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-xl space-y-4">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="shrink-0 pt-0.5">
|
||||
<input
|
||||
id="show-keywords"
|
||||
v-model="formData.showBlacklistKeywords"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded border-zinc-800 bg-zinc-900 accent-blue-600 cursor-pointer"
|
||||
class="w-4 h-4 rounded border-zinc-200 bg-white accent-blue-600 cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
<label for="show-keywords" class="cursor-pointer">
|
||||
<p class="text-xs font-bold text-zinc-200">显示黑名单具体关键词</p>
|
||||
<p class="text-xs font-bold text-zinc-700">显示黑名单具体关键词</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-1 leading-relaxed">
|
||||
开启后,在投稿命中黑名单时将明确提示冲突关键词;关闭则仅提示“包含关键词”。
|
||||
</p>
|
||||
@@ -303,18 +303,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800 rounded-xl space-y-4">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-xl space-y-4">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="shrink-0 pt-0.5">
|
||||
<input
|
||||
id="hide-students"
|
||||
v-model="formData.hideStudentInfo"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded border-zinc-800 bg-zinc-900 accent-blue-600 cursor-pointer"
|
||||
class="w-4 h-4 rounded border-zinc-200 bg-white accent-blue-600 cursor-pointer"
|
||||
>
|
||||
</div>
|
||||
<label for="hide-students" class="cursor-pointer">
|
||||
<p class="text-xs font-bold text-zinc-200">隐藏学生详细信息</p>
|
||||
<p class="text-xs font-bold text-zinc-700">隐藏学生详细信息</p>
|
||||
<p class="text-[10px] text-zinc-500 mt-1 leading-relaxed">
|
||||
开启后,非管理员用户在前端点歌列表、排期预览中将无法查看投稿学生的完整学号与真实姓名。
|
||||
</p>
|
||||
@@ -359,9 +359,9 @@ const saveSuccess = ref(false)
|
||||
|
||||
// 样式类常量
|
||||
const inputClass =
|
||||
'w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all placeholder:text-zinc-800'
|
||||
'w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all placeholder:text-zinc-500'
|
||||
const labelClass = 'text-[10px] font-black text-zinc-600 uppercase tracking-widest px-1 block mb-2'
|
||||
const cardClass = 'bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6 shadow-xl space-y-6'
|
||||
const cardClass = 'bg-zinc-50/40 border border-zinc-200 rounded-2xl p-6 shadow-xl space-y-6'
|
||||
|
||||
const defaultSubmissionGuidelines = `1. 投稿时无需加入书名号
|
||||
2. 除DJ外,其他类型歌曲均接收(包括小语种)
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<!-- 头部区域 -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">邮件服务配置</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">邮件服务配置</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1">
|
||||
配置 SMTP 服务以发送系统验证码、投稿通知及其他重要提醒
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="flex items-center gap-2 px-6 py-2 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-300 text-xs font-bold rounded-xl transition-all active:scale-95 disabled:opacity-50"
|
||||
class="flex items-center gap-2 px-6 py-2 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-600 text-xs font-bold rounded-xl transition-all active:scale-95 disabled:opacity-50"
|
||||
:disabled="reloading"
|
||||
@click="reloadSmtpConfig"
|
||||
>
|
||||
@@ -31,16 +31,16 @@
|
||||
<!-- 左侧栏:SMTP 设置与测试 -->
|
||||
<div class="xl:col-span-4 space-y-8">
|
||||
<!-- SMTP 核心设置 -->
|
||||
<section class="bg-zinc-900/30 border border-zinc-800 rounded-[2rem] p-6 space-y-6">
|
||||
<section class="bg-zinc-50/30 border border-zinc-200 rounded-[2rem] p-6 space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2"
|
||||
>
|
||||
<Server :size="16" class="text-blue-500" /> 服务配置
|
||||
</h3>
|
||||
<button
|
||||
class="relative w-10 h-5 rounded-full transition-colors"
|
||||
:class="config.smtpEnabled ? 'bg-blue-600' : 'bg-zinc-800'"
|
||||
:class="config.smtpEnabled ? 'bg-blue-600' : 'bg-zinc-100'"
|
||||
@click="config.smtpEnabled = !config.smtpEnabled"
|
||||
>
|
||||
<div
|
||||
@@ -59,7 +59,7 @@
|
||||
v-model="config.smtpHost"
|
||||
type="text"
|
||||
placeholder="smtp.example.com"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
@@ -71,7 +71,7 @@
|
||||
v-model.number="config.smtpPort"
|
||||
type="number"
|
||||
placeholder="587"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
@@ -94,7 +94,7 @@
|
||||
v-model="config.smtpUsername"
|
||||
type="text"
|
||||
placeholder="your-email@example.com"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
@@ -105,7 +105,7 @@
|
||||
v-model="config.smtpFromEmail"
|
||||
type="text"
|
||||
:placeholder="config.smtpUsername || 'your-email@example.com'"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
<p class="text-[9px] text-zinc-500 px-1 italic">
|
||||
通常与发件人账号一致,部分服务商强制要求一致。
|
||||
@@ -119,7 +119,7 @@
|
||||
v-model="config.smtpPassword"
|
||||
type="password"
|
||||
placeholder="••••••••••••"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
@@ -130,16 +130,16 @@
|
||||
v-model="config.smtpFromName"
|
||||
type="text"
|
||||
placeholder="校园广播站"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- SMTP 测试模块 -->
|
||||
<section class="bg-zinc-900/30 border border-zinc-800 rounded-[2rem] p-6 space-y-6">
|
||||
<section class="bg-zinc-50/30 border border-zinc-200 rounded-[2rem] p-6 space-y-6">
|
||||
<h3
|
||||
class="text-sm font-black text-zinc-100 uppercase tracking-widest flex items-center gap-2"
|
||||
class="text-sm font-black text-zinc-900 uppercase tracking-widest flex items-center gap-2"
|
||||
>
|
||||
<Send :size="16" class="text-emerald-500" /> 服务测试
|
||||
</h3>
|
||||
@@ -154,10 +154,10 @@
|
||||
v-model="testEmail"
|
||||
type="email"
|
||||
placeholder="输入测试邮箱地址"
|
||||
class="flex-1 bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/30"
|
||||
class="flex-1 bg-white border border-zinc-200 rounded-xl px-4 py-2.5 text-xs text-zinc-700 focus:outline-none focus:border-blue-200"
|
||||
>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-400 text-xs font-bold rounded-xl transition-all disabled:opacity-50"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-400 text-xs font-bold rounded-xl transition-all disabled:opacity-50"
|
||||
:disabled="testing || !testEmail || !config.smtpEnabled"
|
||||
@click="sendTestEmail"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6 px-1">
|
||||
<div class="space-y-1">
|
||||
<h2 class="text-3xl font-black text-zinc-100 tracking-tight">歌曲审核</h2>
|
||||
<h2 class="text-3xl font-black text-zinc-900 tracking-tight">歌曲审核</h2>
|
||||
<div class="flex items-center gap-6 mt-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest">待审核</span>
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
:disabled="loading"
|
||||
class="flex items-center gap-2 px-5 py-2.5 bg-blue-600/10 hover:bg-blue-600/20 text-blue-400 text-xs font-bold rounded-lg border border-blue-500/20 transition-all disabled:opacity-50"
|
||||
class="flex items-center gap-2 px-5 py-2.5 bg-blue-50 hover:bg-blue-600/20 text-blue-700 text-xs font-bold rounded-lg border border-blue-200 transition-all disabled:opacity-50"
|
||||
@click="refreshSongs(true)"
|
||||
>
|
||||
<RotateCcw :size="16" :class="{ 'animate-spin': loading }" /> 刷新
|
||||
@@ -25,15 +25,15 @@
|
||||
</div>
|
||||
|
||||
<!-- 歌曲表格 -->
|
||||
<div class="bg-zinc-900/10 border border-zinc-800/40 rounded-xl overflow-hidden shadow-2xl relative">
|
||||
<div v-if="loading" class="absolute inset-0 bg-zinc-950/40 backdrop-blur-sm z-10 flex items-center justify-center">
|
||||
<div class="bg-zinc-50/10 border border-zinc-200 rounded-xl overflow-hidden shadow-2xl relative">
|
||||
<div v-if="loading" class="absolute inset-0 bg-white/80 backdrop-blur-sm z-10 flex items-center justify-center">
|
||||
<div class="flex flex-col items-center gap-3">
|
||||
<div class="w-8 h-8 border-2 border-blue-500/20 border-t-blue-500 rounded-full animate-spin" />
|
||||
<div class="w-8 h-8 border-2 border-blue-200 border-t-blue-500 rounded-full animate-spin" />
|
||||
<span class="text-xs font-bold text-zinc-400 tracking-widest uppercase">正在加载...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden lg:grid grid-cols-12 gap-4 px-8 py-5 border-b border-zinc-800/60 bg-zinc-900/40">
|
||||
<div class="hidden lg:grid grid-cols-12 gap-4 px-8 py-5 border-b border-zinc-200/60 bg-zinc-50/40">
|
||||
<div class="col-span-4 text-[10px] font-black text-zinc-600 uppercase tracking-widest">歌曲信息</div>
|
||||
<div class="col-span-2 text-[10px] font-black text-zinc-600 uppercase tracking-widest">投稿人</div>
|
||||
<div class="col-span-3 text-[10px] font-black text-zinc-600 uppercase tracking-widest">期望排期</div>
|
||||
@@ -49,17 +49,17 @@
|
||||
<div
|
||||
v-for="song in pendingAuditSongs"
|
||||
:key="song.id"
|
||||
class="grid grid-cols-1 lg:grid-cols-12 gap-4 px-6 lg:px-8 py-5 transition-all items-center hover:bg-zinc-800/20 group relative"
|
||||
class="grid grid-cols-1 lg:grid-cols-12 gap-4 px-6 lg:px-8 py-5 transition-all items-center hover:bg-zinc-100/20 group relative"
|
||||
>
|
||||
<div class="col-span-12 lg:col-span-4 flex items-center gap-4">
|
||||
<div class="w-12 h-12 rounded-xl flex items-center justify-center shrink-0 border transition-all overflow-hidden bg-zinc-800/40 border-zinc-700/30 cursor-pointer hover:opacity-80" @click.stop="playSong(song)">
|
||||
<div class="w-12 h-12 rounded-xl flex items-center justify-center shrink-0 border transition-all overflow-hidden bg-zinc-100/40 border-zinc-200/30 cursor-pointer hover:opacity-80" @click.stop="playSong(song)">
|
||||
<img v-if="song.cover" :src="convertToHttps(song.cover)" class="w-full h-full object-cover" referrerpolicy="no-referrer">
|
||||
<Music v-else :size="20" class="text-zinc-600" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<h4 class="font-bold transition-colors flex items-center text-zinc-100 group-hover:text-blue-400">
|
||||
<h4 class="font-bold transition-colors flex items-center text-zinc-900 group-hover:text-blue-700">
|
||||
<span class="truncate">{{ song.title }}</span>
|
||||
<button v-if="song.hasSubmissionNote && song.submissionNote" class="ml-2 inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-500/30 bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 transition-all" title="查看备注留言" @click.stop="openSubmissionRemark(song)">
|
||||
<button v-if="song.hasSubmissionNote && song.submissionNote" class="ml-2 inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-200 bg-blue-500/10 text-blue-700 hover:bg-blue-500/20 transition-all" title="查看备注留言" @click.stop="openSubmissionRemark(song)">
|
||||
<MessageSquare :size="12" />
|
||||
</button>
|
||||
</h4>
|
||||
@@ -80,10 +80,10 @@
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-3 flex items-center justify-end gap-2">
|
||||
<button class="px-4 py-2 bg-emerald-600/10 hover:bg-emerald-600/20 text-emerald-500 hover:text-emerald-400 text-xs font-bold rounded-lg border border-emerald-500/20 transition-all" @click="auditSong(song.id, 'approved')">
|
||||
<button class="px-4 py-2 bg-emerald-600/10 hover:bg-emerald-600/20 text-emerald-500 hover:text-emerald-700 text-xs font-bold rounded-lg border border-emerald-500/20 transition-all" @click="auditSong(song.id, 'approved')">
|
||||
通过
|
||||
</button>
|
||||
<button class="px-4 py-2 bg-red-600/10 hover:bg-red-600/20 text-red-500 hover:text-red-400 text-xs font-bold rounded-lg border border-red-500/20 transition-all" @click="rejectSong(song.id)">
|
||||
<button class="px-4 py-2 bg-red-600/10 hover:bg-red-600/20 text-red-600 hover:text-red-400 text-xs font-bold rounded-lg border border-red-500/20 transition-all" @click="rejectSong(song.id)">
|
||||
未过审 (释放排期)
|
||||
</button>
|
||||
</div>
|
||||
@@ -93,20 +93,20 @@
|
||||
|
||||
<!-- 驳回歌曲对话框 -->
|
||||
<Transition enter-active-class="transition duration-300 ease-out" enter-from-class="opacity-0" enter-to-class="opacity-100" leave-active-class="transition duration-200 ease-in" leave-from-class="opacity-100" leave-to-class="opacity-0">
|
||||
<div v-if="showRejectDialog" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm">
|
||||
<div class="w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden" @click.stop>
|
||||
<div class="px-8 py-6 border-b border-zinc-800/50 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100">未过审处理</h3>
|
||||
<button class="text-zinc-500 hover:text-zinc-300 transition-colors" @click="showRejectDialog = false"><X :size="20" /></button>
|
||||
<div v-if="showRejectDialog" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-white/80 backdrop-blur-sm">
|
||||
<div class="w-full max-w-lg bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden" @click.stop>
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900">未过审处理</h3>
|
||||
<button class="text-zinc-500 hover:text-zinc-600 transition-colors" @click="showRejectDialog = false"><X :size="20" /></button>
|
||||
</div>
|
||||
<div class="p-8 space-y-6">
|
||||
<div class="p-5 bg-zinc-950 border border-zinc-800 rounded-lg">
|
||||
<div class="p-5 bg-white border border-zinc-200 rounded-lg">
|
||||
<p class="text-xs text-zinc-400 mb-2">此操作会将歌曲标记为未过审,并<span class="text-red-400 font-bold">立即释放其占用的排期序号</span>,供其他同学选择。歌曲不会被删除。</p>
|
||||
<textarea v-model="rejectReason" placeholder="请输入未过审原因 (可选),将通知投稿人..." class="w-full bg-zinc-900 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-red-500/30 min-h-[100px] resize-none transition-all" />
|
||||
<textarea v-model="rejectReason" placeholder="请输入未过审原因 (可选),将通知投稿人..." class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-red-200 min-h-[100px] resize-none transition-all" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-8 py-6 bg-zinc-900/50 border-t border-zinc-800/50 flex gap-3 justify-end">
|
||||
<button class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-300" @click="showRejectDialog = false">取消</button>
|
||||
<div class="px-8 py-6 bg-zinc-50/50 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-600" @click="showRejectDialog = false">取消</button>
|
||||
<button :disabled="auditLoading" class="px-8 py-2.5 bg-red-600 hover:bg-red-500 text-white text-xs font-black rounded-lg transition-all disabled:opacity-50" @click="confirmReject">
|
||||
{{ auditLoading ? '处理中...' : '确认释放排期' }}
|
||||
</button>
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
<Transition name="scale">
|
||||
<div
|
||||
v-if="show"
|
||||
class="bg-zinc-900 border border-zinc-800 rounded-2xl shadow-2xl w-full max-w-lg overflow-hidden flex flex-col max-h-[90vh]"
|
||||
class="bg-white border border-zinc-200 rounded-2xl shadow-2xl w-full max-w-lg overflow-hidden flex flex-col max-h-[90vh]"
|
||||
@click.stop
|
||||
>
|
||||
<!-- 头部 -->
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-800 shrink-0">
|
||||
<h3 class="text-sm font-black text-zinc-100 uppercase tracking-widest">下载歌曲</h3>
|
||||
<div class="flex items-center justify-between p-4 border-b border-zinc-200 shrink-0">
|
||||
<h3 class="text-sm font-black text-zinc-900 uppercase tracking-widest">下载歌曲</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
@click="closeDialog"
|
||||
>
|
||||
<CloseIcon class="w-5 h-5" />
|
||||
@@ -36,15 +36,15 @@
|
||||
class="flex flex-col p-4 rounded-2xl border text-left transition-all relative overflow-hidden group"
|
||||
:class="[
|
||||
selectedQuality === option.value
|
||||
? 'bg-blue-600/10 border-blue-500 shadow-sm'
|
||||
: 'bg-zinc-950 border-zinc-800 hover:border-zinc-700'
|
||||
? 'bg-blue-50 border-blue-500 shadow-sm'
|
||||
: 'bg-white border-zinc-200 hover:border-zinc-200'
|
||||
]"
|
||||
@click="selectedQuality = option.value"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-1 relative z-10">
|
||||
<span
|
||||
class="text-xs font-bold transition-colors"
|
||||
:class="selectedQuality === option.value ? 'text-blue-400' : 'text-zinc-200'"
|
||||
:class="selectedQuality === option.value ? 'text-blue-700' : 'text-zinc-700'"
|
||||
>{{ option.label }}</span
|
||||
>
|
||||
<div
|
||||
@@ -69,16 +69,16 @@
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="bg-zinc-950 border border-zinc-800 rounded-2xl p-4 space-y-4">
|
||||
<div class="bg-white border border-zinc-200 rounded-2xl p-4 space-y-4">
|
||||
<!-- 合并开关 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs font-bold text-zinc-200">合并为一个文件</span>
|
||||
<span class="text-xs font-bold text-zinc-700">合并为一个文件</span>
|
||||
<span class="text-[10px] text-zinc-500">将选中歌曲按顺序合并为单个音频</span>
|
||||
</div>
|
||||
<button
|
||||
class="w-10 h-6 rounded-full transition-colors relative"
|
||||
:class="mergeSongs ? 'bg-blue-600' : 'bg-zinc-700'"
|
||||
:class="mergeSongs ? 'bg-blue-600' : 'bg-zinc-200'"
|
||||
@click="mergeSongs = !mergeSongs"
|
||||
>
|
||||
<div
|
||||
@@ -90,20 +90,20 @@
|
||||
|
||||
<!-- 合并设置区域 -->
|
||||
<Transition name="expand">
|
||||
<div v-if="mergeSongs" class="space-y-4 pt-4 border-t border-zinc-800/50">
|
||||
<div v-if="mergeSongs" class="space-y-4 pt-4 border-t border-zinc-200">
|
||||
<!-- 导出格式选择 -->
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Music class="w-3 h-3 text-zinc-500" />
|
||||
<span class="text-xs font-bold text-zinc-200">导出格式</span>
|
||||
<span class="text-xs font-bold text-zinc-700">导出格式</span>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="flex-1 py-1.5 px-3 rounded-lg border text-[10px] font-bold transition-all"
|
||||
:class="
|
||||
exportFormat === 'mp3'
|
||||
? 'bg-blue-600/10 border-blue-500 text-blue-400'
|
||||
: 'bg-zinc-900 border-zinc-800 text-zinc-400 hover:border-zinc-700'
|
||||
? 'bg-blue-50 border-blue-500 text-blue-700'
|
||||
: 'bg-white border-zinc-200 text-zinc-400 hover:border-zinc-200'
|
||||
"
|
||||
@click="exportFormat = 'mp3'"
|
||||
>
|
||||
@@ -113,8 +113,8 @@
|
||||
class="flex-1 py-1.5 px-3 rounded-lg border text-[10px] font-bold transition-all"
|
||||
:class="
|
||||
exportFormat === 'wav'
|
||||
? 'bg-blue-600/10 border-blue-500 text-blue-400'
|
||||
: 'bg-zinc-900 border-zinc-800 text-zinc-400 hover:border-zinc-700'
|
||||
? 'bg-blue-50 border-blue-500 text-blue-700'
|
||||
: 'bg-white border-zinc-200 text-zinc-400 hover:border-zinc-200'
|
||||
"
|
||||
@click="exportFormat = 'wav'"
|
||||
>
|
||||
@@ -128,10 +128,10 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<Edit3 class="w-3 h-3 text-zinc-500" />
|
||||
<span class="text-xs font-bold text-zinc-200">自定义文件名</span>
|
||||
<span class="text-xs font-bold text-zinc-700">自定义文件名</span>
|
||||
</div>
|
||||
<button
|
||||
class="text-[10px] text-blue-400 hover:text-blue-300 transition-colors flex items-center gap-1"
|
||||
class="text-[10px] text-blue-700 hover:text-blue-300 transition-colors flex items-center gap-1"
|
||||
:class="{ 'opacity-50 cursor-not-allowed': !customFilename }"
|
||||
title="保存为默认预设"
|
||||
@click="saveFilenamePreset"
|
||||
@@ -145,19 +145,19 @@
|
||||
v-model="customFilename"
|
||||
type="text"
|
||||
placeholder="例如: 第XX期 - {songs}"
|
||||
class="w-full bg-zinc-900 border border-zinc-800 rounded-xl px-3 py-2 text-xs text-zinc-200 focus:outline-none focus:border-blue-500/50 transition-colors pr-8"
|
||||
class="w-full bg-white border border-zinc-200 rounded-xl px-3 py-2 text-xs text-zinc-700 focus:outline-none focus:border-blue-500/50 transition-colors pr-8"
|
||||
>
|
||||
<!-- 快速插入占位符按钮 -->
|
||||
<div class="absolute right-2 top-1/2 -translate-y-1/2 flex gap-1">
|
||||
<button
|
||||
class="px-1.5 py-0.5 rounded bg-zinc-800 text-[9px] text-zinc-400 hover:text-zinc-200 hover:bg-zinc-700 transition-colors"
|
||||
class="px-1.5 py-0.5 rounded bg-zinc-100 text-[9px] text-zinc-400 hover:text-zinc-700 hover:bg-zinc-200 transition-colors"
|
||||
title="插入所有歌名"
|
||||
@click="insertPlaceholder('{songs}')"
|
||||
>
|
||||
{songs}
|
||||
</button>
|
||||
<button
|
||||
class="px-1.5 py-0.5 rounded bg-zinc-800 text-[9px] text-zinc-400 hover:text-zinc-200 hover:bg-zinc-700 transition-colors"
|
||||
class="px-1.5 py-0.5 rounded bg-zinc-100 text-[9px] text-zinc-400 hover:text-zinc-700 hover:bg-zinc-200 transition-colors"
|
||||
title="插入当前日期"
|
||||
@click="insertPlaceholder('{date}')"
|
||||
>
|
||||
@@ -168,13 +168,13 @@
|
||||
<p class="text-[9px] text-zinc-600">
|
||||
可用占位符:
|
||||
<code
|
||||
class="bg-zinc-800 px-1 rounded text-zinc-400 cursor-pointer hover:text-blue-400"
|
||||
class="bg-zinc-100 px-1 rounded text-zinc-400 cursor-pointer hover:text-blue-700"
|
||||
@click="insertPlaceholder('{songs}')"
|
||||
>{songs}</code
|
||||
>
|
||||
(所有歌名),
|
||||
<code
|
||||
class="bg-zinc-800 px-1 rounded text-zinc-400 cursor-pointer hover:text-blue-400"
|
||||
class="bg-zinc-100 px-1 rounded text-zinc-400 cursor-pointer hover:text-blue-700"
|
||||
@click="insertPlaceholder('{date}')"
|
||||
>{date}</code
|
||||
>
|
||||
@@ -184,14 +184,14 @@
|
||||
|
||||
<!-- 标准化选项 (仅在合并模式或需要时显示,这里允许单独使用) -->
|
||||
<div
|
||||
class="flex items-center justify-between pt-3 border-t border-zinc-800/50"
|
||||
class="flex items-center justify-between pt-3 border-t border-zinc-200"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs font-bold text-zinc-200">统一音量 (标准化)</span>
|
||||
<span class="text-xs font-bold text-zinc-700">统一音量 (标准化)</span>
|
||||
<span
|
||||
v-if="normalizeAudio"
|
||||
class="text-[10px] bg-blue-500/10 text-blue-400 px-1.5 py-0.5 rounded border border-blue-500/20"
|
||||
class="text-[10px] bg-blue-500/10 text-blue-700 px-1.5 py-0.5 rounded border border-blue-200"
|
||||
>Peak {{ targetDb }}dB</span
|
||||
>
|
||||
</div>
|
||||
@@ -201,7 +201,7 @@
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="text-[10px] text-blue-400 hover:text-blue-300 transition-colors flex items-center gap-1"
|
||||
class="text-[10px] text-blue-700 hover:text-blue-300 transition-colors flex items-center gap-1"
|
||||
title="保存当前音量设置(包括开启状态)为默认"
|
||||
@click="saveDbPreset"
|
||||
>
|
||||
@@ -210,7 +210,7 @@
|
||||
</button>
|
||||
<button
|
||||
class="w-10 h-6 rounded-full transition-colors relative"
|
||||
:class="normalizeAudio ? 'bg-blue-600' : 'bg-zinc-700'"
|
||||
:class="normalizeAudio ? 'bg-blue-600' : 'bg-zinc-200'"
|
||||
@click="normalizeAudio = !normalizeAudio"
|
||||
>
|
||||
<div
|
||||
@@ -232,9 +232,9 @@
|
||||
min="-10"
|
||||
max="0"
|
||||
step="0.5"
|
||||
class="flex-1 h-1.5 bg-zinc-800 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-blue-500"
|
||||
class="flex-1 h-1.5 bg-zinc-100 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-blue-500"
|
||||
>
|
||||
<span class="text-xs font-mono text-zinc-300 w-12 text-right"
|
||||
<span class="text-xs font-mono text-zinc-600 w-12 text-right"
|
||||
>{{ targetDb }} dB</span
|
||||
>
|
||||
</div>
|
||||
@@ -255,7 +255,7 @@
|
||||
>
|
||||
<div
|
||||
v-if="estimatedTotalDuration.count > 0"
|
||||
class="flex items-center gap-1.5 text-[10px] text-blue-400 font-bold bg-blue-500/10 px-2 py-0.5 rounded-full border border-blue-500/20"
|
||||
class="flex items-center gap-1.5 text-[10px] text-blue-700 font-bold bg-blue-500/10 px-2 py-0.5 rounded-full border border-blue-200"
|
||||
>
|
||||
<Clock class="w-3 h-3" />
|
||||
<span>预估总时长: {{ formatDuration(estimatedTotalDuration.total) }}</span>
|
||||
@@ -264,7 +264,7 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
v-if="selectedSongs.size > 0"
|
||||
class="text-[10px] font-bold text-zinc-500 hover:text-zinc-300 transition-colors flex items-center gap-1"
|
||||
class="text-[10px] font-bold text-zinc-500 hover:text-zinc-600 transition-colors flex items-center gap-1"
|
||||
title="预下载选中歌曲到浏览器缓存"
|
||||
@click="preloadSelectedSongs"
|
||||
>
|
||||
@@ -272,7 +272,7 @@
|
||||
预下载选中
|
||||
</button>
|
||||
<button
|
||||
class="text-[10px] font-bold text-blue-500/80 hover:text-blue-400 transition-colors"
|
||||
class="text-[10px] font-bold text-blue-500/80 hover:text-blue-700 transition-colors"
|
||||
@click="toggleSelectAll"
|
||||
>
|
||||
{{ isAllSelected ? '取消全选' : '全选' }}
|
||||
@@ -281,12 +281,12 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="bg-zinc-950/50 border border-zinc-800/50 rounded-2xl overflow-hidden max-h-[300px] overflow-y-auto custom-scrollbar"
|
||||
class="bg-white/70 border border-zinc-200 rounded-2xl overflow-hidden max-h-[300px] overflow-y-auto custom-scrollbar"
|
||||
>
|
||||
<div
|
||||
v-for="song in songs"
|
||||
:key="song.id"
|
||||
class="w-full flex items-center gap-3 p-3.5 hover:bg-zinc-800/30 transition-all text-left border-b border-zinc-800/30 last:border-0 group relative"
|
||||
class="w-full flex items-center gap-3 p-3.5 hover:bg-zinc-100/30 transition-all text-left border-b border-zinc-200/30 last:border-0 group relative"
|
||||
>
|
||||
<!-- 预下载进度条背景 -->
|
||||
<div
|
||||
@@ -310,7 +310,7 @@
|
||||
:class="[
|
||||
selectedSongs.has(song.song.id)
|
||||
? 'bg-blue-600 border-blue-600 shadow-sm'
|
||||
: 'bg-zinc-900 border-zinc-800 group-hover:border-zinc-700'
|
||||
: 'bg-white border-zinc-200 group-hover:border-zinc-200'
|
||||
]"
|
||||
@click="toggleSongSelection(song.song.id)"
|
||||
>
|
||||
@@ -326,7 +326,7 @@
|
||||
@click="toggleSongSelection(song.song.id)"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<p class="text-xs font-bold text-zinc-300 truncate">{{ song.song.title }}</p>
|
||||
<p class="text-xs font-bold text-zinc-600 truncate">{{ song.song.title }}</p>
|
||||
<!-- 预下载标记 -->
|
||||
<div
|
||||
v-if="
|
||||
@@ -360,7 +360,7 @@
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="p-1.5 rounded-lg hover:bg-blue-500/10 text-zinc-600 hover:text-blue-400 transition-colors"
|
||||
class="p-1.5 rounded-lg hover:bg-blue-500/10 text-zinc-600 hover:text-blue-700 transition-colors"
|
||||
title="预下载此歌曲"
|
||||
@click.stop="preloadSong(song.song)"
|
||||
>
|
||||
@@ -378,16 +378,16 @@
|
||||
<!-- 进度条 -->
|
||||
<section
|
||||
v-if="downloading || downloadedCount > 0"
|
||||
class="space-y-3 pt-4 border-t border-zinc-800/50"
|
||||
class="space-y-3 pt-4 border-t border-zinc-200"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between text-[10px] font-bold uppercase tracking-wider"
|
||||
>
|
||||
<span class="text-zinc-400">{{ mergeSongs ? '处理进度' : '下载进度' }}</span>
|
||||
<span class="text-blue-400">{{ downloadedCount }} / {{ totalCount }}</span>
|
||||
<span class="text-blue-700">{{ downloadedCount }} / {{ totalCount }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="h-2 bg-zinc-950 rounded-full overflow-hidden border border-zinc-800/50 relative"
|
||||
class="h-2 bg-white rounded-full overflow-hidden border border-zinc-200 relative"
|
||||
>
|
||||
<div
|
||||
class="h-full bg-gradient-to-r from-blue-600 to-indigo-500 transition-all duration-300 ease-out relative overflow-hidden"
|
||||
@@ -402,7 +402,7 @@
|
||||
</div>
|
||||
<div class="text-[10px] text-zinc-500 truncate">
|
||||
<template v-if="downloading">
|
||||
<span v-if="processingStatus" class="text-blue-400 animate-pulse">{{
|
||||
<span v-if="processingStatus" class="text-blue-700 animate-pulse">{{
|
||||
processingStatus
|
||||
}}</span>
|
||||
<span v-else>{{
|
||||
@@ -438,14 +438,14 @@
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<div
|
||||
class="p-4 border-t border-zinc-800 bg-zinc-900/50 flex items-center justify-between shrink-0"
|
||||
class="p-4 border-t border-zinc-200 bg-zinc-50/50 flex items-center justify-between shrink-0"
|
||||
>
|
||||
<div class="text-[11px] font-black text-zinc-500 uppercase tracking-widest">
|
||||
已选择 <span class="text-blue-400">{{ selectedSongs.size }}</span> 首歌曲
|
||||
已选择 <span class="text-blue-700">{{ selectedSongs.size }}</span> 首歌曲
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-300 transition-colors uppercase tracking-wider"
|
||||
class="px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-600 transition-colors uppercase tracking-wider"
|
||||
:disabled="downloading"
|
||||
@click="closeDialog"
|
||||
>
|
||||
@@ -453,7 +453,7 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="!downloading && downloadedCount > 0"
|
||||
class="px-6 py-2.5 bg-zinc-800 hover:bg-zinc-700 text-zinc-200 text-xs font-bold rounded-xl border border-zinc-700 transition-all uppercase tracking-wider"
|
||||
class="px-6 py-2.5 bg-zinc-100 hover:bg-zinc-200 text-zinc-700 text-xs font-bold rounded-xl border border-zinc-200 transition-all uppercase tracking-wider"
|
||||
@click="closeDialog"
|
||||
>
|
||||
关闭
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6 px-1">
|
||||
<div class="space-y-1">
|
||||
<h2 class="text-3xl font-black text-zinc-100 tracking-tight">歌曲管理</h2>
|
||||
<h2 class="text-3xl font-black text-zinc-900 tracking-tight">歌曲管理</h2>
|
||||
<div class="flex items-center gap-6 mt-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
>总计</span
|
||||
>
|
||||
<span class="text-sm font-bold text-zinc-300">{{ filteredSongs.length }}</span>
|
||||
<span class="text-sm font-bold text-zinc-600">{{ filteredSongs.length }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-[10px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
@@ -39,10 +39,10 @@
|
||||
>
|
||||
<div
|
||||
v-if="selectedSongs.length > 0"
|
||||
class="flex items-center gap-2 p-1.5 bg-zinc-900/90 border border-zinc-800 rounded-lg backdrop-blur-xl shadow-2xl"
|
||||
class="flex items-center gap-2 p-1.5 bg-zinc-50/90 border border-zinc-200 rounded-lg backdrop-blur-xl shadow-2xl"
|
||||
>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-emerald-400 text-[11px] font-black rounded-lg border border-zinc-700/50 transition-all active:scale-95 uppercase tracking-widest"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-100 hover:bg-zinc-200 text-emerald-700 text-[11px] font-black rounded-lg border border-zinc-200 transition-all active:scale-95 uppercase tracking-widest"
|
||||
@click="openDownloadDialog"
|
||||
>
|
||||
<Download :size="14" class="text-emerald-500/70" /> 下载 ({{
|
||||
@@ -50,25 +50,25 @@
|
||||
}})
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-emerald-400 text-[11px] font-black rounded-lg border border-zinc-700/50 transition-all active:scale-95 uppercase tracking-widest"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-100 hover:bg-zinc-200 text-emerald-700 text-[11px] font-black rounded-lg border border-zinc-200 transition-all active:scale-95 uppercase tracking-widest"
|
||||
@click="batchMarkPlayed"
|
||||
>
|
||||
<Check :size="14" class="text-emerald-500/70" /> 已播放 ({{ selectedSongs.length }})
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-blue-400 text-[11px] font-black rounded-lg border border-zinc-700/50 transition-all active:scale-95 uppercase tracking-widest"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-100 hover:bg-zinc-200 text-blue-700 text-[11px] font-black rounded-lg border border-zinc-200 transition-all active:scale-95 uppercase tracking-widest"
|
||||
@click="batchMarkUnplayed"
|
||||
>
|
||||
<RotateCcw :size="14" class="text-blue-500/70" /> 未播放 ({{ selectedSongs.length }})
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-amber-400 text-[11px] font-black rounded-lg border border-zinc-700/50 transition-all active:scale-95 uppercase tracking-widest"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-100 hover:bg-zinc-200 text-amber-700 text-[11px] font-black rounded-lg border border-zinc-200 transition-all active:scale-95 uppercase tracking-widest"
|
||||
@click="batchReject"
|
||||
>
|
||||
<X :size="14" class="text-amber-500/70" /> 驳回 ({{ selectedSongs.length }})
|
||||
</button>
|
||||
<button
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-red-400 text-[11px] font-black rounded-lg border border-zinc-700/50 transition-all active:scale-95 uppercase tracking-widest"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-zinc-100 hover:bg-zinc-200 text-red-400 text-[11px] font-black rounded-lg border border-zinc-200 transition-all active:scale-95 uppercase tracking-widest"
|
||||
@click="batchDelete"
|
||||
>
|
||||
<Trash2 :size="14" class="text-red-500/70" /> 删除 ({{ selectedSongs.length }})
|
||||
@@ -77,14 +77,14 @@
|
||||
</Transition>
|
||||
|
||||
<button
|
||||
class="flex items-center gap-2 px-5 py-2.5 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-300 text-xs font-bold rounded-lg transition-all active:scale-95"
|
||||
class="flex items-center gap-2 px-5 py-2.5 bg-white border border-zinc-200 hover:border-zinc-200 text-zinc-600 text-xs font-bold rounded-lg transition-all active:scale-95"
|
||||
@click="openAddSongModal"
|
||||
>
|
||||
<Plus :size="16" /> 手动添加
|
||||
</button>
|
||||
<button
|
||||
:disabled="loading"
|
||||
class="flex items-center gap-2 px-5 py-2.5 bg-blue-600/10 hover:bg-blue-600/20 text-blue-400 text-xs font-bold rounded-lg border border-blue-500/20 transition-all disabled:opacity-50"
|
||||
class="flex items-center gap-2 px-5 py-2.5 bg-blue-50 hover:bg-blue-600/20 text-blue-700 text-xs font-bold rounded-lg border border-blue-200 transition-all disabled:opacity-50"
|
||||
@click="refreshSongs(true)"
|
||||
>
|
||||
<RotateCcw :size="16" :class="{ 'animate-spin': loading }" /> 刷新
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
<!-- 过滤器栏 -->
|
||||
<div
|
||||
class="bg-zinc-900/30 border border-zinc-800/60 rounded-xl p-4 flex flex-col lg:flex-row gap-4 items-stretch lg:items-center"
|
||||
class="bg-zinc-50/30 border border-zinc-200/60 rounded-xl p-4 flex flex-col lg:flex-row gap-4 items-stretch lg:items-center"
|
||||
>
|
||||
<div class="relative flex-1 group w-full lg:w-auto">
|
||||
<Search
|
||||
@@ -105,7 +105,7 @@
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="搜索歌曲标题、艺术家或投稿人..."
|
||||
class="w-full bg-zinc-950/50 border border-zinc-800/80 rounded-lg pl-12 pr-4 py-3 text-sm focus:outline-none focus:border-blue-500/30 transition-all placeholder:text-zinc-800 text-zinc-200"
|
||||
class="w-full bg-white/70 border border-zinc-200/80 rounded-lg pl-12 pr-4 py-3 text-sm focus:outline-none focus:border-blue-200 transition-all placeholder:text-zinc-500 text-zinc-700"
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 lg:flex lg:items-center gap-3 w-full lg:w-auto">
|
||||
@@ -138,16 +138,16 @@
|
||||
|
||||
<!-- 歌曲表格 -->
|
||||
<div
|
||||
class="bg-zinc-900/10 border border-zinc-800/40 rounded-xl overflow-hidden shadow-2xl relative"
|
||||
class="bg-zinc-50/10 border border-zinc-200 rounded-xl overflow-hidden shadow-2xl relative"
|
||||
>
|
||||
<!-- 加载遮罩 -->
|
||||
<div
|
||||
v-if="loading"
|
||||
class="absolute inset-0 bg-zinc-950/40 backdrop-blur-sm z-10 flex items-center justify-center"
|
||||
class="absolute inset-0 bg-white/80 backdrop-blur-sm z-10 flex items-center justify-center"
|
||||
>
|
||||
<div class="flex flex-col items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 border-2 border-blue-500/20 border-t-blue-500 rounded-full animate-spin"
|
||||
class="w-8 h-8 border-2 border-blue-200 border-t-blue-500 rounded-full animate-spin"
|
||||
/>
|
||||
<span class="text-xs font-bold text-zinc-400 tracking-widest uppercase">正在加载...</span>
|
||||
</div>
|
||||
@@ -155,13 +155,13 @@
|
||||
|
||||
<!-- 表头 -->
|
||||
<div
|
||||
class="hidden lg:grid grid-cols-12 gap-4 px-8 py-5 border-b border-zinc-800/60 bg-zinc-900/40"
|
||||
class="hidden lg:grid grid-cols-12 gap-4 px-8 py-5 border-b border-zinc-200/60 bg-zinc-50/40"
|
||||
>
|
||||
<div class="col-span-1 flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isAllSelected"
|
||||
class="w-5 h-5 rounded-lg border-2 border-zinc-800 bg-zinc-950 accent-blue-600 transition-all cursor-pointer"
|
||||
class="w-5 h-5 rounded-lg border-2 border-zinc-200 bg-white accent-blue-600 transition-all cursor-pointer"
|
||||
@change="toggleSelectAll"
|
||||
>
|
||||
</div>
|
||||
@@ -204,14 +204,14 @@
|
||||
:key="song.id"
|
||||
:class="[
|
||||
'grid grid-cols-1 lg:grid-cols-12 gap-4 px-6 lg:px-8 py-5 transition-all items-center group relative',
|
||||
selectedSongs.includes(song.id) ? 'bg-blue-600/5' : 'hover:bg-zinc-800/20'
|
||||
selectedSongs.includes(song.id) ? 'bg-blue-600/5' : 'hover:bg-zinc-100/20'
|
||||
]"
|
||||
>
|
||||
<div class="hidden lg:flex col-span-1 items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="selectedSongs.includes(song.id)"
|
||||
class="w-5 h-5 rounded-lg border-2 border-zinc-800 bg-zinc-950 accent-blue-600 transition-all cursor-pointer"
|
||||
class="w-5 h-5 rounded-lg border-2 border-zinc-200 bg-white accent-blue-600 transition-all cursor-pointer"
|
||||
@change="toggleSelectSong(song.id)"
|
||||
>
|
||||
</div>
|
||||
@@ -219,7 +219,7 @@
|
||||
<div class="col-span-12 lg:col-span-4 flex items-center gap-4">
|
||||
<div
|
||||
:class="[
|
||||
'w-12 h-12 rounded-xl flex items-center justify-center shrink-0 border transition-all overflow-hidden bg-zinc-800/40 border-zinc-700/30 group-hover:border-zinc-600 cursor-pointer hover:opacity-80',
|
||||
'w-12 h-12 rounded-xl flex items-center justify-center shrink-0 border transition-all overflow-hidden bg-zinc-100/40 border-zinc-200/30 group-hover:border-zinc-300 cursor-pointer hover:opacity-80',
|
||||
selectedSongs.includes(song.id) ? 'border-blue-500/50' : ''
|
||||
]"
|
||||
@click.stop="playSong(song)"
|
||||
@@ -237,8 +237,8 @@
|
||||
:class="[
|
||||
'font-bold transition-colors flex items-center',
|
||||
selectedSongs.includes(song.id)
|
||||
? 'text-blue-400'
|
||||
: 'text-zinc-100 group-hover:text-blue-400'
|
||||
? 'text-blue-700'
|
||||
: 'text-zinc-900 group-hover:text-blue-700'
|
||||
]"
|
||||
>
|
||||
<span
|
||||
@@ -250,7 +250,7 @@
|
||||
<span v-else class="truncate">{{ song.title }}</span>
|
||||
<button
|
||||
v-if="song.hasSubmissionNote && song.submissionNote"
|
||||
class="ml-2 inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-500/30 bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 transition-all"
|
||||
class="ml-2 inline-flex items-center justify-center w-5 h-5 rounded-full border border-blue-200 bg-blue-500/10 text-blue-700 hover:bg-blue-500/20 transition-all"
|
||||
title="查看备注留言"
|
||||
@click.stop="openSubmissionRemark(song)"
|
||||
>
|
||||
@@ -261,7 +261,7 @@
|
||||
|
||||
<!-- 期望播出日期标签 -->
|
||||
<div v-if="song.expectedPlayDate" class="mt-1 flex items-center">
|
||||
<span class="inline-flex items-center gap-1 px-1.5 py-0.5 bg-blue-500/10 text-blue-400 rounded border border-blue-500/20 text-[10px] font-medium">
|
||||
<span class="inline-flex items-center gap-1 px-1.5 py-0.5 bg-blue-500/10 text-blue-700 rounded border border-blue-200 text-[10px] font-medium">
|
||||
<CalendarDays :size="10" />
|
||||
期望: {{ song.expectedPlayDate }}
|
||||
<template v-if="song.expectedPlaySequence">
|
||||
@@ -295,7 +295,7 @@
|
||||
'inline-flex items-center gap-1.5 text-xs font-bold px-2 py-1 rounded-lg transition-all',
|
||||
song.voteCount > 0
|
||||
? 'bg-pink-500/10 text-pink-500 hover:bg-pink-500/20 cursor-pointer'
|
||||
: 'bg-zinc-950/30 text-zinc-700 cursor-default'
|
||||
: 'bg-white/60 text-zinc-700 cursor-default'
|
||||
]"
|
||||
@click="showVoters(song.id)"
|
||||
>
|
||||
@@ -312,7 +312,7 @@
|
||||
? 'bg-emerald-500/10 text-emerald-500'
|
||||
: song.scheduled
|
||||
? 'bg-blue-500/10 text-blue-500'
|
||||
: 'bg-zinc-800 text-zinc-500'
|
||||
: 'bg-zinc-100 text-zinc-500'
|
||||
]"
|
||||
>
|
||||
{{ getStatusText(song) }}
|
||||
@@ -323,7 +323,7 @@
|
||||
class="col-span-12 lg:col-span-2 flex items-center justify-end gap-2 opacity-100 lg:opacity-0 group-hover:opacity-100 transition-all"
|
||||
>
|
||||
<button
|
||||
class="p-2 bg-zinc-800/50 text-blue-400 hover:bg-blue-600 hover:text-white rounded-xl transition-all border border-zinc-700/30"
|
||||
class="p-2 bg-zinc-100/50 text-blue-700 hover:bg-blue-600 hover:text-zinc-900 rounded-xl transition-all border border-zinc-200/30"
|
||||
title="编辑歌曲"
|
||||
@click="editSong(song)"
|
||||
>
|
||||
@@ -332,7 +332,7 @@
|
||||
|
||||
<button
|
||||
v-if="!song.played"
|
||||
class="p-2 bg-zinc-800/50 text-emerald-400 hover:bg-emerald-600 hover:text-white rounded-xl transition-all border border-zinc-700/30"
|
||||
class="p-2 bg-zinc-100/50 text-emerald-700 hover:bg-emerald-600 hover:text-zinc-900 rounded-xl transition-all border border-zinc-200/30"
|
||||
title="标记为已播放"
|
||||
@click="markAsPlayed(song.id)"
|
||||
>
|
||||
@@ -340,7 +340,7 @@
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="p-2 bg-zinc-800/50 text-zinc-400 hover:bg-zinc-600 hover:text-white rounded-xl transition-all border border-zinc-700/30"
|
||||
class="p-2 bg-zinc-100/50 text-zinc-400 hover:bg-zinc-600 hover:text-zinc-900 rounded-xl transition-all border border-zinc-200/30"
|
||||
title="标记为未播放"
|
||||
@click="markAsUnplayed(song.id)"
|
||||
>
|
||||
@@ -348,14 +348,14 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="p-2 bg-zinc-800/50 text-amber-500 hover:bg-amber-600 hover:text-white rounded-xl transition-all border border-zinc-700/30"
|
||||
class="p-2 bg-zinc-100/50 text-amber-500 hover:bg-amber-600 hover:text-zinc-900 rounded-xl transition-all border border-zinc-200/30"
|
||||
title="驳回歌曲"
|
||||
@click="rejectSong(song.id)"
|
||||
>
|
||||
<X :size="14" />
|
||||
</button>
|
||||
<button
|
||||
class="p-2 bg-zinc-800/50 text-red-400 hover:bg-red-600 hover:text-white rounded-xl transition-all border border-zinc-700/30"
|
||||
class="p-2 bg-zinc-100/50 text-red-400 hover:bg-red-600 hover:text-zinc-900 rounded-xl transition-all border border-zinc-200/30"
|
||||
title="删除歌曲"
|
||||
@click="deleteSong(song.id)"
|
||||
>
|
||||
@@ -407,16 +407,16 @@
|
||||
>
|
||||
<div
|
||||
v-if="showRejectDialog"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-white/80 backdrop-blur-sm"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden"
|
||||
class="w-full max-w-lg bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden"
|
||||
@click.stop
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800/50 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100">{{ isBatchReject ? `批量驳回歌曲 (${selectedSongs.length} 首)` : '驳回歌曲' }}</h3>
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900">{{ isBatchReject ? `批量驳回歌曲 (${selectedSongs.length} 首)` : '驳回歌曲' }}</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
@click="cancelReject"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -424,25 +424,25 @@
|
||||
</div>
|
||||
|
||||
<div class="p-8 space-y-6">
|
||||
<div v-if="!isBatchReject" class="p-5 bg-zinc-950 border border-zinc-800 rounded-lg flex items-center gap-4">
|
||||
<div v-if="!isBatchReject" class="p-5 bg-white border border-zinc-200 rounded-lg flex items-center gap-4">
|
||||
<div
|
||||
class="w-10 h-10 rounded-lg bg-red-500/10 text-red-500 flex items-center justify-center border border-red-500/10"
|
||||
>
|
||||
<Music :size="18" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-bold text-zinc-100 text-sm">{{ rejectSongInfo.title }}</h4>
|
||||
<h4 class="font-bold text-zinc-900 text-sm">{{ rejectSongInfo.title }}</h4>
|
||||
<p class="text-xs text-zinc-500">投稿人: {{ rejectSongInfo.requester }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="p-5 bg-zinc-950 border border-zinc-800 rounded-lg flex items-center gap-4">
|
||||
<div v-else class="p-5 bg-white border border-zinc-200 rounded-lg flex items-center gap-4">
|
||||
<div
|
||||
class="w-10 h-10 rounded-lg bg-amber-500/10 text-amber-500 flex items-center justify-center border border-amber-500/10"
|
||||
>
|
||||
<ListMusic :size="18" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-bold text-zinc-100 text-sm">即将批量驳回 {{ selectedSongs.length }} 首歌曲</h4>
|
||||
<h4 class="font-bold text-zinc-900 text-sm">即将批量驳回 {{ selectedSongs.length }} 首歌曲</h4>
|
||||
<p class="text-xs text-zinc-500">驳回后将一并清理投票、排期记录并发送通知给对应投稿人</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -454,7 +454,7 @@
|
||||
<textarea
|
||||
v-model="rejectReason"
|
||||
placeholder="请输入驳回原因,将通过系统通知发送给投稿人..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-4 text-sm text-zinc-200 focus:outline-none focus:border-red-500/30 min-h-[120px] resize-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-4 text-sm text-zinc-700 focus:outline-none focus:border-red-200 min-h-[120px] resize-none transition-all"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -462,11 +462,11 @@
|
||||
<input
|
||||
v-model="addToBlacklist"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded border-zinc-800 bg-zinc-950 accent-red-600"
|
||||
class="w-4 h-4 rounded border-zinc-200 bg-white accent-red-600"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="text-xs font-bold text-zinc-300 group-hover:text-red-400 transition-colors"
|
||||
class="text-xs font-bold text-zinc-600 group-hover:text-red-400 transition-colors"
|
||||
>同时将此歌曲加入黑名单</span
|
||||
>
|
||||
<p class="text-[10px] text-zinc-600 font-medium">
|
||||
@@ -476,9 +476,9 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="px-8 py-6 bg-zinc-900/50 border-t border-zinc-800/50 flex gap-3 justify-end">
|
||||
<div class="px-8 py-6 bg-zinc-50/50 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-300"
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-600"
|
||||
@click="cancelReject"
|
||||
>
|
||||
取消
|
||||
@@ -516,18 +516,18 @@
|
||||
>
|
||||
<div
|
||||
v-if="showEditModal || showAddSongModal"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm overflow-y-auto"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-white/80 backdrop-blur-sm overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-2xl bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden my-auto"
|
||||
class="w-full max-w-2xl bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden my-auto"
|
||||
@click.stop
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800/50 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100">
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900">
|
||||
{{ showEditModal ? '编辑歌曲' : '手动添加歌曲' }}
|
||||
</h3>
|
||||
<button
|
||||
class="text-zinc-500 hover:text-zinc-300 transition-colors"
|
||||
class="text-zinc-500 hover:text-zinc-600 transition-colors"
|
||||
@click="showEditModal ? cancelEditSong() : cancelAddSong()"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -545,14 +545,14 @@
|
||||
v-model="editForm.title"
|
||||
type="text"
|
||||
placeholder="输入歌曲标题"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
v-model="addForm.title"
|
||||
type="text"
|
||||
placeholder="输入歌曲标题"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all"
|
||||
>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
@@ -564,14 +564,14 @@
|
||||
v-model="editForm.artist"
|
||||
type="text"
|
||||
placeholder="输入歌手姓名"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
v-model="addForm.artist"
|
||||
type="text"
|
||||
placeholder="输入歌手姓名"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -587,7 +587,7 @@
|
||||
v-model="editUserSearchQuery"
|
||||
type="text"
|
||||
placeholder="搜索用户姓名或用户名"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all"
|
||||
@focus="showEditUserDropdown = true"
|
||||
@input="searchEditUsers()"
|
||||
>
|
||||
@@ -596,7 +596,7 @@
|
||||
v-model="userSearchQuery"
|
||||
type="text"
|
||||
placeholder="搜索用户姓名或用户名"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all"
|
||||
@focus="showUserDropdown = true"
|
||||
@input="searchUsers()"
|
||||
>
|
||||
@@ -605,7 +605,7 @@
|
||||
class="absolute right-4 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<div
|
||||
class="w-4 h-4 border-2 border-blue-500/20 border-t-blue-500 rounded-full animate-spin"
|
||||
class="w-4 h-4 border-2 border-blue-200 border-t-blue-500 rounded-full animate-spin"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -617,18 +617,18 @@
|
||||
? showEditUserDropdown && filteredEditUsers.length > 0
|
||||
: showUserDropdown && filteredUsers.length > 0
|
||||
"
|
||||
class="absolute z-10 w-full mt-2 bg-zinc-900 border border-zinc-800 rounded-lg shadow-2xl overflow-hidden max-h-48 overflow-y-auto"
|
||||
class="absolute z-10 w-full mt-2 bg-white border border-zinc-200 rounded-lg shadow-2xl overflow-hidden max-h-48 overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
v-for="user in showEditModal
|
||||
? filteredEditUsers.slice(0, 10)
|
||||
: filteredUsers.slice(0, 10)"
|
||||
:key="user.id"
|
||||
class="px-4 py-3 hover:bg-zinc-800 cursor-pointer transition-colors flex items-center justify-between group"
|
||||
class="px-4 py-3 hover:bg-zinc-100 cursor-pointer transition-colors flex items-center justify-between group"
|
||||
@click="showEditModal ? selectEditUser(user) : selectUser(user)"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-zinc-200">{{ user.name }}</span>
|
||||
<span class="text-sm font-bold text-zinc-700">{{ user.name }}</span>
|
||||
<span class="text-[10px] text-zinc-500">@{{ user.username }}</span>
|
||||
</div>
|
||||
<Check
|
||||
@@ -674,7 +674,7 @@
|
||||
<span
|
||||
v-for="collaborator in selectedEditCollaborators"
|
||||
:key="collaborator.id"
|
||||
class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-zinc-950 border border-zinc-800 text-xs text-zinc-300"
|
||||
class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-white border border-zinc-200 text-xs text-zinc-600"
|
||||
>
|
||||
{{ getCollaboratorDisplayName(collaborator) }}
|
||||
<button
|
||||
@@ -690,7 +690,7 @@
|
||||
v-model="editCollaboratorSearchQuery"
|
||||
type="text"
|
||||
placeholder="搜索并添加联合投稿人"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 transition-all"
|
||||
@focus="showEditCollaboratorDropdown = true"
|
||||
@input="searchEditCollaborators()"
|
||||
>
|
||||
@@ -699,21 +699,21 @@
|
||||
class="absolute right-4 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<div
|
||||
class="w-4 h-4 border-2 border-blue-500/20 border-t-blue-500 rounded-full animate-spin"
|
||||
class="w-4 h-4 border-2 border-blue-200 border-t-blue-500 rounded-full animate-spin"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="showEditCollaboratorDropdown && filteredEditCollaborators.length > 0"
|
||||
class="absolute z-10 w-full mt-2 bg-zinc-900 border border-zinc-800 rounded-lg shadow-2xl overflow-hidden max-h-48 overflow-y-auto"
|
||||
class="absolute z-10 w-full mt-2 bg-white border border-zinc-200 rounded-lg shadow-2xl overflow-hidden max-h-48 overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
v-for="user in filteredEditCollaborators.slice(0, 10)"
|
||||
:key="user.id"
|
||||
class="px-4 py-3 hover:bg-zinc-800 cursor-pointer transition-colors flex items-center justify-between group"
|
||||
class="px-4 py-3 hover:bg-zinc-100 cursor-pointer transition-colors flex items-center justify-between group"
|
||||
@click="selectEditCollaborator(user)"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-zinc-200">{{ user.name }}</span>
|
||||
<span class="text-sm font-bold text-zinc-700">{{ user.name }}</span>
|
||||
<span class="text-[10px] text-zinc-500">@{{ user.username }}</span>
|
||||
</div>
|
||||
<Plus :size="14" class="text-blue-500 opacity-70 group-hover:opacity-100" />
|
||||
@@ -759,8 +759,8 @@
|
||||
class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border text-[10px] font-black tracking-widest uppercase transition-all disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
:class="
|
||||
submissionNoteClearRequested
|
||||
? 'border-zinc-700 bg-zinc-950 text-zinc-300 hover:border-zinc-600'
|
||||
: 'border-amber-500/20 bg-amber-500/10 text-amber-400 hover:bg-amber-500/20'
|
||||
? 'border-zinc-200 bg-white text-zinc-600 hover:border-zinc-300'
|
||||
: 'border-amber-500/20 bg-amber-500/10 text-amber-700 hover:bg-amber-500/20'
|
||||
"
|
||||
@click="
|
||||
submissionNoteClearRequested
|
||||
@@ -775,7 +775,7 @@
|
||||
v-model="editForm.submissionNote"
|
||||
:disabled="submissionNoteClearRequested"
|
||||
placeholder="填写歌曲备注"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-4 text-sm text-zinc-200 focus:outline-none focus:border-blue-500/30 min-h-[120px] resize-none transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-4 text-sm text-zinc-700 focus:outline-none focus:border-blue-200 min-h-[120px] resize-none transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
/>
|
||||
<div
|
||||
v-if="submissionNoteClearRequested"
|
||||
@@ -787,16 +787,16 @@
|
||||
<textarea
|
||||
v-model="submissionNoteClearReason"
|
||||
placeholder="可选:请输入清空备注的理由,例如:备注内容违规"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none focus:border-amber-500/30 min-h-[96px] resize-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none focus:border-amber-200 min-h-[96px] resize-none transition-all"
|
||||
/>
|
||||
<label class="flex items-center gap-3 cursor-pointer group">
|
||||
<input
|
||||
v-model="notifyOnSubmissionNoteClear"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded border-zinc-800 bg-zinc-950 accent-amber-500"
|
||||
class="w-4 h-4 rounded border-zinc-200 bg-white accent-amber-500"
|
||||
>
|
||||
<div>
|
||||
<span class="text-xs font-bold text-zinc-300 group-hover:text-amber-300 transition-colors"
|
||||
<span class="text-xs font-bold text-zinc-600 group-hover:text-amber-300 transition-colors"
|
||||
>清空后发送通知</span
|
||||
>
|
||||
<p class="text-[10px] text-zinc-600 font-medium">
|
||||
@@ -806,7 +806,7 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 pt-4 border-t border-zinc-800/50">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 pt-4 border-t border-zinc-200">
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black text-zinc-600 uppercase tracking-widest px-1"
|
||||
>音乐平台 (可选)</label
|
||||
@@ -841,14 +841,14 @@
|
||||
v-model="editForm.musicId"
|
||||
type="text"
|
||||
placeholder="输入平台唯一标识符"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none transition-all"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
v-model="addForm.musicId"
|
||||
type="text"
|
||||
placeholder="输入平台唯一标识符"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none transition-all"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -862,14 +862,14 @@
|
||||
v-model="editForm.cover"
|
||||
type="text"
|
||||
placeholder="http://..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none transition-all"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
v-model="addForm.cover"
|
||||
type="text"
|
||||
placeholder="http://..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none transition-all"
|
||||
>
|
||||
<p
|
||||
v-if="
|
||||
@@ -901,14 +901,14 @@
|
||||
v-model="editForm.playUrl"
|
||||
type="text"
|
||||
placeholder="http://..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none transition-all"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
v-model="addForm.playUrl"
|
||||
type="text"
|
||||
placeholder="http://..."
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-4 py-3 text-sm text-zinc-200 focus:outline-none transition-all"
|
||||
class="w-full bg-white border border-zinc-200 rounded-lg px-4 py-3 text-sm text-zinc-700 focus:outline-none transition-all"
|
||||
>
|
||||
<p
|
||||
v-if="
|
||||
@@ -932,9 +932,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-8 py-6 bg-zinc-900/50 border-t border-zinc-800/50 flex gap-3 justify-end">
|
||||
<div class="px-8 py-6 bg-zinc-50/50 border-t border-zinc-200 flex gap-3 justify-end">
|
||||
<button
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-300"
|
||||
class="px-6 py-2.5 text-xs font-bold text-zinc-500 hover:text-zinc-600"
|
||||
@click="showEditModal ? cancelEditSong() : cancelAddSong()"
|
||||
>
|
||||
取消
|
||||
|
||||
@@ -9,16 +9,16 @@
|
||||
>
|
||||
<div
|
||||
v-if="show"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-white/80 backdrop-blur-sm"
|
||||
@click="close"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden"
|
||||
class="w-full max-w-lg bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden"
|
||||
@click.stop
|
||||
>
|
||||
<div class="px-8 py-6 border-b border-zinc-800/50 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-100">投稿备注留言</h3>
|
||||
<button class="text-zinc-500 hover:text-zinc-300 transition-colors" @click="close">
|
||||
<div class="px-8 py-6 border-b border-zinc-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-black text-zinc-900">投稿备注留言</h3>
|
||||
<button class="text-zinc-500 hover:text-zinc-600 transition-colors" @click="close">
|
||||
<X :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -29,15 +29,15 @@
|
||||
:class="[
|
||||
'px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider border',
|
||||
isPublic
|
||||
? 'bg-blue-500/10 text-blue-400 border-blue-500/20'
|
||||
? 'bg-blue-500/10 text-blue-700 border-blue-200'
|
||||
: 'bg-amber-500/10 text-amber-500 border-amber-500/20'
|
||||
]"
|
||||
>
|
||||
{{ isPublic ? '公开备注' : '仅管理员可见' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="bg-zinc-950/50 border border-zinc-800/50 rounded-xl p-4">
|
||||
<p class="text-sm text-zinc-200 leading-relaxed whitespace-pre-wrap">
|
||||
<div class="bg-white/70 border border-zinc-200 rounded-xl p-4">
|
||||
<p class="text-sm text-zinc-700 leading-relaxed whitespace-pre-wrap">
|
||||
{{ content }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6 mt-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-black text-zinc-100 tracking-tight">用户管理</h2>
|
||||
<h2 class="text-2xl font-black text-zinc-900 tracking-tight">用户管理</h2>
|
||||
<p class="text-xs text-zinc-500 mt-1">系统共有 {{ totalUsers }} 位成员 · 权限与账户管理</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
@@ -15,14 +15,14 @@
|
||||
添加
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 md:flex-none flex items-center justify-center gap-2 px-4 py-2.5 bg-zinc-900 border border-zinc-800 text-zinc-300 text-xs font-black rounded-lg transition-all uppercase tracking-widest"
|
||||
class="flex-1 md:flex-none flex items-center justify-center gap-2 px-4 py-2.5 bg-white border border-zinc-200 text-zinc-600 text-xs font-black rounded-lg transition-all uppercase tracking-widest"
|
||||
@click="showImportModal = true"
|
||||
>
|
||||
<FileSpreadsheet class="text-emerald-500" :size="14" />
|
||||
导入
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 md:flex-none flex items-center justify-center gap-2 px-4 py-2.5 bg-zinc-900 border border-zinc-800 text-zinc-300 text-xs font-black rounded-lg transition-all uppercase tracking-widest"
|
||||
class="flex-1 md:flex-none flex items-center justify-center gap-2 px-4 py-2.5 bg-white border border-zinc-200 text-zinc-600 text-xs font-black rounded-lg transition-all uppercase tracking-widest"
|
||||
@click="showBatchUpdateModal = true"
|
||||
>
|
||||
<Layers class="text-purple-500" :size="14" />
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<div
|
||||
class="bg-zinc-900/40 border border-zinc-800/60 rounded-xl p-3 flex flex-col lg:flex-row gap-3 items-center"
|
||||
class="bg-zinc-50/40 border border-zinc-200/60 rounded-xl p-3 flex flex-col lg:flex-row gap-3 items-center"
|
||||
>
|
||||
<div class="relative flex-1 w-full group">
|
||||
<Search
|
||||
@@ -42,7 +42,7 @@
|
||||
/>
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
class="w-full bg-zinc-950 border border-zinc-800/80 rounded-lg pl-11 pr-4 py-2.5 text-xs focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200"
|
||||
class="w-full bg-white border border-zinc-200/80 rounded-lg pl-11 pr-4 py-2.5 text-xs focus:outline-none focus:border-blue-200 transition-all text-zinc-700"
|
||||
placeholder="通过姓名或学号搜索..."
|
||||
type="text"
|
||||
>
|
||||
@@ -84,7 +84,7 @@
|
||||
/>
|
||||
|
||||
<button
|
||||
class="p-3 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-600 hover:text-blue-400 transition-all shadow-sm"
|
||||
class="p-3 bg-white border border-zinc-200 rounded-lg text-zinc-600 hover:text-blue-700 transition-all shadow-sm"
|
||||
@click="loadUsers(1)"
|
||||
>
|
||||
<RefreshCw :size="14" />
|
||||
@@ -96,17 +96,17 @@
|
||||
<div class="space-y-4">
|
||||
<div v-if="loading" class="flex flex-col items-center justify-center py-20 text-zinc-500">
|
||||
<div
|
||||
class="w-8 h-8 border-2 border-blue-500/20 border-t-blue-500 rounded-full animate-spin mb-4"
|
||||
class="w-8 h-8 border-2 border-blue-200 border-t-blue-500 rounded-full animate-spin mb-4"
|
||||
/>
|
||||
<div class="text-xs font-black uppercase tracking-widest">正在加载用户...</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="users.length === 0"
|
||||
class="flex flex-col items-center justify-center py-20 bg-zinc-900/20 border border-zinc-800/50 rounded-xl"
|
||||
class="flex flex-col items-center justify-center py-20 bg-zinc-50/20 border border-zinc-200 rounded-xl"
|
||||
>
|
||||
<div
|
||||
class="w-16 h-16 rounded-lg bg-zinc-800/50 flex items-center justify-center text-zinc-600 mb-4"
|
||||
class="w-16 h-16 rounded-lg bg-zinc-100/50 flex items-center justify-center text-zinc-600 mb-4"
|
||||
>
|
||||
<Search :size="32" />
|
||||
</div>
|
||||
@@ -118,12 +118,12 @@
|
||||
<template v-else>
|
||||
<!-- 桌面端表格 -->
|
||||
<div
|
||||
class="hidden lg:block bg-zinc-900/20 border border-zinc-800/50 rounded-xl overflow-hidden shadow-lg custom-scrollbar"
|
||||
class="hidden lg:block bg-zinc-50/20 border border-zinc-200 rounded-xl overflow-hidden shadow-lg custom-scrollbar"
|
||||
>
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr
|
||||
class="bg-zinc-900/60 border-b border-zinc-800 text-[10px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
class="bg-zinc-50/60 border-b border-zinc-200 text-[10px] font-black text-zinc-600 uppercase tracking-widest"
|
||||
>
|
||||
<th class="px-6 py-5 text-left">用户详情</th>
|
||||
<th class="px-6 py-5 text-left">角色权限</th>
|
||||
@@ -137,7 +137,7 @@
|
||||
<tr
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
class="group hover:bg-zinc-800/30 transition-all text-xs cursor-pointer"
|
||||
class="group hover:bg-zinc-100/30 transition-all text-xs cursor-pointer"
|
||||
@click="showUserDetail(user, $event)"
|
||||
>
|
||||
<td class="px-6 py-5">
|
||||
@@ -145,17 +145,17 @@
|
||||
<img
|
||||
v-if="user.avatar && !failedImages[user.id]"
|
||||
:src="user.avatar"
|
||||
class="w-10 h-10 rounded-xl object-cover border border-zinc-700/50"
|
||||
class="w-10 h-10 rounded-xl object-cover border border-zinc-200"
|
||||
@error="handleImageError(user.id)"
|
||||
>
|
||||
<div
|
||||
v-else
|
||||
class="w-10 h-10 rounded-xl bg-zinc-800 flex items-center justify-center font-black text-zinc-500 group-hover:text-zinc-300 transition-colors border border-zinc-700/50"
|
||||
class="w-10 h-10 rounded-xl bg-zinc-100 flex items-center justify-center font-black text-zinc-500 group-hover:text-zinc-600 transition-colors border border-zinc-200"
|
||||
>
|
||||
{{ (user.name || user.username || '?').charAt(0).toUpperCase() }}
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-black text-zinc-100">{{ user.name || user.username }}</p>
|
||||
<p class="font-black text-zinc-900">{{ user.name || user.username }}</p>
|
||||
<p class="text-[10px] text-zinc-600 font-mono mt-0.5">
|
||||
ID: {{ user.username }}
|
||||
</p>
|
||||
@@ -167,9 +167,9 @@
|
||||
class="px-2 py-0.5 text-[10px] font-black rounded border uppercase tracking-widest"
|
||||
:class="{
|
||||
'bg-orange-500/10 text-orange-400 border-orange-500/20': user.role?.toUpperCase() === 'SUPER_ADMIN',
|
||||
'bg-blue-500/10 text-blue-400 border-blue-500/20': user.role?.toUpperCase() === 'ADMIN' || user.role?.toUpperCase() === 'MINISTER',
|
||||
'bg-blue-500/10 text-blue-700 border-blue-200': user.role?.toUpperCase() === 'ADMIN' || user.role?.toUpperCase() === 'MINISTER',
|
||||
'bg-purple-500/10 text-purple-400 border-purple-500/20': user.role?.toUpperCase() === 'SONG_ADMIN' || user.role?.toUpperCase() === 'STAFF' || user.role?.toUpperCase() === 'VICE_MINISTER',
|
||||
'bg-zinc-800 text-zinc-500 border-zinc-700/50': !['SUPER_ADMIN', 'ADMIN', 'MINISTER', 'SONG_ADMIN', 'STAFF', 'VICE_MINISTER'].includes(user.role?.toUpperCase())
|
||||
'bg-zinc-100 text-zinc-500 border-zinc-200': !['SUPER_ADMIN', 'ADMIN', 'MINISTER', 'SONG_ADMIN', 'STAFF', 'VICE_MINISTER'].includes(user.role?.toUpperCase())
|
||||
}"
|
||||
>
|
||||
{{ getRoleDisplayName(user.role?.toUpperCase()) }}
|
||||
@@ -222,14 +222,14 @@
|
||||
>
|
||||
<button
|
||||
:disabled="isSelf(user) || !canManageTargetUser(user)"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-500 hover:text-blue-400 transition-colors disabled:opacity-20 disabled:cursor-not-allowed action-btn"
|
||||
class="p-2 bg-white border border-zinc-200 rounded-xl text-zinc-500 hover:text-blue-700 transition-colors disabled:opacity-20 disabled:cursor-not-allowed action-btn"
|
||||
:title="!canManageTargetUser(user) ? '权限不足:无法编辑该用户' : '编辑用户'"
|
||||
@click="editUser(user)"
|
||||
>
|
||||
<Edit2 :size="13" />
|
||||
</button>
|
||||
<button
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-500 hover:text-purple-400 transition-colors action-btn"
|
||||
class="p-2 bg-white border border-zinc-200 rounded-xl text-zinc-500 hover:text-purple-400 transition-colors action-btn"
|
||||
title="查看歌曲"
|
||||
@click="viewUserSongs(user)"
|
||||
>
|
||||
@@ -237,7 +237,7 @@
|
||||
</button>
|
||||
<button
|
||||
:disabled="isSelf(user) || !canResetPasswordFor(user)"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-500 hover:text-amber-400 transition-colors disabled:opacity-20 disabled:cursor-not-allowed action-btn"
|
||||
class="p-2 bg-white border border-zinc-200 rounded-xl text-zinc-500 hover:text-amber-700 transition-colors disabled:opacity-20 disabled:cursor-not-allowed action-btn"
|
||||
:title="!canResetPasswordFor(user) ? '权限不足:无法重置该用户的密码' : '重置密码'"
|
||||
@click="resetPassword(user)"
|
||||
>
|
||||
@@ -245,7 +245,7 @@
|
||||
</button>
|
||||
<button
|
||||
:disabled="isSelf(user) || !canManageTargetUser(user)"
|
||||
class="p-2 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-500 hover:text-red-400 transition-colors disabled:opacity-20 disabled:cursor-not-allowed action-btn"
|
||||
class="p-2 bg-white border border-zinc-200 rounded-xl text-zinc-500 hover:text-red-400 transition-colors disabled:opacity-20 disabled:cursor-not-allowed action-btn"
|
||||
:title="!canManageTargetUser(user) ? '权限不足:无法删除该用户' : '删除用户'"
|
||||
@click="confirmDeleteUser(user)"
|
||||
>
|
||||
@@ -263,7 +263,7 @@
|
||||
<div
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
class="bg-zinc-900/40 border border-zinc-800 rounded-xl p-5 space-y-5 shadow-lg shadow-black/20"
|
||||
class="bg-zinc-50/40 border border-zinc-200 rounded-xl p-5 space-y-5 shadow-lg shadow-zinc-900/5"
|
||||
@click="showUserDetail(user, $event)"
|
||||
>
|
||||
<div class="flex items-start justify-between">
|
||||
@@ -271,17 +271,17 @@
|
||||
<img
|
||||
v-if="user.avatar && !failedImages[user.id]"
|
||||
:src="user.avatar"
|
||||
class="w-12 h-12 rounded-lg object-cover border border-zinc-700"
|
||||
class="w-12 h-12 rounded-lg object-cover border border-zinc-200"
|
||||
@error="handleImageError(user.id)"
|
||||
>
|
||||
<div
|
||||
v-else
|
||||
class="w-12 h-12 rounded-lg bg-zinc-800 flex items-center justify-center font-black text-lg text-zinc-500 border border-zinc-700"
|
||||
class="w-12 h-12 rounded-lg bg-zinc-100 flex items-center justify-center font-black text-lg text-zinc-500 border border-zinc-200"
|
||||
>
|
||||
{{ (user.name || user.username || '?').charAt(0).toUpperCase() }}
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-base font-black text-zinc-100">{{ user.name || user.username }}</h4>
|
||||
<h4 class="text-base font-black text-zinc-900">{{ user.name || user.username }}</h4>
|
||||
<p class="text-xs text-zinc-500 font-mono">@{{ user.username }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -320,7 +320,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 py-4 border-y border-zinc-800/50">
|
||||
<div class="grid grid-cols-2 gap-4 py-4 border-y border-zinc-200">
|
||||
<div class="space-y-1">
|
||||
<p class="text-[9px] font-black text-zinc-600 uppercase tracking-widest">
|
||||
角色/等级
|
||||
@@ -330,9 +330,9 @@
|
||||
class="px-2 py-0.5 text-[10px] font-black rounded border uppercase tracking-widest"
|
||||
:class="{
|
||||
'bg-orange-500/10 text-orange-400 border-orange-500/20': user.role?.toUpperCase() === 'SUPER_ADMIN',
|
||||
'bg-blue-500/10 text-blue-400 border-blue-500/20': user.role?.toUpperCase() === 'ADMIN' || user.role?.toUpperCase() === 'MINISTER',
|
||||
'bg-blue-500/10 text-blue-700 border-blue-200': user.role?.toUpperCase() === 'ADMIN' || user.role?.toUpperCase() === 'MINISTER',
|
||||
'bg-purple-500/10 text-purple-400 border-purple-500/20': user.role?.toUpperCase() === 'SONG_ADMIN' || user.role?.toUpperCase() === 'STAFF' || user.role?.toUpperCase() === 'VICE_MINISTER',
|
||||
'bg-zinc-800 text-zinc-500 border-zinc-700/50': !['SUPER_ADMIN', 'ADMIN', 'MINISTER', 'SONG_ADMIN', 'STAFF', 'VICE_MINISTER'].includes(user.role?.toUpperCase())
|
||||
'bg-zinc-100 text-zinc-500 border-zinc-200': !['SUPER_ADMIN', 'ADMIN', 'MINISTER', 'SONG_ADMIN', 'STAFF', 'VICE_MINISTER'].includes(user.role?.toUpperCase())
|
||||
}"
|
||||
>
|
||||
{{ getRoleDisplayName(user.role?.toUpperCase()) }}
|
||||
@@ -343,7 +343,7 @@
|
||||
<p class="text-[9px] font-black text-zinc-600 uppercase tracking-widest">
|
||||
所在班级
|
||||
</p>
|
||||
<p class="text-xs font-bold text-zinc-300">
|
||||
<p class="text-xs font-bold text-zinc-600">
|
||||
{{ user.grade || '-' }} {{ user.class || '-' }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -364,21 +364,21 @@
|
||||
<div class="flex gap-2 action-buttons">
|
||||
<button
|
||||
:disabled="isSelf(user) || !canManageTargetUser(user)"
|
||||
class="flex-1 py-2.5 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-400 flex items-center justify-center gap-2 text-[10px] font-black uppercase tracking-widest active:bg-blue-600 active:text-white transition-colors disabled:opacity-20 action-btn"
|
||||
class="flex-1 py-2.5 bg-white border border-zinc-200 rounded-lg text-zinc-400 flex items-center justify-center gap-2 text-[10px] font-black uppercase tracking-widest active:bg-blue-600 active:text-white transition-colors disabled:opacity-20 action-btn"
|
||||
:title="!canManageTargetUser(user) ? '权限不足:无法编辑该用户' : '编辑用户'"
|
||||
@click="editUser(user)"
|
||||
>
|
||||
<Edit2 :size="12" /> 编辑
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 py-2.5 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-400 flex items-center justify-center gap-2 text-[10px] font-black uppercase tracking-widest active:bg-purple-600 active:text-white transition-colors action-btn"
|
||||
class="flex-1 py-2.5 bg-white border border-zinc-200 rounded-lg text-zinc-400 flex items-center justify-center gap-2 text-[10px] font-black uppercase tracking-widest active:bg-purple-600 active:text-white transition-colors action-btn"
|
||||
@click="viewUserSongs(user)"
|
||||
>
|
||||
<Music :size="12" /> 记录
|
||||
</button>
|
||||
<button
|
||||
:disabled="isSelf(user) || !canResetPasswordFor(user)"
|
||||
class="flex-1 py-2.5 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-400 flex items-center justify-center gap-2 text-[10px] font-black uppercase tracking-widest active:bg-amber-600 active:text-white transition-colors disabled:opacity-20 action-btn"
|
||||
class="flex-1 py-2.5 bg-white border border-zinc-200 rounded-lg text-zinc-400 flex items-center justify-center gap-2 text-[10px] font-black uppercase tracking-widest active:bg-amber-600 active:text-white transition-colors disabled:opacity-20 action-btn"
|
||||
:title="!canResetPasswordFor(user) ? '权限不足:无法重置该用户的密码' : '重置密码'"
|
||||
@click="resetPassword(user)"
|
||||
>
|
||||
@@ -386,7 +386,7 @@
|
||||
</button>
|
||||
<button
|
||||
:disabled="isSelf(user) || !canManageTargetUser(user)"
|
||||
class="px-3 py-2.5 bg-zinc-950 border border-zinc-800 rounded-lg text-zinc-400 active:bg-red-600 active:text-white transition-colors disabled:opacity-20 action-btn"
|
||||
class="px-3 py-2.5 bg-white border border-zinc-200 rounded-lg text-zinc-400 active:bg-red-600 active:text-white transition-colors disabled:opacity-20 action-btn"
|
||||
:title="!canManageTargetUser(user) ? '权限不足:无法删除该用户' : '删除用户'"
|
||||
@click="confirmDeleteUser(user)"
|
||||
>
|
||||
@@ -422,15 +422,15 @@
|
||||
@click="closeModal"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 w-full max-w-lg rounded-3xl overflow-hidden shadow-2xl"
|
||||
class="bg-white border border-zinc-200 w-full max-w-lg rounded-3xl overflow-hidden shadow-2xl"
|
||||
@click.stop
|
||||
>
|
||||
<div class="p-8">
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight flex items-center gap-3">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-2xl bg-blue-600/10 flex items-center justify-center text-blue-500"
|
||||
class="w-10 h-10 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-500"
|
||||
>
|
||||
<UserPlus v-if="!editingUser" :size="20" />
|
||||
<Edit2 v-else :size="20" />
|
||||
@@ -440,7 +440,7 @@
|
||||
<p class="text-xs text-zinc-500 mt-1 ml-13">请填写以下账户详细信息以继续</p>
|
||||
</div>
|
||||
<button
|
||||
class="p-3 bg-zinc-800/50 hover:bg-zinc-800 text-zinc-500 hover:text-zinc-200 rounded-2xl transition-all"
|
||||
class="p-3 bg-zinc-100/50 hover:bg-zinc-100 text-zinc-500 hover:text-zinc-700 rounded-2xl transition-all"
|
||||
@click="closeModal"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -460,7 +460,7 @@
|
||||
/>
|
||||
<input
|
||||
v-model="userForm.name"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-blue-200 transition-all text-zinc-700"
|
||||
placeholder="请输入真实姓名"
|
||||
type="text"
|
||||
>
|
||||
@@ -478,7 +478,7 @@
|
||||
<input
|
||||
v-model="userForm.username"
|
||||
:disabled="!!editingUser"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200 disabled:opacity-50"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-blue-200 transition-all text-zinc-700 disabled:opacity-50"
|
||||
placeholder="登录唯一标识"
|
||||
type="text"
|
||||
>
|
||||
@@ -497,7 +497,7 @@
|
||||
/>
|
||||
<input
|
||||
v-model="userForm.password"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-blue-500/30 transition-all text-zinc-200"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-blue-200 transition-all text-zinc-700"
|
||||
placeholder="设置安全访问密码"
|
||||
type="password"
|
||||
>
|
||||
@@ -571,7 +571,7 @@
|
||||
|
||||
<div class="flex gap-3 mt-10">
|
||||
<button
|
||||
class="flex-1 px-6 py-4 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
class="flex-1 px-6 py-4 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
@click="closeModal"
|
||||
>
|
||||
取消操作
|
||||
@@ -606,7 +606,7 @@
|
||||
@click="closeResetPassword"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 w-full max-w-md rounded-3xl overflow-hidden shadow-2xl"
|
||||
class="bg-white border border-zinc-200 w-full max-w-md rounded-3xl overflow-hidden shadow-2xl"
|
||||
@click.stop
|
||||
>
|
||||
<div class="p-8 text-center">
|
||||
@@ -615,10 +615,10 @@
|
||||
>
|
||||
<Lock :size="32" />
|
||||
</div>
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight">重置访问密码</h3>
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight">重置访问密码</h3>
|
||||
<p class="text-xs text-zinc-500 mt-2 mb-8">
|
||||
正在为
|
||||
<span class="text-zinc-200 font-bold">{{ resetPasswordUser.name }}</span> 修改登录凭据
|
||||
<span class="text-zinc-700 font-bold">{{ resetPasswordUser.name }}</span> 修改登录凭据
|
||||
</p>
|
||||
|
||||
<div class="space-y-4 text-left">
|
||||
@@ -633,7 +633,7 @@
|
||||
/>
|
||||
<input
|
||||
v-model="passwordForm.password"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-amber-500/30 transition-all text-zinc-200"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-amber-200 transition-all text-zinc-700"
|
||||
placeholder="设置高强度新密码"
|
||||
type="password"
|
||||
>
|
||||
@@ -650,7 +650,7 @@
|
||||
/>
|
||||
<input
|
||||
v-model="passwordForm.confirmPassword"
|
||||
class="w-full bg-zinc-950 border border-zinc-800 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-amber-500/30 transition-all text-zinc-200"
|
||||
class="w-full bg-white border border-zinc-200 rounded-2xl pl-11 pr-4 py-3 text-xs focus:outline-none focus:border-amber-200 transition-all text-zinc-700"
|
||||
placeholder="请再次输入以确认"
|
||||
type="password"
|
||||
>
|
||||
@@ -668,7 +668,7 @@
|
||||
|
||||
<div class="flex gap-3 mt-8">
|
||||
<button
|
||||
class="flex-1 px-6 py-4 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
class="flex-1 px-6 py-4 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
@click="closeResetPassword"
|
||||
>
|
||||
取消
|
||||
@@ -703,13 +703,13 @@
|
||||
@click="closeImportModal"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 w-full max-w-2xl rounded-3xl overflow-hidden shadow-2xl"
|
||||
class="bg-white border border-zinc-200 w-full max-w-2xl rounded-3xl overflow-hidden shadow-2xl"
|
||||
@click.stop
|
||||
>
|
||||
<div class="p-8">
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight flex items-center gap-3">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-2xl bg-emerald-600/10 flex items-center justify-center text-emerald-500"
|
||||
>
|
||||
@@ -722,7 +722,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="p-3 bg-zinc-800/50 hover:bg-zinc-800 text-zinc-500 hover:text-zinc-200 rounded-2xl transition-all"
|
||||
class="p-3 bg-zinc-100/50 hover:bg-zinc-100 text-zinc-500 hover:text-zinc-700 rounded-2xl transition-all"
|
||||
@click="closeImportModal"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -730,9 +730,9 @@
|
||||
</div>
|
||||
|
||||
<div class="space-y-6 max-h-[60vh] overflow-y-auto pr-2 custom-scrollbar">
|
||||
<div class="p-5 bg-zinc-950/50 border border-zinc-800/50 rounded-3xl space-y-4">
|
||||
<div class="flex items-center gap-2 text-zinc-300 font-bold text-sm mb-2">
|
||||
<Info :size="16" class="text-blue-400" />
|
||||
<div class="p-5 bg-white/70 border border-zinc-200 rounded-3xl space-y-4">
|
||||
<div class="flex items-center gap-2 text-zinc-600 font-bold text-sm mb-2">
|
||||
<Info :size="16" class="text-blue-700" />
|
||||
导入说明
|
||||
</div>
|
||||
<p class="text-xs text-zinc-500 leading-relaxed">
|
||||
@@ -740,16 +740,16 @@
|
||||
注意:第一行可以是标题行(会自动跳过),角色字段必须匹配系统定义的角色。
|
||||
</p>
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-800/80">
|
||||
<div class="overflow-hidden rounded-2xl border border-zinc-200/80">
|
||||
<table class="w-full text-[10px] text-left">
|
||||
<thead class="bg-zinc-900 text-zinc-400 uppercase tracking-tighter">
|
||||
<thead class="bg-white text-zinc-400 uppercase tracking-tighter">
|
||||
<tr>
|
||||
<th class="px-3 py-2 border-b border-zinc-800">姓名</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-800">账号名</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-800">密码</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-800">角色</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-800">年级</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-800">班级</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-200">姓名</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-200">账号名</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-200">密码</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-200">角色</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-200">年级</th>
|
||||
<th class="px-3 py-2 border-b border-zinc-200">班级</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-zinc-500">
|
||||
@@ -767,7 +767,7 @@
|
||||
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div
|
||||
class="p-3 bg-blue-500/5 border border-blue-500/10 rounded-2xl text-[10px] text-blue-400/80 leading-relaxed flex-1"
|
||||
class="p-3 bg-blue-500/5 border border-blue-500/10 rounded-2xl text-[10px] text-blue-700/80 leading-relaxed flex-1"
|
||||
>
|
||||
<strong>支持的角色:</strong>
|
||||
<span v-if="isSuperAdmin"
|
||||
@@ -804,15 +804,15 @@
|
||||
@change="handleFileUpload"
|
||||
>
|
||||
<div
|
||||
class="w-full py-10 border-2 border-dashed border-zinc-800 group-hover:border-emerald-500/50 group-hover:bg-emerald-500/5 rounded-3xl transition-all flex flex-col items-center justify-center gap-3"
|
||||
class="w-full py-10 border-2 border-dashed border-zinc-200 group-hover:border-emerald-500/50 group-hover:bg-emerald-500/5 rounded-3xl transition-all flex flex-col items-center justify-center gap-3"
|
||||
>
|
||||
<div
|
||||
class="w-12 h-12 rounded-2xl bg-zinc-900 flex items-center justify-center text-zinc-600 group-hover:text-emerald-500 transition-colors"
|
||||
class="w-12 h-12 rounded-2xl bg-white flex items-center justify-center text-zinc-600 group-hover:text-emerald-500 transition-colors"
|
||||
>
|
||||
<Upload :size="24" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-sm font-bold text-zinc-300">点击或拖拽上传 Excel 文件</p>
|
||||
<p class="text-sm font-bold text-zinc-600">点击或拖拽上传 Excel 文件</p>
|
||||
<p class="text-xs text-zinc-500 mt-1">仅支持 .xlsx 格式</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -829,7 +829,7 @@
|
||||
|
||||
<div
|
||||
v-if="importSuccess"
|
||||
class="p-4 bg-emerald-500/10 border border-emerald-500/20 rounded-2xl flex items-center gap-3 text-emerald-400 text-xs"
|
||||
class="p-4 bg-emerald-500/10 border border-emerald-500/20 rounded-2xl flex items-center gap-3 text-emerald-700 text-xs"
|
||||
>
|
||||
<CheckCircle2 :size="16" />
|
||||
{{ importSuccess }}
|
||||
@@ -841,9 +841,9 @@
|
||||
>预览数据 ({{ previewData.length }}条记录)</label
|
||||
>
|
||||
</div>
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-800 bg-zinc-950/30">
|
||||
<div class="overflow-hidden rounded-3xl border border-zinc-200 bg-white/60">
|
||||
<table class="w-full text-xs text-left">
|
||||
<thead class="bg-zinc-900/50 text-zinc-500">
|
||||
<thead class="bg-zinc-50/50 text-zinc-500">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-medium">姓名</th>
|
||||
<th class="px-4 py-3 font-medium">账号</th>
|
||||
@@ -855,13 +855,13 @@
|
||||
<tr
|
||||
v-for="(row, index) in previewData.slice(0, 5)"
|
||||
:key="index"
|
||||
class="text-zinc-300"
|
||||
class="text-zinc-600"
|
||||
>
|
||||
<td class="px-4 py-3">{{ row.name }}</td>
|
||||
<td class="px-4 py-3">{{ row.username }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<span
|
||||
class="px-2 py-0.5 bg-zinc-800 rounded-md text-[10px] text-zinc-400 uppercase"
|
||||
class="px-2 py-0.5 bg-zinc-100 rounded-md text-[10px] text-zinc-400 uppercase"
|
||||
>{{ row.role }}</span
|
||||
>
|
||||
</td>
|
||||
@@ -883,7 +883,7 @@
|
||||
|
||||
<div class="flex gap-3 mt-8">
|
||||
<button
|
||||
class="flex-1 px-6 py-4 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
class="flex-1 px-6 py-4 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
@click="closeImportModal"
|
||||
>
|
||||
取消
|
||||
@@ -945,15 +945,15 @@
|
||||
@click="closeUserDetailModal"
|
||||
>
|
||||
<div
|
||||
class="bg-zinc-900 border border-zinc-800 w-full max-w-3xl rounded-3xl overflow-hidden shadow-2xl"
|
||||
class="bg-white border border-zinc-200 w-full max-w-3xl rounded-3xl overflow-hidden shadow-2xl"
|
||||
@click.stop
|
||||
>
|
||||
<div class="p-8">
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h3 class="text-xl font-black text-zinc-100 tracking-tight flex items-center gap-3">
|
||||
<h3 class="text-xl font-black text-zinc-900 tracking-tight flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-2xl bg-blue-600/10 flex items-center justify-center text-blue-500"
|
||||
class="w-10 h-10 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-500"
|
||||
>
|
||||
<User :size="20" />
|
||||
</div>
|
||||
@@ -962,7 +962,7 @@
|
||||
<p class="text-xs text-zinc-500 mt-1 ml-13">查看完整的账户资料与操作记录</p>
|
||||
</div>
|
||||
<button
|
||||
class="p-3 bg-zinc-800/50 hover:bg-zinc-800 text-zinc-500 hover:text-zinc-200 rounded-2xl transition-all"
|
||||
class="p-3 bg-zinc-100/50 hover:bg-zinc-100 text-zinc-500 hover:text-zinc-700 rounded-2xl transition-all"
|
||||
@click="closeUserDetailModal"
|
||||
>
|
||||
<X :size="20" />
|
||||
@@ -982,27 +982,27 @@
|
||||
基本信息
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl space-y-1">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-2xl space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
用户 ID
|
||||
</div>
|
||||
<div class="text-sm font-bold text-zinc-300">{{ selectedUserDetail.id }}</div>
|
||||
<div class="text-sm font-bold text-zinc-600">{{ selectedUserDetail.id }}</div>
|
||||
</div>
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl space-y-1">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-2xl space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
姓名
|
||||
</div>
|
||||
<div class="text-sm font-bold text-zinc-300">{{ selectedUserDetail.name }}</div>
|
||||
<div class="text-sm font-bold text-zinc-600">{{ selectedUserDetail.name }}</div>
|
||||
</div>
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl space-y-1">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-2xl space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
用户名
|
||||
</div>
|
||||
<div class="text-sm font-bold text-zinc-300">
|
||||
<div class="text-sm font-bold text-zinc-600">
|
||||
{{ selectedUserDetail.username }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl space-y-1">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-2xl space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
角色权限
|
||||
</div>
|
||||
@@ -1017,19 +1017,19 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl space-y-1">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-2xl space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
年级
|
||||
</div>
|
||||
<div class="text-sm font-bold text-zinc-300">
|
||||
<div class="text-sm font-bold text-zinc-600">
|
||||
{{ selectedUserDetail.grade || '未设置' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl space-y-1">
|
||||
<div class="p-4 bg-white/70 border border-zinc-200 rounded-2xl space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
班级
|
||||
</div>
|
||||
<div class="text-sm font-bold text-zinc-300">
|
||||
<div class="text-sm font-bold text-zinc-600">
|
||||
{{ selectedUserDetail.class || '未设置' }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1046,7 +1046,7 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div
|
||||
class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl flex items-center justify-between"
|
||||
class="p-4 bg-white/70 border border-zinc-200 rounded-2xl flex items-center justify-between"
|
||||
>
|
||||
<div class="space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
@@ -1089,7 +1089,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl flex items-center justify-between"
|
||||
class="p-4 bg-white/70 border border-zinc-200 rounded-2xl flex items-center justify-between"
|
||||
>
|
||||
<div class="space-y-1">
|
||||
<div class="text-[10px] font-black text-zinc-600 uppercase tracking-tighter">
|
||||
@@ -1113,7 +1113,7 @@
|
||||
'w-8 h-8 rounded-xl flex items-center justify-center',
|
||||
selectedUserDetail.meowNickname
|
||||
? 'bg-emerald-500/10 text-emerald-500'
|
||||
: 'bg-zinc-800 text-zinc-600'
|
||||
: 'bg-zinc-100 text-zinc-600'
|
||||
]"
|
||||
>
|
||||
<AtSign :size="16" />
|
||||
@@ -1133,18 +1133,18 @@
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
class="flex items-center justify-between text-xs p-3 bg-zinc-950/30 rounded-xl border border-zinc-800/30"
|
||||
class="flex items-center justify-between text-xs p-3 bg-white/60 rounded-xl border border-zinc-200/30"
|
||||
>
|
||||
<span class="text-zinc-500">最后登录</span>
|
||||
<span class="text-zinc-300 font-medium">{{
|
||||
<span class="text-zinc-600 font-medium">{{
|
||||
formatDate(selectedUserDetail.lastLogin)
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center justify-between text-xs p-3 bg-zinc-950/30 rounded-xl border border-zinc-800/30"
|
||||
class="flex items-center justify-between text-xs p-3 bg-white/60 rounded-xl border border-zinc-200/30"
|
||||
>
|
||||
<span class="text-zinc-500">登录 IP</span>
|
||||
<span class="text-zinc-300 font-medium">{{
|
||||
<span class="text-zinc-600 font-medium">{{
|
||||
selectedUserDetail.lastLoginIp || '未知'
|
||||
}}</span>
|
||||
</div>
|
||||
@@ -1159,18 +1159,18 @@
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
class="flex items-center justify-between text-xs p-3 bg-zinc-950/30 rounded-xl border border-zinc-800/30"
|
||||
class="flex items-center justify-between text-xs p-3 bg-white/60 rounded-xl border border-zinc-200/30"
|
||||
>
|
||||
<span class="text-zinc-500">创建时间</span>
|
||||
<span class="text-zinc-300 font-medium">{{
|
||||
<span class="text-zinc-600 font-medium">{{
|
||||
formatDate(selectedUserDetail.createdAt)
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center justify-between text-xs p-3 bg-zinc-950/30 rounded-xl border border-zinc-800/30"
|
||||
class="flex items-center justify-between text-xs p-3 bg-white/60 rounded-xl border border-zinc-200/30"
|
||||
>
|
||||
<span class="text-zinc-500">最近更新</span>
|
||||
<span class="text-zinc-300 font-medium">{{
|
||||
<span class="text-zinc-600 font-medium">{{
|
||||
formatDate(selectedUserDetail.updatedAt)
|
||||
}}</span>
|
||||
</div>
|
||||
@@ -1201,21 +1201,21 @@
|
||||
|
||||
<div
|
||||
v-else-if="statusLogs.length === 0"
|
||||
class="py-12 text-center bg-zinc-950/30 border border-zinc-800/50 rounded-3xl"
|
||||
class="py-12 text-center bg-white/60 border border-zinc-200 rounded-3xl"
|
||||
>
|
||||
<p class="text-xs text-zinc-600">暂无状态变更记录</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-4">
|
||||
<div
|
||||
class="relative pl-6 space-y-6 before:absolute before:left-[11px] before:top-2 before:bottom-2 before:w-0.5 before:bg-zinc-800"
|
||||
class="relative pl-6 space-y-6 before:absolute before:left-[11px] before:top-2 before:bottom-2 before:w-0.5 before:bg-zinc-100"
|
||||
>
|
||||
<div v-for="log in statusLogs" :key="log.id" class="relative">
|
||||
<div
|
||||
class="absolute -left-[23px] top-1.5 w-3 h-3 rounded-full bg-zinc-900 border-2 border-zinc-700 ring-4 ring-zinc-900"
|
||||
class="absolute -left-[23px] top-1.5 w-3 h-3 rounded-full bg-white border-2 border-zinc-200 ring-4 ring-zinc-900"
|
||||
/>
|
||||
<div
|
||||
class="p-4 bg-zinc-950/50 border border-zinc-800/50 rounded-2xl space-y-3"
|
||||
class="p-4 bg-white/70 border border-zinc-200 rounded-2xl space-y-3"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -1244,7 +1244,7 @@
|
||||
|
||||
<div
|
||||
v-if="log.reason"
|
||||
class="text-xs text-zinc-400 bg-zinc-900/50 p-2.5 rounded-xl border border-zinc-800/30 leading-relaxed"
|
||||
class="text-xs text-zinc-400 bg-zinc-50/50 p-2.5 rounded-xl border border-zinc-200/30 leading-relaxed"
|
||||
>
|
||||
<span class="text-zinc-600 font-bold mr-1">原因:</span>
|
||||
{{ log.reason }}
|
||||
@@ -1252,12 +1252,12 @@
|
||||
|
||||
<div class="flex items-center gap-2 text-[10px]">
|
||||
<div
|
||||
class="w-4 h-4 rounded-full bg-zinc-800 flex items-center justify-center text-zinc-500"
|
||||
class="w-4 h-4 rounded-full bg-zinc-100 flex items-center justify-center text-zinc-500"
|
||||
>
|
||||
<User :size="8" />
|
||||
</div>
|
||||
<span class="text-zinc-500">操作者:</span>
|
||||
<span class="text-zinc-300 font-bold">{{
|
||||
<span class="text-zinc-600 font-bold">{{
|
||||
log.operator?.name || '系统'
|
||||
}}</span>
|
||||
</div>
|
||||
@@ -1277,7 +1277,7 @@
|
||||
|
||||
<div class="mt-8">
|
||||
<button
|
||||
class="w-full px-6 py-4 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
class="w-full px-6 py-4 bg-zinc-100 hover:bg-zinc-200 text-zinc-600 text-xs font-black rounded-2xl transition-all uppercase tracking-widest"
|
||||
@click="closeUserDetailModal"
|
||||
>
|
||||
关闭窗口
|
||||
|
||||
@@ -26,16 +26,16 @@
|
||||
>
|
||||
<div
|
||||
v-if="show"
|
||||
class="relative w-full max-w-4xl max-h-[85vh] flex flex-col bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden"
|
||||
class="relative w-full max-w-4xl max-h-[85vh] flex flex-col bg-white border border-zinc-200 rounded-xl shadow-2xl overflow-hidden"
|
||||
@click.stop
|
||||
>
|
||||
<!-- 头部 -->
|
||||
<div
|
||||
class="flex items-center justify-between px-6 py-4 border-b border-zinc-800 bg-zinc-900/50 backdrop-blur-sm"
|
||||
class="flex items-center justify-between px-6 py-4 border-b border-zinc-200 bg-zinc-50/50 backdrop-blur-sm"
|
||||
>
|
||||
<h3 class="text-lg font-semibold text-white">用户歌曲信息</h3>
|
||||
<button
|
||||
class="p-2 text-zinc-400 hover:text-white hover:bg-zinc-800 rounded-lg transition-colors"
|
||||
class="p-2 text-zinc-400 hover:text-zinc-900 hover:bg-zinc-100 rounded-lg transition-colors"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<svg
|
||||
@@ -56,7 +56,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 主体内容 -->
|
||||
<div class="flex-1 flex flex-col min-h-0 overflow-hidden bg-zinc-900">
|
||||
<div class="flex-1 flex flex-col min-h-0 overflow-hidden bg-white">
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="flex-1 flex flex-col items-center justify-center">
|
||||
<LoadingState message="加载数据中..." spinner-type="circle" />
|
||||
@@ -80,7 +80,7 @@
|
||||
<!-- 内容 -->
|
||||
<div v-else-if="userSongs" class="flex-1 flex flex-col min-h-0">
|
||||
<!-- 用户信息头部 -->
|
||||
<div class="px-6 py-4 bg-zinc-900 border-b border-zinc-800">
|
||||
<div class="px-6 py-4 bg-white border-b border-zinc-200">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center gap-3">
|
||||
<h4 class="text-base font-medium text-white">{{ userSongs.user.name }}</h4>
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
<!-- 工具栏 (标签页 + 过滤) -->
|
||||
<div
|
||||
class="flex flex-col sm:flex-row items-center justify-between border-b border-zinc-800 bg-zinc-900/50"
|
||||
class="flex flex-col sm:flex-row items-center justify-between border-b border-zinc-200 bg-zinc-50/50"
|
||||
>
|
||||
<!-- 标签页 -->
|
||||
<div class="flex w-full sm:w-auto overflow-x-auto no-scrollbar">
|
||||
@@ -110,7 +110,7 @@
|
||||
:class="
|
||||
activeTab === tab.id
|
||||
? 'text-blue-500'
|
||||
: 'text-zinc-400 hover:text-white hover:bg-zinc-800/50'
|
||||
: 'text-zinc-400 hover:text-zinc-900 hover:bg-zinc-100/50'
|
||||
"
|
||||
@click="activeTab = tab.id"
|
||||
>
|
||||
@@ -120,7 +120,7 @@
|
||||
:class="
|
||||
activeTab === tab.id
|
||||
? 'bg-blue-500/10 text-blue-500'
|
||||
: 'bg-zinc-800 text-zinc-500'
|
||||
: 'bg-zinc-100 text-zinc-500'
|
||||
"
|
||||
>
|
||||
{{ getTabCount(tab.id) }}
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
<!-- 学期过滤器 -->
|
||||
<div
|
||||
class="w-full sm:w-auto px-4 py-2 border-t sm:border-t-0 sm:border-l border-zinc-800 flex items-center gap-3 bg-zinc-900/30"
|
||||
class="w-full sm:w-auto px-4 py-2 border-t sm:border-t-0 sm:border-l border-zinc-200 flex items-center gap-3 bg-zinc-50/30"
|
||||
>
|
||||
<CustomSelect
|
||||
v-model="selectedSemester"
|
||||
@@ -160,7 +160,7 @@
|
||||
<div
|
||||
v-for="song in filteredSongs"
|
||||
:key="song.id"
|
||||
class="group flex flex-col sm:flex-row sm:items-center justify-between gap-4 p-4 rounded-lg border border-zinc-800 bg-zinc-800/20 hover:bg-zinc-800/50 hover:border-zinc-700 transition-all duration-200"
|
||||
class="group flex flex-col sm:flex-row sm:items-center justify-between gap-4 p-4 rounded-lg border border-zinc-200 bg-zinc-100/20 hover:bg-zinc-100/50 hover:border-zinc-200 transition-all duration-200"
|
||||
>
|
||||
<!-- 歌曲信息 -->
|
||||
<div class="flex-1 min-w-0">
|
||||
@@ -168,7 +168,7 @@
|
||||
<h5 class="text-sm font-medium text-white truncate">{{ song.title }}</h5>
|
||||
<span
|
||||
v-if="song.semester"
|
||||
class="px-1.5 py-0.5 rounded text-[10px] font-medium bg-zinc-800 text-zinc-400 border border-zinc-700"
|
||||
class="px-1.5 py-0.5 rounded text-[10px] font-medium bg-zinc-100 text-zinc-400 border border-zinc-200"
|
||||
>
|
||||
{{ song.semester }}
|
||||
</span>
|
||||
@@ -214,7 +214,7 @@
|
||||
|
||||
<!-- Submitter Info (if available) -->
|
||||
<div v-if="song.requester" class="hidden sm:block text-right min-w-[80px]">
|
||||
<div class="text-xs text-zinc-300">{{ song.requester.name }}</div>
|
||||
<div class="text-xs text-zinc-600">{{ song.requester.name }}</div>
|
||||
<div class="text-[10px] text-zinc-500">
|
||||
{{ song.requester.grade }}{{ song.requester.class }}
|
||||
</div>
|
||||
@@ -224,10 +224,10 @@
|
||||
<!-- 投稿人信息 (移动端) -->
|
||||
<div
|
||||
v-if="song.requester"
|
||||
class="sm:hidden pt-3 mt-1 border-t border-zinc-800/50 flex justify-between items-center text-xs"
|
||||
class="sm:hidden pt-3 mt-1 border-t border-zinc-200 flex justify-between items-center text-xs"
|
||||
>
|
||||
<span class="text-zinc-500">投稿人:</span>
|
||||
<span class="text-zinc-300">
|
||||
<span class="text-zinc-600">
|
||||
{{ song.requester.name }}
|
||||
<span class="text-zinc-500 ml-1">
|
||||
{{ song.requester.grade }}{{ song.requester.class }}
|
||||
@@ -422,7 +422,7 @@ const getStatusClasses = (song) => {
|
||||
return 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20'
|
||||
}
|
||||
if (song.scheduled) {
|
||||
return 'bg-blue-500/10 text-blue-500 border-blue-500/20'
|
||||
return 'bg-blue-500/10 text-blue-500 border-blue-200'
|
||||
}
|
||||
return 'bg-amber-500/10 text-amber-500 border-amber-500/20'
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ watch(
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--surface-primary, var(--bg-primary, #1a1a1a));
|
||||
background: var(--surface-primary, var(--bg-primary, #ffffff));
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-primary, #2a2a2a);
|
||||
width: 100%;
|
||||
@@ -342,7 +342,7 @@ watch(
|
||||
|
||||
.retry-btn {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
color: var(--text-primary, #09090b);
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
@@ -414,7 +414,7 @@ watch(
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
color: var(--text-primary, #09090b);
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
<template>
|
||||
<div class="space-y-6 max-w-5xl mx-auto pb-24 md:pb-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
<div class="flex flex-col gap-2">
|
||||
<h2 class="text-2xl md:text-3xl font-bold tracking-tight text-zinc-100">工作流与使用教程</h2>
|
||||
<h2 class="text-2xl md:text-3xl font-bold tracking-tight text-zinc-900">工作流与使用教程</h2>
|
||||
<p class="text-zinc-400">了解校园广播站点歌系统的标准操作流程和功能说明,帮助您更高效地管理点歌与排期。</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- 歌曲管理流程 -->
|
||||
<div class="bg-zinc-900/50 border border-zinc-800 rounded-xl p-6 hover:border-zinc-700 transition-colors">
|
||||
<div class="flex items-center gap-3 mb-4 text-blue-400">
|
||||
<div class="bg-zinc-50/50 border border-zinc-200 rounded-xl p-6 hover:border-zinc-200 transition-colors">
|
||||
<div class="flex items-center gap-3 mb-4 text-blue-700">
|
||||
<CheckCircle2 class="w-6 h-6" />
|
||||
<h3 class="text-xl font-semibold">1. 歌曲管理与审核</h3>
|
||||
</div>
|
||||
<ol class="space-y-4 text-zinc-300">
|
||||
<ol class="space-y-4 text-zinc-600">
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-500/20 text-blue-400 flex items-center justify-center text-sm font-bold">1</span>
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-500/20 text-blue-700 flex items-center justify-center text-sm font-bold">1</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">查看所有歌曲</p>
|
||||
<p class="font-medium text-zinc-900">查看所有歌曲</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">进入“歌曲管理”页面,系统会列出所有用户提交的歌曲。</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-500/20 text-blue-400 flex items-center justify-center text-sm font-bold">2</span>
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-500/20 text-blue-700 flex items-center justify-center text-sm font-bold">2</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">试听与检查</p>
|
||||
<p class="font-medium text-zinc-900">试听与检查</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">点击播放按钮试听歌曲质量,检查留言内容是否合规,确保没有不当言论。</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-500/20 text-blue-400 flex items-center justify-center text-sm font-bold">3</span>
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-blue-500/20 text-blue-700 flex items-center justify-center text-sm font-bold">3</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">拒绝或修改状态</p>
|
||||
<p class="font-medium text-zinc-900">拒绝或修改状态</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">如不符合要求,可以在歌曲管理中拒绝该歌曲,并填写退回原因;审核通过的歌曲可直接用于排期。</p>
|
||||
</div>
|
||||
</li>
|
||||
@@ -38,30 +38,30 @@
|
||||
</div>
|
||||
|
||||
<!-- 排期流程 -->
|
||||
<div class="bg-zinc-900/50 border border-zinc-800 rounded-xl p-6 hover:border-zinc-700 transition-colors">
|
||||
<div class="flex items-center gap-3 mb-4 text-emerald-400">
|
||||
<div class="bg-zinc-50/50 border border-zinc-200 rounded-xl p-6 hover:border-zinc-200 transition-colors">
|
||||
<div class="flex items-center gap-3 mb-4 text-emerald-700">
|
||||
<CalendarDays class="w-6 h-6" />
|
||||
<h3 class="text-xl font-semibold">2. 节目排期流程</h3>
|
||||
</div>
|
||||
<ol class="space-y-4 text-zinc-300">
|
||||
<ol class="space-y-4 text-zinc-600">
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center text-sm font-bold">1</span>
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-emerald-500/20 text-emerald-700 flex items-center justify-center text-sm font-bold">1</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">选择日期与时段</p>
|
||||
<p class="font-medium text-zinc-900">选择日期与时段</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">在“排期管理”中,选择目标播放日期和播出时段。</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center text-sm font-bold">2</span>
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-emerald-500/20 text-emerald-700 flex items-center justify-center text-sm font-bold">2</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">拖拽编排</p>
|
||||
<p class="font-medium text-zinc-900">拖拽编排</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">将左侧“待排歌曲”拖拽至右侧的“播放列表”中,可上下拖拽调整播放顺序。</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center text-sm font-bold">3</span>
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-emerald-500/20 text-emerald-700 flex items-center justify-center text-sm font-bold">3</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">发布排期</p>
|
||||
<p class="font-medium text-zinc-900">发布排期</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">排期完成后,点击“发布排期”。发布后用户即可在前端页面看到该日期的节目单。</p>
|
||||
</div>
|
||||
</li>
|
||||
@@ -69,23 +69,23 @@
|
||||
</div>
|
||||
|
||||
<!-- 播音流程 -->
|
||||
<div class="bg-zinc-900/50 border border-zinc-800 rounded-xl p-6 hover:border-zinc-700 transition-colors">
|
||||
<div class="bg-zinc-50/50 border border-zinc-200 rounded-xl p-6 hover:border-zinc-200 transition-colors">
|
||||
<div class="flex items-center gap-3 mb-4 text-purple-400">
|
||||
<Mic2 class="w-6 h-6" />
|
||||
<h3 class="text-xl font-semibold">3. 播音与打印</h3>
|
||||
</div>
|
||||
<ol class="space-y-4 text-zinc-300">
|
||||
<ol class="space-y-4 text-zinc-600">
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-purple-500/20 text-purple-400 flex items-center justify-center text-sm font-bold">1</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">生成歌单图片/打印</p>
|
||||
<p class="font-medium text-zinc-900">生成歌单图片/打印</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">播音前,前往“导入酷狗”生成今日节目单图片用于宣传,或在“打印排期”中生成播音员用的纸质稿。</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex gap-3">
|
||||
<span class="flex-shrink-0 w-6 h-6 rounded-full bg-purple-500/20 text-purple-400 flex items-center justify-center text-sm font-bold">2</span>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-100">标记已播</p>
|
||||
<p class="font-medium text-zinc-900">标记已播</p>
|
||||
<p class="text-sm text-zinc-500 mt-1">播音结束后,系统可自动/手动将排期内的歌曲标记为“已播”,方便统计数据。</p>
|
||||
</div>
|
||||
</li>
|
||||
@@ -93,22 +93,22 @@
|
||||
</div>
|
||||
|
||||
<!-- 常见问题 -->
|
||||
<div class="bg-zinc-900/50 border border-zinc-800 rounded-xl p-6 hover:border-zinc-700 transition-colors">
|
||||
<div class="flex items-center gap-3 mb-4 text-amber-400">
|
||||
<div class="bg-zinc-50/50 border border-zinc-200 rounded-xl p-6 hover:border-zinc-200 transition-colors">
|
||||
<div class="flex items-center gap-3 mb-4 text-amber-700">
|
||||
<HelpCircle class="w-6 h-6" />
|
||||
<h3 class="text-xl font-semibold">常见问题 (FAQ)</h3>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h4 class="text-sm font-medium text-zinc-200">Q: 为什么找不到某些待排歌曲?</h4>
|
||||
<h4 class="text-sm font-medium text-zinc-700">Q: 为什么找不到某些待排歌曲?</h4>
|
||||
<p class="text-sm text-zinc-500 mt-1">A: 请检查是否在排期页面的左侧选错了标签(如普通投稿/重播申请),或者该歌曲还未通过审核。</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-sm font-medium text-zinc-200">Q: 什么是选曲日期?</h4>
|
||||
<h4 class="text-sm font-medium text-zinc-700">Q: 什么是选曲日期?</h4>
|
||||
<p class="text-sm text-zinc-500 mt-1">A: 选曲日期是用户在点歌时可选择的期望播放日期。请在“选曲日期”设置中提前开放可用的日期。</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-sm font-medium text-zinc-200">Q: 权限不足怎么办?</h4>
|
||||
<h4 class="text-sm font-medium text-zinc-700">Q: 权限不足怎么办?</h4>
|
||||
<p class="text-sm text-zinc-500 mt-1">A: 不同的功能需要不同的管理员角色(如歌曲管理员、超级管理员),请联系超级管理员调整您的角色权限。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user