九宫格优化
This commit is contained in:
@@ -151,6 +151,16 @@
|
||||
<a-switch v-model:checked="cfg.introEnabled" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-2">
|
||||
<a-form-item label="祝福弹幕">
|
||||
<a-switch v-model:checked="cfg.danmakuEnabled" />
|
||||
<div class="text-[11px] text-[#8A8680] mt-1">关闭后前台不展示弹幕,也不显示送祝福入口。</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="弹幕显示时间">
|
||||
<a-switch v-model:checked="cfg.danmakuShowTime" :disabled="!cfg.danmakuEnabled" />
|
||||
<div class="text-[11px] text-[#8A8680] mt-1">开启后弹幕后缀展示相对时间(刚刚、x分钟前等)。</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
@@ -161,6 +171,10 @@
|
||||
<div class="text-[12px] text-[#8A8680] leading-relaxed mb-4">
|
||||
上传多首背景音乐(支持 mp3 等格式),访客首次交互后自动播放当前曲目,可在前台随时切换或暂停。
|
||||
</div>
|
||||
<a-form-item label="随机播放">
|
||||
<a-switch v-model:checked="cfg.musicRandomEnabled" />
|
||||
<div class="text-[11px] text-[#8A8680] mt-1">开启后访客进入时随机选曲;本地缓存 24 小时内再次进入保持同一首。</div>
|
||||
</a-form-item>
|
||||
<a-empty v-if="!cfg.musicList.length" description="尚未上传音乐" />
|
||||
<a-card v-for="(t, i) in cfg.musicList" :key="i" :bordered="false"
|
||||
class="!shadow-sm mb-3 !border" :class="t.url === cfg.activeMusicUrl ? '!border-[#a88c6b]' : '!border-[#a88c6b]/15'">
|
||||
@@ -386,19 +400,65 @@
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
|
||||
<!-- 弹幕审核 -->
|
||||
<a-tab-pane key="danmaku" tab="弹幕审核">
|
||||
<div class="flex items-center justify-between mb-3 gap-3 flex-wrap">
|
||||
<a-radio-group v-model:value="danmakuFilter" button-style="solid" size="small">
|
||||
<a-radio-button value="">全部</a-radio-button>
|
||||
<a-radio-button value="pending">待审核</a-radio-button>
|
||||
<a-radio-button value="approved">已通过</a-radio-button>
|
||||
<a-radio-button value="rejected">已拒绝</a-radio-button>
|
||||
</a-radio-group>
|
||||
<a-button size="small" @click="loadDanmaku"><i class="fa-solid fa-rotate-right mr-1"></i>刷新</a-button>
|
||||
</div>
|
||||
<a-table :columns="danmakuColumns" :data-source="danmakuList" :loading="danmakuLoading" row-key="id" size="small" :pagination="{ pageSize: 8 }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key==='content'">
|
||||
<span class="inline-flex items-center gap-2 min-w-0">
|
||||
<i class="danmaku-admin-dot" :style="{ background: resolveDanmakuColor(record.color) }" />
|
||||
<span class="truncate">{{ record.content }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key==='source'">
|
||||
<a-tag :color="record.source === 'rsvp' ? 'blue' : 'gold'">{{ record.source === 'rsvp' ? '回执' : '弹幕' }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key==='status'">
|
||||
<a-tag :color="danmakuStatusColor(record.status)">{{ danmakuStatusLabel(record.status) }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key==='created_at'">
|
||||
{{ formatTime(record.created_at) }}
|
||||
</template>
|
||||
<template v-else-if="column.key==='actions'">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" class="!bg-[#a88c6b] !border-[#a88c6b]"
|
||||
:disabled="record.status === 'approved'"
|
||||
@click="setDanmakuStatus(record.id, 'approved')">通过</a-button>
|
||||
<a-button size="small" danger
|
||||
:disabled="record.status === 'rejected'"
|
||||
@click="setDanmakuStatus(record.id, 'rejected')">拒绝</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref, onMounted } from 'vue'
|
||||
import { computed, reactive, ref, onMounted, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getConfig, saveConfig, uploadImage, getRsvpList, getUploadConfig, saveUploadConfig } from '@/api/wedding'
|
||||
import {
|
||||
getConfig, saveConfig, uploadImage, getRsvpList, getUploadConfig, saveUploadConfig,
|
||||
getDanmakuList, updateDanmakuStatus,
|
||||
} from '@/api/wedding'
|
||||
import { useAdminStore } from '@/stores/admin'
|
||||
import ImageField from '@/components/ImageField.vue'
|
||||
import { BORDER_STYLES, borderClass } from '@/composables/borderStyles'
|
||||
import { resolveDanmakuColor } from '@/utils/danmakuColors'
|
||||
|
||||
const router = useRouter()
|
||||
const adminStore = useAdminStore()
|
||||
@@ -410,6 +470,9 @@ const uploading = ref(false)
|
||||
const uploadConfigSaving = ref(false)
|
||||
const rsvpLoading = ref(false)
|
||||
const rsvpList = ref([])
|
||||
const danmakuLoading = ref(false)
|
||||
const danmakuList = ref([])
|
||||
const danmakuFilter = ref('pending')
|
||||
|
||||
const cfg = reactive(defaultConfig())
|
||||
const uploadCfg = reactive(defaultUploadConfig())
|
||||
@@ -425,6 +488,9 @@ function defaultConfig() {
|
||||
endImg: 'https://images.unsplash.com/photo-1519741497674-611481863552?auto=format&fit=crop&w=800&q=80',
|
||||
musicList: [{ url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', name: '背景音乐 1' }],
|
||||
activeMusicUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
|
||||
musicRandomEnabled: false,
|
||||
danmakuEnabled: true,
|
||||
danmakuShowTime: true,
|
||||
scrollMode: 'snap', petalEnabled: true, bubbleEnabled: true, introEnabled: true,
|
||||
introExitEffect: 'wind',
|
||||
freeScrollInterval: 50, pageSwitchDuration: 800, firstScreenDuration: 4500,
|
||||
@@ -693,6 +759,41 @@ async function loadRsvp() {
|
||||
finally { rsvpLoading.value = false }
|
||||
}
|
||||
|
||||
const danmakuColumns = [
|
||||
{ title: '姓名', dataIndex: 'name', key: 'name', width: 100 },
|
||||
{ title: '内容', dataIndex: 'content', key: 'content', ellipsis: true },
|
||||
{ title: '来源', key: 'source', width: 80 },
|
||||
{ title: '状态', key: 'status', width: 90 },
|
||||
{ title: '时间', dataIndex: 'created_at', key: 'created_at', width: 170 },
|
||||
{ title: '操作', key: 'actions', width: 160 },
|
||||
]
|
||||
const danmakuStatusLabel = (s) => ({ pending: '待审核', approved: '已通过', rejected: '已拒绝' }[s] || s)
|
||||
const danmakuStatusColor = (s) => ({ pending: 'orange', approved: 'green', rejected: 'default' }[s] || 'default')
|
||||
|
||||
async function loadDanmaku() {
|
||||
danmakuLoading.value = true
|
||||
try {
|
||||
const params = danmakuFilter.value ? { status: danmakuFilter.value } : undefined
|
||||
const res = await getDanmakuList(params)
|
||||
danmakuList.value = res.data || []
|
||||
} catch (e) { /* 忽略 */ }
|
||||
finally { danmakuLoading.value = false }
|
||||
}
|
||||
|
||||
async function setDanmakuStatus(id, status) {
|
||||
try {
|
||||
await updateDanmakuStatus(id, status)
|
||||
message.success(status === 'approved' ? '已通过' : '已拒绝')
|
||||
loadDanmaku()
|
||||
} catch (e) {
|
||||
message.error(e.message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
watch(danmakuFilter, () => {
|
||||
if (adminStore.isAdmin) loadDanmaku()
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await getConfig()
|
||||
@@ -714,10 +815,14 @@ onMounted(async () => {
|
||||
cfg.freeScrollInterval = Number(cfg.freeScrollInterval) || 50
|
||||
cfg.pageSwitchDuration = Number(cfg.pageSwitchDuration) || 800
|
||||
cfg.firstScreenDuration = Number.isFinite(Number(cfg.firstScreenDuration)) ? Number(cfg.firstScreenDuration) : 4500
|
||||
if (typeof cfg.musicRandomEnabled !== 'boolean') cfg.musicRandomEnabled = false
|
||||
if (typeof cfg.danmakuEnabled !== 'boolean') cfg.danmakuEnabled = true
|
||||
if (typeof cfg.danmakuShowTime !== 'boolean') cfg.danmakuShowTime = true
|
||||
}
|
||||
} catch (e) { /* 使用默认配置 */ }
|
||||
if (adminStore.isAdmin) {
|
||||
loadRsvp()
|
||||
loadDanmaku()
|
||||
loadUploadConfig()
|
||||
}
|
||||
})
|
||||
@@ -727,4 +832,8 @@ onMounted(async () => {
|
||||
.admin-tabs :deep(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) { color: #a88c6b; }
|
||||
.admin-tabs :deep(.ant-tabs-ink-bar) { background: #a88c6b; }
|
||||
.admin-tabs :deep(.ant-tabs-tab:hover .ant-tabs-tab-btn) { color: #967b5c; }
|
||||
.danmaku-admin-dot {
|
||||
width: 10px; height: 10px; border-radius: 9999px; flex-shrink: 0;
|
||||
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user