refactor: 全面迁移 vue-class-component 至 Vue 3 Composition API
- 将所有页面组件从 class 语法重写为 <script setup> 风格 - App.vue / Login.vue / Home.vue / Welcome.vue - api: Hitokoto.vue / HitokotoAdd.vue / Music.vue / PhotoWall.vue / SourceImage.vue - system: Article.vue / Statistics.vue / SystemConfig.vue / SystemConfigAdd.vue / SystemRole.vue / SystemUser.vue - 新增 src/utils/http.ts:独立 axios 实例,含请求/响应拦截器,替代 vue-axios 插件 - baselist.ts:abstract class BaseList<T> → useBaseList<T>() 组合式函数 - types.ts:VForm 类型改用 Element Plus 原生 FormInstance - main.ts:移除 vue-axios 及内联 axios 配置,路由守卫直接引用 store - 依赖清理:移除 vue-class-component、vue-axios
This commit is contained in:
+65
-65
@@ -47,8 +47,8 @@
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<el-pagination background
|
||||
:page-sizes="$store.state.pageSizeOpts"
|
||||
:layout="$store.state.pageLayout"
|
||||
:page-sizes="store.state.pageSizeOpts"
|
||||
:layout="store.state.pageLayout"
|
||||
:current-page="search.pageNum"
|
||||
:total="total"
|
||||
@size-change="pageSizeChange"
|
||||
@@ -66,73 +66,15 @@
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import HitokotoAdd from './HitokotoAdd.vue'
|
||||
import { Options, Vue } from 'vue-class-component'
|
||||
import BaseList from '@/model/baselist'
|
||||
import { useBaseList } from '@/model/baselist'
|
||||
import { Page } from '@/model/common.dto'
|
||||
import HitokotoModel from '@/model/api/hitokoto'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { VForm } from '@/types'
|
||||
|
||||
let selectedData: string[] = []
|
||||
@Options({
|
||||
name: 'Hitokoto',
|
||||
components: { HitokotoAdd }
|
||||
})
|
||||
export default class Hitokoto extends BaseList<HitokotoPage> {
|
||||
search = new HitokotoPage()
|
||||
typeList: {label: string, value: string}[] = []
|
||||
hitokotoData: HitokotoModel[] = []
|
||||
formData: {[propName:string]: string | null} = {}
|
||||
addModal: boolean = false
|
||||
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
const data = await this.$http.get<HitokotoPage, any>('/api/v1/hitokoto/list', {params:this.search})
|
||||
selectedData = []
|
||||
this.loading = false
|
||||
this.total = data.total
|
||||
this.hitokotoData = data.data
|
||||
}
|
||||
async save() {
|
||||
((this.$refs.addForm as Vue).$refs.hitokotoForm as VForm).validate(async (valid: boolean) => {
|
||||
if (!valid) return
|
||||
this.modalLoading = true
|
||||
const data = await this.$http.post<any, any>('/api/v1/hitokoto/save', this.formData)
|
||||
this.modalLoading = false
|
||||
this.addModal = false
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
// 清空表单
|
||||
this.formData = {}
|
||||
})
|
||||
}
|
||||
deleteAll() {
|
||||
if(!selectedData.length) {
|
||||
ElMessage.warning('请选择要删除的数据')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`是否确认删除选中的${selectedData.length}条数据?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
const data = await this.$http.delete<any, any>('/api/v1/hitokoto/delete', {params:{_ids: selectedData}})
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
dataSelect(selection: HitokotoModel[]) {
|
||||
selectedData = selection.map(item => item._id)
|
||||
}
|
||||
created() {
|
||||
this.loadData()
|
||||
this.$http.get<never, any>('/api/v1/common/config/hitokoto_type').then(data => {
|
||||
this.typeList = data
|
||||
})
|
||||
}
|
||||
findTypeText(value: string): string | null {
|
||||
const type = this.typeList.find(item => item.value === value)
|
||||
return type ? type.label : null
|
||||
}
|
||||
}
|
||||
import http from '@/utils/http'
|
||||
|
||||
class HitokotoPage extends Page {
|
||||
content?: string
|
||||
@@ -145,4 +87,62 @@ class HitokotoPage extends Page {
|
||||
this.createdAt = undefined
|
||||
}
|
||||
}
|
||||
|
||||
const store = useStore()
|
||||
const { loading, modalLoading, total, search, setLoadData, loadDataBase, reset, pageChange, pageSizeChange, datetimeFormat } = useBaseList(new HitokotoPage())
|
||||
|
||||
const typeList = ref<{label: string, value: string}[]>([])
|
||||
const hitokotoData = ref<HitokotoModel[]>([])
|
||||
const formData = reactive<{[propName: string]: string | null}>({})
|
||||
const addModal = ref(false)
|
||||
const addForm = ref<InstanceType<typeof HitokotoAdd>>()
|
||||
|
||||
let selectedData: string[] = []
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
const data = await http.get<HitokotoPage, any>('/api/v1/hitokoto/list', {params: search})
|
||||
selectedData = []
|
||||
loading.value = false
|
||||
total.value = data.total
|
||||
hitokotoData.value = data.data
|
||||
}
|
||||
setLoadData(loadData)
|
||||
|
||||
async function save() {
|
||||
addForm.value?.hitokotoForm?.validate(async (valid: boolean) => {
|
||||
if (!valid) return
|
||||
modalLoading.value = true
|
||||
const data = await http.post<any, any>('/api/v1/hitokoto/save', formData)
|
||||
modalLoading.value = false
|
||||
addModal.value = false
|
||||
ElMessage.success(data.message)
|
||||
loadData()
|
||||
Object.keys(formData).forEach(key => delete formData[key])
|
||||
})
|
||||
}
|
||||
function deleteAll() {
|
||||
if (!selectedData.length) {
|
||||
ElMessage.warning('请选择要删除的数据')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`是否确认删除选中的${selectedData.length}条数据?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
const data = await http.delete<any, any>('/api/v1/hitokoto/delete', {params: {_ids: selectedData}})
|
||||
ElMessage.success(data.message)
|
||||
loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
function dataSelect(selection: HitokotoModel[]) {
|
||||
selectedData = selection.map(item => item._id)
|
||||
}
|
||||
function findTypeText(value: string): string | null {
|
||||
const type = typeList.value.find(item => item.value === value)
|
||||
return type ? type.label : null
|
||||
}
|
||||
|
||||
// created
|
||||
loadData()
|
||||
http.get<never, any>('/api/v1/common/config/hitokoto_type').then(data => {
|
||||
typeList.value = data
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -18,26 +18,25 @@
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component'
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import type { VForm } from '@/types'
|
||||
|
||||
@Options({
|
||||
name: 'SystemConfigAdd',
|
||||
props: {
|
||||
typeList: Array,
|
||||
formData: Object
|
||||
}
|
||||
})
|
||||
export default class HitokotoAdd extends Vue {
|
||||
typeList!: {label: string, value: string}[]
|
||||
formData!: {[propName:string]: string | null}
|
||||
ruleValidate = {
|
||||
hitokoto: [
|
||||
{ required: true, message: '请输入内容', trigger: 'blur' }
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: '请选择类型', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
defineProps<{
|
||||
typeList: {label: string, value: string}[]
|
||||
formData: {[propName: string]: string | null}
|
||||
}>()
|
||||
|
||||
const hitokotoForm = ref<VForm>()
|
||||
|
||||
const ruleValidate = {
|
||||
hitokoto: [
|
||||
{ required: true, message: '请输入内容', trigger: 'blur' }
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: '请选择类型', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
|
||||
defineExpose({ hitokotoForm })
|
||||
</script>
|
||||
+196
-200
@@ -79,8 +79,8 @@
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<el-pagination background
|
||||
:page-sizes="$store.state.pageSizeOpts"
|
||||
:layout="$store.state.pageLayout"
|
||||
:page-sizes="store.state.pageSizeOpts"
|
||||
:layout="store.state.pageLayout"
|
||||
:current-page="search.pageNum"
|
||||
:total="total"
|
||||
@size-change="pageSizeChange"
|
||||
@@ -118,7 +118,7 @@
|
||||
action="/api/v2/music/upload"
|
||||
name="file"
|
||||
accept=".mp3,.flac"
|
||||
:headers="{token: $store.state.loginInfo.token}"
|
||||
:headers="{token: store.state.loginInfo.token}"
|
||||
:on-success="uploadSuccess"
|
||||
:on-error="uploadError"
|
||||
:auto-upload="false"
|
||||
@@ -148,207 +148,17 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options } from 'vue-class-component'
|
||||
import BaseList from '@/model/baselist'
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useBaseList } from '@/model/baselist'
|
||||
import { MsgResult, Page } from '@/model/common.dto'
|
||||
import { ElUpload, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ElUploadInstance, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { MusicModel, MusicLibModel, MusicLyricModel, MusicPlayerItem } from '@/model/api/music'
|
||||
import APlayer from './aplayer/vue-aplayer.vue'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
import { VForm } from '@/types'
|
||||
|
||||
let selectedIds: string[] = []
|
||||
@Options({
|
||||
name: 'Music',
|
||||
components: { ElUpload, APlayer }
|
||||
})
|
||||
export default class Music extends BaseList<MusicPage> {
|
||||
search = new MusicPage()
|
||||
currentRow: MusicModel | null = null
|
||||
libIdSelected: string | null = null
|
||||
exts: string[] = []
|
||||
musicLibs: MusicLibModel[] = []
|
||||
musicData: MusicModel[] = []
|
||||
uploadModal: boolean = false
|
||||
modifyLyricModal: boolean = false
|
||||
lyricRuleValidate = {
|
||||
cloud_id: [
|
||||
{ required: true, message: '请输入网易云ID', trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
lyric: [
|
||||
{ required: true, message: '请输入歌词正文', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
prettyBytes = prettyBytes
|
||||
lyricFormData: MusicLyricModel = {}
|
||||
// 是否正在播放音乐
|
||||
musicPlaying: boolean = false
|
||||
musicList: MusicPlayerItem[] = []
|
||||
currentMusic?: MusicPlayerItem
|
||||
created() {
|
||||
this.$http.get<never, any>('/api/v1/music/listLibs').then(data => {
|
||||
this.musicLibs = data
|
||||
this.loadData()
|
||||
})
|
||||
this.$http.get<never, any>('/api/v1/music/listExts').then(data => {
|
||||
this.exts = data
|
||||
})
|
||||
}
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
const data = await this.$http.get<MusicPage, any>('/api/v1/music/list', {params: this.search})
|
||||
selectedIds = []
|
||||
this.loading = false
|
||||
this.total = data.total
|
||||
this.musicData = data.data
|
||||
}
|
||||
dataSelect(selection: MusicModel[]) {
|
||||
selectedIds = selection.map(item => item._id)
|
||||
}
|
||||
findMusicLib(value: string): string | null {
|
||||
const musicLib = this.musicLibs.find(item => item._id === value)
|
||||
return musicLib ? musicLib.name : null
|
||||
}
|
||||
// 根据当前搜索条件播放音乐
|
||||
async playMusic() {
|
||||
try {
|
||||
const data = await this.$http.get<any, any>('/api/v1/music/list/all', {params: selectedIds.length ? {ids: selectedIds} : this.search})
|
||||
this.musicList = data.map((item: MusicModel, index: number) => {
|
||||
const musicItem: MusicPlayerItem = {
|
||||
id: index,
|
||||
title: item.title || item.name,
|
||||
artist: item.artist,
|
||||
album: item.album,
|
||||
src: `/api/v2/common/music/load/${item._id}`,
|
||||
pic: `/api/v2/common/music/album/${item._id}`,
|
||||
}
|
||||
if(item.lyric_id) {
|
||||
musicItem.lrc = `${location.origin}/api/v2/common/music/lyric/${item.lyric_id}`
|
||||
}
|
||||
return musicItem
|
||||
})
|
||||
this.currentMusic = this.musicList[0]
|
||||
this.musicPlaying = true
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage.error('获取播放列表失败')
|
||||
}
|
||||
}
|
||||
updateLib(row: MusicModel) {
|
||||
this.currentRow = { ...row }
|
||||
row.isEditing = true
|
||||
}
|
||||
download(row: MusicModel) {
|
||||
const link = document.createElement('a')
|
||||
link.setAttribute('href', `/api/v2/common/music/load/${row._id}`)
|
||||
link.setAttribute('download', row.name)
|
||||
link.setAttribute('target', '_blank')
|
||||
link.click()
|
||||
}
|
||||
remove(row: MusicModel) {
|
||||
ElMessageBox.confirm(`是否确认删除 ${row.name} ?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
const data = await this.$http.delete<{params: {id: string}}, any>('/api/v2/music/delete', {params: {id: row._id}})
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
async updateLyric(row: MusicModel) {
|
||||
this.currentRow = { ...row }
|
||||
this.modifyLyricModal = true
|
||||
if (row.lyric_id) {
|
||||
const data = (await this.$http.get<any, any>('/api/v1/music/lyric/get', {params: {lyricId: row.lyric_id}}))
|
||||
data.cloud_id = data.cloud_id ? data.cloud_id.toString() : null
|
||||
this.lyricFormData = data
|
||||
} else {
|
||||
this.lyricFormData = {}
|
||||
}
|
||||
}
|
||||
async saveLyric() {
|
||||
(this.$refs.lyricForm as VForm).validate(async (valid: boolean) => {
|
||||
if (!valid) return
|
||||
this.modalLoading = true
|
||||
const data = await this.$http.post<MusicLyricModel, any>(`/api/v1/music/lyric/save?musicId=${this.currentRow ? this.currentRow._id : ''}`, this.lyricFormData)
|
||||
this.modalLoading = false
|
||||
this.modifyLyricModal = false
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
// 清空表单
|
||||
this.lyricFormData = {}
|
||||
})
|
||||
}
|
||||
async saveMusicLib(row: MusicModel) {
|
||||
if (!this.currentRow) return
|
||||
const data = await this.$http.post<{id: string, libId: string}, any>('/api/v2/music/updateLib', {id: this.currentRow._id, libId: this.currentRow.lib_id})
|
||||
ElMessage.success(data.message)
|
||||
row.lib_id = this.currentRow.lib_id
|
||||
row.isEditing = false
|
||||
}
|
||||
openUploadModal() {
|
||||
this.uploadModal = true
|
||||
this.libIdSelected = null
|
||||
}
|
||||
async uploadMusic() {
|
||||
if (!this.libIdSelected) {
|
||||
ElMessage.warning('请选择歌单')
|
||||
return
|
||||
}
|
||||
// 执行上传
|
||||
(this.$refs.musicUpload as typeof ElUpload).submit()
|
||||
}
|
||||
uploadSuccess(response: MsgResult) {
|
||||
if(response.code === 0) {
|
||||
ElMessage.success(response.message)
|
||||
this.loadData()
|
||||
} else {
|
||||
ElMessage.warning(response.message)
|
||||
}
|
||||
}
|
||||
uploadError(error: Error) {
|
||||
ElMessage.error(error.message)
|
||||
}
|
||||
uploadModalClosed() {
|
||||
(this.$refs.musicUpload as typeof ElUpload).clearFiles()
|
||||
}
|
||||
/**
|
||||
* 创建媒体信息
|
||||
*/
|
||||
musicPlay() {
|
||||
if(!('mediaSession' in window.navigator) || !this.currentMusic) return;
|
||||
const player = <any>this.$refs.player
|
||||
const currentId = this.currentMusic.id
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: this.currentMusic.title,
|
||||
artist: this.currentMusic.artist,
|
||||
album: this.currentMusic.album,
|
||||
artwork: [{src: location.origin + this.currentMusic.pic}]
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('play', () => { // 播放
|
||||
player.play()
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('pause', () => { // 暂停
|
||||
player.pause()
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('previoustrack', () => { // 上一首
|
||||
if (currentId === 0) { // 已经是第一首
|
||||
player.switch(this.musicList.length - 1)
|
||||
} else {
|
||||
player.switch(currentId - 1)
|
||||
}
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('nexttrack', () => { // 下一首
|
||||
if (currentId === this.musicList.length - 1) { // 已经是最后一首
|
||||
player.switch(0)
|
||||
} else {
|
||||
player.switch(currentId + 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
import type { VForm } from '@/types'
|
||||
import http from '@/utils/http'
|
||||
|
||||
class MusicPage extends Page {
|
||||
name?: string
|
||||
@@ -367,4 +177,190 @@ class MusicPage extends Page {
|
||||
this.lib_id = []
|
||||
}
|
||||
}
|
||||
|
||||
const store = useStore()
|
||||
const { loading, modalLoading, total, search, setLoadData, loadDataBase, reset, pageChange, pageSizeChange, datetimeFormat } = useBaseList(new MusicPage())
|
||||
|
||||
const currentRow = ref<MusicModel | null>(null)
|
||||
const libIdSelected = ref<string | null>(null)
|
||||
const exts = ref<string[]>([])
|
||||
const musicLibs = ref<MusicLibModel[]>([])
|
||||
const musicData = ref<MusicModel[]>([])
|
||||
const uploadModal = ref(false)
|
||||
const modifyLyricModal = ref(false)
|
||||
const lyricFormData = ref<MusicLyricModel>({})
|
||||
const musicPlaying = ref(false)
|
||||
const musicList = ref<MusicPlayerItem[]>([])
|
||||
const currentMusic = ref<MusicPlayerItem>()
|
||||
const lyricForm = ref<VForm>()
|
||||
const musicUpload = ref<ElUploadInstance>()
|
||||
const player = ref<any>()
|
||||
|
||||
const lyricRuleValidate = {
|
||||
cloud_id: [
|
||||
{ required: true, message: '请输入网易云ID', trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
lyric: [
|
||||
{ required: true, message: '请输入歌词正文', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
|
||||
let selectedIds: string[] = []
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
const data = await http.get<MusicPage, any>('/api/v1/music/list', {params: search})
|
||||
selectedIds = []
|
||||
loading.value = false
|
||||
total.value = data.total
|
||||
musicData.value = data.data
|
||||
}
|
||||
setLoadData(loadData)
|
||||
|
||||
function dataSelect(selection: MusicModel[]) {
|
||||
selectedIds = selection.map(item => item._id)
|
||||
}
|
||||
function findMusicLib(value: string): string | null {
|
||||
const musicLib = musicLibs.value.find(item => item._id === value)
|
||||
return musicLib ? musicLib.name : null
|
||||
}
|
||||
async function playMusic() {
|
||||
try {
|
||||
const data = await http.get<any, any>('/api/v1/music/list/all', {params: selectedIds.length ? {ids: selectedIds} : search})
|
||||
musicList.value = data.map((item: MusicModel, index: number) => {
|
||||
const musicItem: MusicPlayerItem = {
|
||||
id: index,
|
||||
title: item.title || item.name,
|
||||
artist: item.artist,
|
||||
album: item.album,
|
||||
src: `/api/v2/common/music/load/${item._id}`,
|
||||
pic: `/api/v2/common/music/album/${item._id}`,
|
||||
}
|
||||
if (item.lyric_id) {
|
||||
musicItem.lrc = `${location.origin}/api/v2/common/music/lyric/${item.lyric_id}`
|
||||
}
|
||||
return musicItem
|
||||
})
|
||||
currentMusic.value = musicList.value[0]
|
||||
musicPlaying.value = true
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
ElMessage.error('获取播放列表失败')
|
||||
}
|
||||
}
|
||||
function updateLib(row: MusicModel) {
|
||||
currentRow.value = { ...row }
|
||||
row.isEditing = true
|
||||
}
|
||||
function download(row: MusicModel) {
|
||||
const link = document.createElement('a')
|
||||
link.setAttribute('href', `/api/v2/common/music/load/${row._id}`)
|
||||
link.setAttribute('download', row.name)
|
||||
link.setAttribute('target', '_blank')
|
||||
link.click()
|
||||
}
|
||||
function remove(row: MusicModel) {
|
||||
ElMessageBox.confirm(`是否确认删除 ${row.name} ?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
const data = await http.delete<{params: {id: string}}, any>('/api/v2/music/delete', {params: {id: row._id}})
|
||||
ElMessage.success(data.message)
|
||||
loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
async function updateLyric(row: MusicModel) {
|
||||
currentRow.value = { ...row }
|
||||
modifyLyricModal.value = true
|
||||
if (row.lyric_id) {
|
||||
const data = (await http.get<any, any>('/api/v1/music/lyric/get', {params: {lyricId: row.lyric_id}}))
|
||||
data.cloud_id = data.cloud_id ? data.cloud_id.toString() : null
|
||||
lyricFormData.value = data
|
||||
} else {
|
||||
lyricFormData.value = {}
|
||||
}
|
||||
}
|
||||
async function saveLyric() {
|
||||
lyricForm.value?.validate(async (valid: boolean) => {
|
||||
if (!valid) return
|
||||
modalLoading.value = true
|
||||
const data = await http.post<MusicLyricModel, any>(`/api/v1/music/lyric/save?musicId=${currentRow.value ? currentRow.value._id : ''}`, lyricFormData.value)
|
||||
modalLoading.value = false
|
||||
modifyLyricModal.value = false
|
||||
ElMessage.success(data.message)
|
||||
loadData()
|
||||
lyricFormData.value = {}
|
||||
})
|
||||
}
|
||||
async function saveMusicLib(row: MusicModel) {
|
||||
if (!currentRow.value) return
|
||||
const data = await http.post<{id: string, libId: string}, any>('/api/v2/music/updateLib', {id: currentRow.value._id, libId: currentRow.value.lib_id})
|
||||
ElMessage.success(data.message)
|
||||
row.lib_id = currentRow.value.lib_id
|
||||
row.isEditing = false
|
||||
}
|
||||
function openUploadModal() {
|
||||
uploadModal.value = true
|
||||
libIdSelected.value = null
|
||||
}
|
||||
async function uploadMusic() {
|
||||
if (!libIdSelected.value) {
|
||||
ElMessage.warning('请选择歌单')
|
||||
return
|
||||
}
|
||||
musicUpload.value?.submit()
|
||||
}
|
||||
function uploadSuccess(response: MsgResult) {
|
||||
if (response.code === 0) {
|
||||
ElMessage.success(response.message)
|
||||
loadData()
|
||||
} else {
|
||||
ElMessage.warning(response.message)
|
||||
}
|
||||
}
|
||||
function uploadError(error: Error) {
|
||||
ElMessage.error(error.message)
|
||||
}
|
||||
function uploadModalClosed() {
|
||||
musicUpload.value?.clearFiles()
|
||||
}
|
||||
function musicPlay() {
|
||||
if (!('mediaSession' in window.navigator) || !currentMusic.value) return
|
||||
const currentId = currentMusic.value.id
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: currentMusic.value.title,
|
||||
artist: currentMusic.value.artist,
|
||||
album: currentMusic.value.album,
|
||||
artwork: [{src: location.origin + currentMusic.value.pic}]
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('play', () => {
|
||||
player.value.play()
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('pause', () => {
|
||||
player.value.pause()
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
||||
if (currentId === 0) {
|
||||
player.value.switch(musicList.value.length - 1)
|
||||
} else {
|
||||
player.value.switch(currentId - 1)
|
||||
}
|
||||
})
|
||||
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
||||
if (currentId === musicList.value.length - 1) {
|
||||
player.value.switch(0)
|
||||
} else {
|
||||
player.value.switch(currentId + 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// created
|
||||
http.get<never, any>('/api/v1/music/listLibs').then(data => {
|
||||
musicLibs.value = data
|
||||
loadData()
|
||||
})
|
||||
http.get<never, any>('/api/v1/music/listExts').then(data => {
|
||||
exts.value = data
|
||||
})
|
||||
</script>
|
||||
+77
-75
@@ -34,7 +34,7 @@
|
||||
action="/api/v2/photoWall/upload"
|
||||
accept="image/jpeg,image/png"
|
||||
name="image"
|
||||
:headers="{token: $store.state.loginInfo.token}"
|
||||
:headers="{token: store.state.loginInfo.token}"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="uploadSuccess"
|
||||
:on-error="uploadError"
|
||||
@@ -67,8 +67,8 @@
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<el-pagination background
|
||||
:page-sizes="$store.state.pageSizeOpts"
|
||||
:layout="$store.state.pageLayout"
|
||||
:page-sizes="store.state.pageSizeOpts"
|
||||
:layout="store.state.pageLayout"
|
||||
:current-page="search.pageNum"
|
||||
:total="total"
|
||||
@size-change="pageSizeChange"
|
||||
@@ -77,81 +77,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Options } from 'vue-class-component'
|
||||
<script setup lang="ts">
|
||||
import { ref, h } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { MsgResult, Page } from '@/model/common.dto'
|
||||
import BaseList from '@/model/baselist'
|
||||
import { useBaseList } from '@/model/baselist'
|
||||
import PhotoWallModel from '@/model/api/photowall'
|
||||
import { h } from 'vue'
|
||||
|
||||
let selectedData: string[] = []
|
||||
@Options({
|
||||
name: 'PhotoWall'
|
||||
})
|
||||
export default class PhotoWall extends BaseList<PhotoWallPage> {
|
||||
search = new PhotoWallPage()
|
||||
allowUploadExt = ['jpg','jpeg','png']
|
||||
photowallData = []
|
||||
isUploading: boolean = false
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
const data = await this.$http.get<PhotoWallPage, any>('/api/v1/photowall/list', {params:this.search})
|
||||
selectedData = []
|
||||
this.loading = false
|
||||
this.total = data.total
|
||||
this.photowallData = data.data
|
||||
}
|
||||
deleteAll() {
|
||||
if(!selectedData || !selectedData.length) {
|
||||
ElMessage.warning('请选择要删除的数据')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`是否确认删除选中的${selectedData.length}条数据?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
await this.$http.delete('/api/v1/photowall/delete', {params:{_ids: selectedData}})
|
||||
ElMessage.success('删除成功')
|
||||
this.loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
dataSelect(selection: PhotoWallModel[]) {
|
||||
selectedData = selection.map(item => item._id)
|
||||
}
|
||||
beforeUpload(file: File): boolean {
|
||||
if(file.size > 10 << 20) {
|
||||
ElMessage.warning('文件大小超过10MB')
|
||||
return false
|
||||
}
|
||||
this.isUploading = true
|
||||
return true
|
||||
}
|
||||
uploadSuccess(response: MsgResult) {
|
||||
if(response.code === 0) {
|
||||
ElMessage.success(response.message)
|
||||
this.loadData()
|
||||
} else {
|
||||
ElMessage.warning(response.message)
|
||||
}
|
||||
this.isUploading = false
|
||||
}
|
||||
uploadError(error: Error) {
|
||||
this.isUploading = false
|
||||
ElMessage.error(error.message)
|
||||
}
|
||||
async preview(row: PhotoWallModel) {
|
||||
const previewHeight = Math.floor(row.height * (500 / row.width))
|
||||
const pictureCdn = await this.$http.get('/api/v1/common/config/picture_cdn')
|
||||
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() {
|
||||
this.loadData()
|
||||
}
|
||||
}
|
||||
import http from '@/utils/http'
|
||||
|
||||
class PhotoWallPage extends Page {
|
||||
name?: string
|
||||
@@ -168,4 +101,73 @@ class PhotoWallPage extends Page {
|
||||
this.heightMax = 0
|
||||
}
|
||||
}
|
||||
|
||||
const store = useStore()
|
||||
const { loading, total, search, setLoadData, loadDataBase, reset, pageChange, pageSizeChange } = useBaseList(new PhotoWallPage())
|
||||
|
||||
const allowUploadExt = ['jpg', 'jpeg', 'png']
|
||||
const photowallData = ref([])
|
||||
const isUploading = ref(false)
|
||||
|
||||
let selectedData: string[] = []
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
const data = await http.get<PhotoWallPage, any>('/api/v1/photowall/list', {params: search})
|
||||
selectedData = []
|
||||
loading.value = false
|
||||
total.value = data.total
|
||||
photowallData.value = data.data
|
||||
}
|
||||
setLoadData(loadData)
|
||||
|
||||
function deleteAll() {
|
||||
if (!selectedData || !selectedData.length) {
|
||||
ElMessage.warning('请选择要删除的数据')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`是否确认删除选中的${selectedData.length}条数据?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
await http.delete('/api/v1/photowall/delete', {params: {_ids: selectedData}})
|
||||
ElMessage.success('删除成功')
|
||||
loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
function dataSelect(selection: PhotoWallModel[]) {
|
||||
selectedData = selection.map(item => item._id)
|
||||
}
|
||||
function beforeUpload(file: File): boolean {
|
||||
if (file.size > 10 << 20) {
|
||||
ElMessage.warning('文件大小超过10MB')
|
||||
return false
|
||||
}
|
||||
isUploading.value = true
|
||||
return true
|
||||
}
|
||||
function uploadSuccess(response: MsgResult) {
|
||||
if (response.code === 0) {
|
||||
ElMessage.success(response.message)
|
||||
loadData()
|
||||
} else {
|
||||
ElMessage.warning(response.message)
|
||||
}
|
||||
isUploading.value = false
|
||||
}
|
||||
function uploadError(error: Error) {
|
||||
isUploading.value = false
|
||||
ElMessage.error(error.message)
|
||||
}
|
||||
async function preview(row: PhotoWallModel) {
|
||||
const previewHeight = Math.floor(row.height * (500 / row.width))
|
||||
const pictureCdn = await http.get('/api/v1/common/config/picture_cdn')
|
||||
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
|
||||
loadData()
|
||||
</script>
|
||||
@@ -9,7 +9,7 @@
|
||||
action="/api/source-image/upload"
|
||||
accept="image/jpeg,image/png,image/svg+xml,image/x-icon"
|
||||
name="image"
|
||||
:headers="{token: $store.state.loginInfo.token}"
|
||||
:headers="{token: store.state.loginInfo.token}"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="uploadSuccess"
|
||||
:on-error="uploadError"
|
||||
@@ -50,8 +50,8 @@
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<el-pagination background
|
||||
:page-sizes="$store.state.pageSizeOpts"
|
||||
:layout="$store.state.pageLayout"
|
||||
:page-sizes="store.state.pageSizeOpts"
|
||||
:layout="store.state.pageLayout"
|
||||
:current-page="search.pageNum"
|
||||
:total="total"
|
||||
@size-change="pageSizeChange"
|
||||
@@ -74,105 +74,107 @@
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Options } from 'vue-class-component'
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, h } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
import { MsgResult, Page } from '@/model/common.dto'
|
||||
import BaseList from '@/model/baselist'
|
||||
import { useBaseList } from '@/model/baselist'
|
||||
import { SourceImageModel } from '@/model/api/source-image'
|
||||
import { h } from 'vue'
|
||||
import http from '@/utils/http'
|
||||
|
||||
const store = useStore()
|
||||
const { loading, total, search, setLoadData, loadDataBase, reset, pageChange, pageSizeChange, datetimeFormat } = useBaseList(new Page())
|
||||
|
||||
const allowUploadExt = ['jpg', 'jpeg', 'png', 'svg', 'ico']
|
||||
const sourceImageData = ref<SourceImageModel[]>([])
|
||||
const curModifyLabels = ref<string[]>([])
|
||||
const labelList = ref<string[]>([])
|
||||
const curId = ref<string | null>(null)
|
||||
const modifyModal = ref(false)
|
||||
const isUploading = ref(false)
|
||||
|
||||
function renderFunc(h: Function, option: any) {
|
||||
return h('span', null, option.label)
|
||||
}
|
||||
const labels = computed(() => {
|
||||
return labelList.value.map(item => {
|
||||
return { key: item, label: item }
|
||||
})
|
||||
})
|
||||
|
||||
let selectedData: string[] = []
|
||||
@Options({
|
||||
name: 'SourceImage',
|
||||
})
|
||||
export default class SourceImage extends BaseList<Page> {
|
||||
prettyBytes = prettyBytes
|
||||
search = new Page()
|
||||
allowUploadExt = ['jpg','jpeg','png','svg','ico']
|
||||
sourceImageData: SourceImageModel[] = []
|
||||
curModifyLabels: string[] = []
|
||||
labelList: string[] = []
|
||||
curId: string | null = null
|
||||
modifyModal: boolean = false
|
||||
isUploading: boolean = false
|
||||
renderFunc(h: Function, option: any) {
|
||||
return h('span', null, option.label)
|
||||
}
|
||||
get labels() {
|
||||
return this.labelList.map(item => {
|
||||
return { key: item, label: item }
|
||||
})
|
||||
}
|
||||
async loadData(): Promise<void> {
|
||||
this.loading = true
|
||||
const data = await this.$http.get<Page, any>('/api/v1/source-image/list', {params:this.search})
|
||||
selectedData = []
|
||||
this.loading = false
|
||||
this.total = data.total
|
||||
this.sourceImageData = data.data
|
||||
}
|
||||
deleteAll(): void {
|
||||
if(!selectedData.length) {
|
||||
ElMessage.warning('请选择要删除的数据')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`是否确认删除选中的${selectedData.length}条数据?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
await this.$http.delete('/api/v1/source-image/delete', {params:{_ids: selectedData}})
|
||||
ElMessage.success('删除成功')
|
||||
this.loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
dataSelect(selection: SourceImageModel[]): void {
|
||||
selectedData = selection.map(item => item._id)
|
||||
}
|
||||
beforeUpload(file: File): boolean {
|
||||
if(file.size > 10 << 20) {
|
||||
ElMessage.warning('文件大小超过10MB')
|
||||
return false
|
||||
}
|
||||
this.isUploading = true
|
||||
return true
|
||||
}
|
||||
uploadSuccess(response: MsgResult): void {
|
||||
if(response.status) {
|
||||
ElMessage.success(response.message)
|
||||
this.loadData()
|
||||
} else {
|
||||
ElMessage.warning(response.message)
|
||||
}
|
||||
this.isUploading = false
|
||||
}
|
||||
uploadError(error: Error): void {
|
||||
this.isUploading = false
|
||||
ElMessage.error(error.message)
|
||||
}
|
||||
preview(row: SourceImageModel): void {
|
||||
ElMessageBox({
|
||||
title: '图片预览',
|
||||
message: h('img', { style: `width:500px`, src: `/api/v1/common/randomBg?id=${row._id}` }, ''),
|
||||
showCancelButton: false,
|
||||
confirmButtonText: '关闭',
|
||||
customStyle: {width: '530px', maxWidth: 'unset'}
|
||||
}).catch(() => {})
|
||||
}
|
||||
modifyTags(item: SourceImageModel): void {
|
||||
this.curModifyLabels.length = 0
|
||||
if(item.label) {
|
||||
this.curModifyLabels.push(...item.label)
|
||||
}
|
||||
this.curId = item._id
|
||||
this.modifyModal = true
|
||||
}
|
||||
async tarnsferChange(newTargetKeys: string[], direction: 'right' | 'left', moveKeys: string[]) {
|
||||
await this.$http.post('/api/v1/source-image/updateLabel', {id: this.curId, labels: newTargetKeys})
|
||||
}
|
||||
created() {
|
||||
this.$http.get<never, any>('/api/v1/common/config/image_label').then(data => {
|
||||
this.labelList.push(...data)
|
||||
this.loadData()
|
||||
})
|
||||
}
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
loading.value = true
|
||||
const data = await http.get<Page, any>('/api/v1/source-image/list', {params: search})
|
||||
selectedData = []
|
||||
loading.value = false
|
||||
total.value = data.total
|
||||
sourceImageData.value = data.data
|
||||
}
|
||||
setLoadData(loadData)
|
||||
|
||||
function deleteAll(): void {
|
||||
if (!selectedData.length) {
|
||||
ElMessage.warning('请选择要删除的数据')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`是否确认删除选中的${selectedData.length}条数据?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
await http.delete('/api/v1/source-image/delete', {params: {_ids: selectedData}})
|
||||
ElMessage.success('删除成功')
|
||||
loadData()
|
||||
}).catch(() => {})
|
||||
}
|
||||
function dataSelect(selection: SourceImageModel[]): void {
|
||||
selectedData = selection.map(item => item._id)
|
||||
}
|
||||
function beforeUpload(file: File): boolean {
|
||||
if (file.size > 10 << 20) {
|
||||
ElMessage.warning('文件大小超过10MB')
|
||||
return false
|
||||
}
|
||||
isUploading.value = true
|
||||
return true
|
||||
}
|
||||
function uploadSuccess(response: MsgResult): void {
|
||||
if (response.status) {
|
||||
ElMessage.success(response.message)
|
||||
loadData()
|
||||
} else {
|
||||
ElMessage.warning(response.message)
|
||||
}
|
||||
isUploading.value = false
|
||||
}
|
||||
function uploadError(error: Error): void {
|
||||
isUploading.value = false
|
||||
ElMessage.error(error.message)
|
||||
}
|
||||
function preview(row: SourceImageModel): void {
|
||||
ElMessageBox({
|
||||
title: '图片预览',
|
||||
message: h('img', { style: `width:500px`, src: `/api/v1/common/randomBg?id=${row._id}` }, ''),
|
||||
showCancelButton: false,
|
||||
confirmButtonText: '关闭',
|
||||
customStyle: {width: '530px', maxWidth: 'unset'}
|
||||
}).catch(() => {})
|
||||
}
|
||||
function modifyTags(item: SourceImageModel): void {
|
||||
curModifyLabels.value.length = 0
|
||||
if (item.label) {
|
||||
curModifyLabels.value.push(...item.label)
|
||||
}
|
||||
curId.value = item._id
|
||||
modifyModal.value = true
|
||||
}
|
||||
async function tarnsferChange(newTargetKeys: string[], direction: 'right' | 'left', moveKeys: string[]) {
|
||||
await http.post('/api/v1/source-image/updateLabel', {id: curId.value, labels: newTargetKeys})
|
||||
}
|
||||
|
||||
// created
|
||||
http.get<never, any>('/api/v1/common/config/image_label').then(data => {
|
||||
labelList.value.push(...data)
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user