Compare commits
No commits in common. "3ca39fc010704f654e11f4b56ca22284e55c87ae" and "265ce0c6c7141cef0416b0348ac62d773be7d66f" have entirely different histories.
3ca39fc010
...
265ce0c6c7
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -23,7 +23,6 @@ declare module '@vue/runtime-core' {
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
|
||||
@ -7,8 +7,6 @@ import type { RouteLocationNormalized, NavigationGuardNext } from 'vue-router'
|
||||
import { ElLoading } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/el-message.css'
|
||||
import 'element-plus/theme-chalk/el-message-box.css'
|
||||
import 'element-plus/theme-chalk/el-image-viewer.css'
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
|
||||
// 全局路由导航前置守卫
|
||||
|
||||
@ -16,7 +16,6 @@ export interface MusicLibModel {
|
||||
_id: string
|
||||
name: string // 歌单名称
|
||||
path: string // 歌单文件路径
|
||||
musicCount: number // 歌曲数量
|
||||
}
|
||||
|
||||
export interface MusicLyricModel {
|
||||
|
||||
@ -152,6 +152,7 @@ html, body, #app, .layout {
|
||||
.search-btn {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,10 +3,6 @@
|
||||
<el-header class="layout-header">
|
||||
<div class="main-title">博客管理后台</div>
|
||||
<div class="nav-btns-right">
|
||||
<el-button link @click="toggleDarkMode" style="color: #fff; font-size: 18px; margin-right: 12px;">
|
||||
<Moon v-if="!isDark" style="width: 18px" />
|
||||
<Sunny v-else style="width: 18px" />
|
||||
</el-button>
|
||||
<el-dropdown @command="dropdownMenuCommand">
|
||||
<el-button link type="primary">
|
||||
<UserFilled style="width: 18px" />
|
||||
@ -44,8 +40,8 @@
|
||||
<el-tab-pane v-for="tab in store.state.tabs" :key="tab.name" :label="tab.title" :name="tab.path" closable></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<div class="layout-content">
|
||||
<router-view class="main-view" v-slot="{ Component }" v-if="mainViewReady">
|
||||
<div class="layout-content" v-if="mainViewReady">
|
||||
<router-view class="main-view" v-slot="{ Component }">
|
||||
<keep-alive :include="keepViews">
|
||||
<component :is="Component" />
|
||||
</keep-alive>
|
||||
@ -96,32 +92,6 @@ const defaultActiveMenuKey = ref<string | null>(null)
|
||||
const openMenuNames = ref<string[]>([])
|
||||
const mainViewReady = ref(false)
|
||||
|
||||
const isDark = ref(false)
|
||||
function applyDarkMode(dark: boolean) {
|
||||
isDark.value = dark
|
||||
document.documentElement.classList.toggle('dark', dark)
|
||||
}
|
||||
function toggleDarkMode() {
|
||||
const next = !isDark.value
|
||||
applyDarkMode(next)
|
||||
localStorage.setItem('theme_mode', next ? 'dark' : 'light')
|
||||
}
|
||||
// 初始化主题
|
||||
;(() => {
|
||||
const saved = localStorage.getItem('theme_mode')
|
||||
if (saved) {
|
||||
applyDarkMode(saved === 'dark')
|
||||
} else {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
applyDarkMode(prefersDark)
|
||||
}
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||
if (!localStorage.getItem('theme_mode')) {
|
||||
applyDarkMode(e.matches)
|
||||
}
|
||||
})
|
||||
})()
|
||||
|
||||
const changePwdModal = ref(false)
|
||||
const changePwdLoading = ref(false)
|
||||
const changePwdForm = ref<VForm>()
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
<div class="btn-container">
|
||||
<el-button type="success" plain icon="VideoPlay" @click="playMusic">播放</el-button>
|
||||
<el-button type="primary" icon="Upload" @click="openUploadModal">上传音乐</el-button>
|
||||
<el-button type="warning" plain icon="Notebook" @click="libDrawerVisible = true">歌单管理</el-button>
|
||||
<div class="search-btn">
|
||||
<el-button type="primary" @click="loadDataBase(true)" icon="Search">搜索</el-button>
|
||||
<el-button @click="reset" icon="RefreshLeft">重置</el-button>
|
||||
@ -86,7 +85,7 @@
|
||||
</el-pagination>
|
||||
</div>
|
||||
<el-dialog v-model="modifyLyricModal" title="编辑歌词" :width="600" >
|
||||
<el-form v-loading="lyricLoading" ref="lyricForm" :model="lyricFormData" :rules="lyricRuleValidate" :label-width="120">
|
||||
<el-form ref="lyricForm" :model="lyricFormData" :rules="lyricRuleValidate" :label-width="120">
|
||||
<el-form-item label="网易云ID" prop="cloudId">
|
||||
<el-input v-model="lyricFormData.cloudId" />
|
||||
</el-form-item>
|
||||
@ -140,31 +139,6 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-drawer v-model="libDrawerVisible" title="歌单管理" size="900px">
|
||||
<el-button type="primary" icon="Plus" @click="addLibRow" style="margin-bottom: 12px">添加歌单</el-button>
|
||||
<el-table :data="libTableData" stripe>
|
||||
<el-table-column prop="name" label="名称">
|
||||
<template #default="scope">
|
||||
<el-input v-if="scope.row._isNew" v-model="scope.row.name" placeholder="歌单名称" />
|
||||
<span v-else>{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="path" label="路径">
|
||||
<template #default="scope">
|
||||
<el-input v-if="scope.row._isNew" v-model="scope.row.path" placeholder="music/歌单名/" />
|
||||
<span v-else>{{ scope.row.path }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="musicCount" label="歌曲数量" width="100" />
|
||||
<el-table-column label="操作" width="160">
|
||||
<template #default="scope">
|
||||
<el-button v-if="scope.row._isNew" link type="primary" icon="Check" @click="saveLib(scope.row)" :loading="libSaving">保存</el-button>
|
||||
<el-button v-if="scope.row._isNew" link icon="Close" @click="cancelAddLib">取消</el-button>
|
||||
<el-button v-if="!scope.row._isNew" link type="danger" icon="Delete" @click="removeLib(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-drawer>
|
||||
<el-drawer v-model="musicPlaying" :close-on-click-modal="false" size="40%" title="播放音乐">
|
||||
<a-player v-if="musicPlaying" ref="player" autoplay showLrc :list="musicList" v-model:music="currentMusic" @play="musicPlay"/>
|
||||
</el-drawer>
|
||||
@ -216,10 +190,6 @@ const currentMusic = ref<MusicPlayerItem>()
|
||||
const lyricForm = ref<VForm>()
|
||||
const musicUpload = ref<UploadInstance>()
|
||||
const player = ref<any>()
|
||||
const lyricLoading = ref(false)
|
||||
const libDrawerVisible = ref(false)
|
||||
const libTableData = ref<(MusicLibModel & { _isNew?: boolean })[]>([])
|
||||
const libSaving = ref(false)
|
||||
|
||||
const lyricRuleValidate = {
|
||||
cloudId: [
|
||||
@ -296,17 +266,13 @@ function remove(row: MusicModel) {
|
||||
}
|
||||
async function updateLyric(row: MusicModel) {
|
||||
currentRow.value = { ...row }
|
||||
lyricFormData.value = {}
|
||||
modifyLyricModal.value = true
|
||||
if (row.lyricId) {
|
||||
lyricLoading.value = true
|
||||
try {
|
||||
const data = await http.get<any, any>('/api/v2/music/lyric/get', {params: {lyricId: row.lyricId}})
|
||||
const data = (await http.get<any, any>('/api/v2/music/lyric/get', {params: {lyricId: row.lyricId}}))
|
||||
data.cloudId = data.cloudId ? data.cloudId.toString() : null
|
||||
lyricFormData.value = data
|
||||
} finally {
|
||||
lyricLoading.value = false
|
||||
}
|
||||
} else {
|
||||
lyricFormData.value = {}
|
||||
}
|
||||
}
|
||||
async function saveLyric() {
|
||||
@ -384,43 +350,9 @@ function musicPlay() {
|
||||
})
|
||||
}
|
||||
|
||||
function loadLibs() {
|
||||
return http.get<never, any>('/api/v2/music/lib/list').then(data => {
|
||||
musicLibs.value = data
|
||||
libTableData.value = data.map((item: MusicLibModel) => ({ ...item }))
|
||||
})
|
||||
}
|
||||
function addLibRow() {
|
||||
if (libTableData.value.some(item => (item as any)._isNew)) return
|
||||
libTableData.value.unshift({ _id: '', name: '', path: '', musicCount: 0, _isNew: true })
|
||||
}
|
||||
function cancelAddLib() {
|
||||
libTableData.value = libTableData.value.filter(item => !(item as any)._isNew)
|
||||
}
|
||||
async function saveLib(row: MusicLibModel & { _isNew?: boolean }) {
|
||||
if (!row.name || !row.path) {
|
||||
ElMessage.warning('请输入歌单名称和路径')
|
||||
return
|
||||
}
|
||||
libSaving.value = true
|
||||
try {
|
||||
await http.post<any, any>('/api/v2/music/lib/add', { name: row.name, path: row.path })
|
||||
ElMessage.success('歌单创建成功')
|
||||
await loadLibs()
|
||||
} finally {
|
||||
libSaving.value = false
|
||||
}
|
||||
}
|
||||
function removeLib(row: MusicLibModel) {
|
||||
ElMessageBox.confirm(`是否确认删除歌单「${row.name}」?`, '确认删除', { type: 'warning' }).then(async () => {
|
||||
await http.delete<any, any>('/api/v2/music/lib/delete', { params: { id: row._id } })
|
||||
ElMessage.success('歌单删除成功')
|
||||
await loadLibs()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// created
|
||||
loadLibs().then(() => {
|
||||
http.get<never, any>('/api/v2/music/lib/list').then(data => {
|
||||
musicLibs.value = data
|
||||
loadData()
|
||||
})
|
||||
http.get<never, any>('/api/v2/music/listExts').then(data => {
|
||||
|
||||
@ -75,16 +75,10 @@
|
||||
@current-change="pageChange">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<el-image-viewer
|
||||
v-if="previewVisible"
|
||||
:url-list="previewUrlList"
|
||||
:initial-index="previewIndex"
|
||||
@close="previewVisible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, h } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { MsgResult, Page } from '@/model/common.dto'
|
||||
@ -112,11 +106,8 @@ const store = useStore()
|
||||
const { loading, total, search, setLoadData, loadDataBase, reset, pageChange, pageSizeChange } = useBaseList(new PhotoWallPage())
|
||||
|
||||
const allowUploadExt = ['jpg', 'jpeg', 'png']
|
||||
const photowallData = ref<PhotoWallModel[]>([])
|
||||
const photowallData = ref([])
|
||||
const isUploading = ref(false)
|
||||
const previewVisible = ref(false)
|
||||
const previewUrlList = ref<string[]>([])
|
||||
const previewIndex = ref(0)
|
||||
|
||||
let selectedData: string[] = []
|
||||
|
||||
@ -166,11 +157,15 @@ function uploadError(error: Error) {
|
||||
ElMessage.error(error.message)
|
||||
}
|
||||
async function preview(row: PhotoWallModel) {
|
||||
const previewHeight = Math.floor(row.height * (500 / row.width))
|
||||
const pictureCdn = await http.get('/api/v2/common/config/picture_cdn')
|
||||
const index = photowallData.value.findIndex(item => item._id === row._id)
|
||||
previewUrlList.value = photowallData.value.map(item => `${pictureCdn}/${item.name}`)
|
||||
previewIndex.value = index >= 0 ? index : 0
|
||||
previewVisible.value = true
|
||||
ElMessageBox({
|
||||
title: '图片预览',
|
||||
message: h('img', { style: `width:500px;height:${previewHeight}px;`, src: `${pictureCdn}/${row.name}` }, ''),
|
||||
showCancelButton: false,
|
||||
confirmButtonText: '关闭',
|
||||
customStyle: {width: '530px', maxWidth: 'unset'}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// created
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user