歌曲库
This commit is contained in:
parent
0a7980bded
commit
8253a1b62b
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
<title>博客API管理后台</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
"axios": "^0.22.0",
|
||||
"element-plus": "^1.1.0-beta.19",
|
||||
"moment": "^2.29.1",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"unplugin-element-plus": "^0.1.0",
|
||||
"vue": "^3.2.16",
|
||||
"vue-axios": "^3.3.7",
|
||||
|
||||
@ -18,7 +18,6 @@ export default [
|
||||
{ title: '一言', path: '/api/hitokoto' },
|
||||
{ title: '照片墙', path: '/api/photoWall' },
|
||||
{ title: '图片资源库', path: '/api/sourceImage' },
|
||||
{ title: '中国行政区划', path: '/api/chinaProvince' },
|
||||
{ title: '歌曲库', path: '/api/music' }
|
||||
]
|
||||
},{
|
||||
|
||||
@ -5,12 +5,19 @@ import Home from './views/Home.vue'
|
||||
import Welcome from './views/Welcome.vue'
|
||||
import SystemUser from './views/system/SystemUser.vue'
|
||||
import SystemRole from './views/system/SystemRole.vue'
|
||||
import SystemConfig from './views/system/SystemConfig.vue'
|
||||
|
||||
import Music from './views/api/Music.vue'
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{ path: '/login', name: 'Login', component: Login },
|
||||
{ path: '/', name: 'Home', component: Home, children: [
|
||||
{ path: '/', name: 'Welcome', component: Welcome },
|
||||
{ path: '/system/user', name: 'SystemUser', component: SystemUser },
|
||||
{ path: '/system/role', name: 'SystemRole', component: SystemRole },
|
||||
{ path: '/system/config', name: 'SystemConfig', component: SystemConfig },
|
||||
|
||||
{ path: '/api/music', name: 'Music', component: Music },
|
||||
]}
|
||||
]
|
||||
|
||||
|
||||
@ -67,7 +67,4 @@ html,body,#app,.layout {
|
||||
.main-view {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
.main-view .search-row:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@ -11,7 +11,8 @@ class Store {
|
||||
token: null
|
||||
},
|
||||
breadcrumb: [],
|
||||
pageSizeOpts: [10, 20, 50, 100]
|
||||
pageSizeOpts: [10, 20, 50, 100],
|
||||
pageLayout: 'sizes, prev, pager, next, total, ->, jumper'
|
||||
}
|
||||
mutations = {
|
||||
/**
|
||||
|
||||
@ -12,4 +12,5 @@ export interface StateType {
|
||||
}
|
||||
breadcrumb: string[] // 面包屑导航文字
|
||||
pageSizeOpts: number[] // 分页大小可选列表
|
||||
pageLayout: string // 分页工具栏
|
||||
}
|
||||
314
src/views/api/Music.vue
Normal file
314
src/views/api/Music.vue
Normal file
@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form inline :model="search" @submit.prevent>
|
||||
<el-form-item label="名称:">
|
||||
<el-input size="small" v-model="search.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属歌单:">
|
||||
<el-select size="small" v-model="search.lib_id" multiple :max-tag-count="3">
|
||||
<el-option v-for="musicLib in musicLibs" :key="musicLib._id" :value="musicLib._id" :label="musicLib.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="文件类型:">
|
||||
<el-select size="small" v-model="search.ext" multiple :max-tag-count="3">
|
||||
<el-option v-for="ext in exts" :key="ext" :value="ext" :label="ext" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题:">
|
||||
<el-input size="small" v-model="search.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="唱片集:">
|
||||
<el-input size="small" v-model="search.album" />
|
||||
</el-form-item>
|
||||
<el-form-item label="艺术家:">
|
||||
<el-input size="small" v-model="search.artist" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="btn-container">
|
||||
<el-button type="success" plain size="small" icon="el-icon-caret-right" >播放</el-button>
|
||||
<div class="search-btn">
|
||||
<el-button type="primary" @click="loadDataBase(true)" size="small" icon="el-icon-search">搜索</el-button>
|
||||
<el-button @click="reset" size="small" icon="el-icon-refresh-right">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="musicData" v-loading="loading" stripe height="520" @selection-change="dataSelect">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" label="名称" show-overflow-tooltip role=""/>
|
||||
<el-table-column prop="ext" label="类型" width="100" />
|
||||
<el-table-column prop="size" label="文件大小" width="150">
|
||||
<template #default="scope">
|
||||
{{ prettyBytes(scope.row.size) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="标题" show-overflow-tooltip />
|
||||
<el-table-column prop="album" label="唱片集" />
|
||||
<el-table-column prop="artist" label="艺术家" />
|
||||
<el-table-column prop="lib_id" label="所属歌单" >
|
||||
<template #default="scope">
|
||||
{{ findMusicLib(scope.row.lib_id) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="lyric_id" label="歌词" width="120" >
|
||||
<template #default="scope">
|
||||
<i :class="scope.row.lyric_id ? 'el-icon-check' : 'el-icon-close'" ></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="230" >
|
||||
<template #default="scope">
|
||||
<el-button size="mini" plain @click="update(scope.row)">修改</el-button>
|
||||
<el-button size="mini" plain @click="updateLyric(scope.row)">歌词</el-button>
|
||||
<el-button type="danger" size="mini" plain @click="remove(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<el-pagination background
|
||||
:page-sizes="$store.state.pageSizeOpts"
|
||||
:layout="$store.state.pageLayout"
|
||||
:total="total"
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageChange">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<el-dialog v-model="modifyLyricModal" title="编辑歌词" :width="600" >
|
||||
<el-form ref="lyricForm" :model="lyricFormData" :rules="lyricRuleValidate" :label-width="120">
|
||||
<el-form-item label="网易云ID" prop="cloud_id">
|
||||
<el-input v-model="lyricFormData.cloud_id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="lyricFormData.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="歌词" prop="lyric">
|
||||
<el-input v-model="lyricFormData.lyric" type="textarea" :rows="4"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="modifyLyricModal = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveLyric" :loading="modalLoading">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="modifyModal" title="修改所属歌单" :width="470" >
|
||||
<el-radio-group v-model="currentRow.lib_id">
|
||||
<el-radio v-for="item in musicLibs" :key="item._id" :label="item._id" border>{{item.name}}</el-radio>
|
||||
</el-radio-group>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="modifyModal = false">取消</el-button>
|
||||
<el-button type="primary" @click="updateMusicLib" >确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!--
|
||||
<Drawer title="播放音乐" v-model="musicPlaying" width="720" :mask-closable="false" >
|
||||
<template v-if="musicPlaying">
|
||||
<a-player ref="player" :audio="musicList" :lrcType="3" @playing="musicPlay"/>
|
||||
</template>
|
||||
</Drawer> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options } from 'vue-class-component'
|
||||
import BaseList from '../../model/baselist'
|
||||
import { Page } from '../../model/common.dto'
|
||||
import { AxiosResponse } from 'axios'
|
||||
import { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElPagination, ElDialog, ElSelect, ElOption, ElRadioGroup, ElRadio, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
|
||||
import { MusicModel, MusicLibModel, MusicLyricModel, MusicPlayerItem } from '../../model/api/music'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
// import APlayer, {APlayer as APlayerComponent} from '@moefe/vue-aplayer'
|
||||
|
||||
// Vue.use(APlayer, {
|
||||
// defaultCover: `${Vue.prototype.$http.defaults.baseURL}/api/common/randomBg?id=5ec7770b60990123b7340233`, // 播放器默认封面图片
|
||||
// productionTip: false, // 是否在控制台输出版本信息
|
||||
// })
|
||||
let selectedIds: string[] = []
|
||||
@Options({
|
||||
name: 'Music',
|
||||
components: { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElPagination, ElDialog, ElSelect, ElOption, ElRadioGroup, ElRadio }
|
||||
})
|
||||
export default class Music extends BaseList<MusicPage> {
|
||||
// @Ref('player') private readonly player!: APlayerComponent
|
||||
private readonly lyricForm: any = ref('lyricForm')
|
||||
search = new MusicPage()
|
||||
currentRow: MusicModel | null = null
|
||||
exts: string[] = []
|
||||
musicLibs: MusicLibModel[] = []
|
||||
musicData: MusicModel[] = []
|
||||
modifyModal: 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 = {}
|
||||
// 是否正在播放音乐
|
||||
private musicPlaying: boolean = false
|
||||
private musicList: MusicPlayerItem[] = []
|
||||
created() {
|
||||
this.$http.get('/api/music/listLibs').then(({data}) => {
|
||||
this.musicLibs = data
|
||||
this.loadData()
|
||||
})
|
||||
this.$http.get('/api/music/listExts').then(({data}) => {
|
||||
this.exts = data
|
||||
})
|
||||
}
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
const { data } = await this.$http.get<MusicPage, AxiosResponse<any>>('/api/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() {
|
||||
// // 显示加载进度条
|
||||
// this.$Loading.start()
|
||||
// try {
|
||||
// const { data } = await this.$http.get('/api/music/list/all', {params: selectedIds.length ? {ids: selectedIds} : this.search})
|
||||
// this.musicList = data.map((item: MusicModel, index: number) => {
|
||||
// return {
|
||||
// id: index + 1,
|
||||
// name: item.title || item.name,
|
||||
// artist: item.artist,
|
||||
// album: item.album,
|
||||
// url: `${this.$http.defaults.baseURL || ''}/api/common/music/get/${item._id}`,
|
||||
// cover: `${this.$http.defaults.baseURL || ''}/api/common/music/album/${item._id}`,
|
||||
// lrc: item.lyric_id ? `${this.$http.defaults.baseURL || ''}/api/common/music/lyric/${item.lyric_id}` : undefined
|
||||
// }
|
||||
// })
|
||||
// // 结束加载进度条
|
||||
// this.$Loading.finish()
|
||||
// // 删除原本可能存在的播放器配置(包含播放地址 时间进度等信息)
|
||||
// localStorage.removeItem('aplayer-setting')
|
||||
// this.musicPlaying = true
|
||||
// } catch (err) {
|
||||
// console.error(err)
|
||||
// ElMessage.error('获取播放列表失败')
|
||||
// this.$Loading.error()
|
||||
// }
|
||||
// }
|
||||
update(row: MusicModel) {
|
||||
this.currentRow = row
|
||||
this.modifyModal = true
|
||||
}
|
||||
remove(row: MusicModel) {
|
||||
ElMessageBox.confirm(`是否确认删除 ${row.name} ?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
const { data } = await this.$http.delete<{params: {id: string}}, AxiosResponse<any>>('/api/music/delete', {params: {id: row._id}})
|
||||
if(data.status) {
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
} else {
|
||||
ElMessage.warning(data.message)
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
async updateLyric(row: MusicModel) {
|
||||
this.currentRow = row
|
||||
this.modifyLyricModal = true
|
||||
if (row.lyric_id) {
|
||||
const { data } = (await this.$http.get<any, AxiosResponse<any>>('/api/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.lyricForm.validate(async (valid: boolean) => {
|
||||
if(!valid) return
|
||||
this.modalLoading = true
|
||||
const { data } = await this.$http.post<MusicLyricModel, AxiosResponse<any>>(`/api/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 updateMusicLib() {
|
||||
if (!this.currentRow) return
|
||||
const { data } = await this.$http.post<any, AxiosResponse<any>>('/api/music/updateLib', {id: this.currentRow._id, libId: this.currentRow.lib_id})
|
||||
ElMessage.success(data.message)
|
||||
this.modifyModal = false
|
||||
}
|
||||
/**
|
||||
* 创建媒体信息
|
||||
*/
|
||||
// musicPlay() {
|
||||
// if(!('mediaSession' in window.navigator)) return;
|
||||
// const currentMusic = this.player.currentMusic
|
||||
// const mediaSession: any = navigator.mediaSession
|
||||
// mediaSession.metadata = new MediaMetadata({
|
||||
// title: currentMusic.name,
|
||||
// artist: currentMusic.artist,
|
||||
// album: currentMusic.album,
|
||||
// artwork: [{src: currentMusic.cover}]
|
||||
// });
|
||||
// mediaSession.setActionHandler('play', () => { // 播放
|
||||
// this.player.play()
|
||||
// })
|
||||
// mediaSession.setActionHandler('pause', () => { // 暂停
|
||||
// this.player.pause()
|
||||
// })
|
||||
// const currentIndex = this.musicList.findIndex(item => item.id === currentMusic.id)
|
||||
// mediaSession.setActionHandler('previoustrack', () => { // 上一首
|
||||
// if (currentIndex === 0) { // 已经是第一首
|
||||
// this.player.switch(this.musicList.length - 1)
|
||||
// } else {
|
||||
// this.player.switch(currentIndex - 1)
|
||||
// }
|
||||
// })
|
||||
// mediaSession.setActionHandler('nexttrack', () => { // 下一首
|
||||
// if (currentIndex === this.musicList.length - 1) { // 已经是最后一首
|
||||
// this.player.switch(0)
|
||||
// } else {
|
||||
// this.player.switch(currentIndex + 1)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
class MusicPage extends Page {
|
||||
name?: string
|
||||
ext: string[] = []
|
||||
title?: string
|
||||
album?: string
|
||||
artist?: string
|
||||
lib_id?: string[] = []
|
||||
reset() {
|
||||
super.reset()
|
||||
this.name = undefined
|
||||
this.ext = []
|
||||
this.title = undefined
|
||||
this.album = undefined
|
||||
this.artist = undefined
|
||||
this.lib_id = []
|
||||
}
|
||||
}
|
||||
</script>
|
||||
151
src/views/system/SystemConfig.vue
Normal file
151
src/views/system/SystemConfig.vue
Normal file
@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form inline :model="search" @submit.prevent>
|
||||
<el-form-item label="配置项:">
|
||||
<el-input size="small" placeholder="名称/描述" v-model="search.name" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="btn-container">
|
||||
<el-button size="small" type="primary" @click="add">添加</el-button>
|
||||
<div class="search-btn">
|
||||
<el-button type="primary" @click="loadData" size="small" icon="el-icon-search">搜索</el-button>
|
||||
<el-button @click="reset" size="small" icon="el-icon-refresh-right">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="systemConfigData" v-loading="loading" stripe height="520">
|
||||
<el-table-column type="expand">
|
||||
<template #default="scope">
|
||||
<pre style="margin:0 10px;">{{ JSON.stringify(scope.row.value, null, ' ') }}</pre>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="配置项名称" />
|
||||
<el-table-column prop="description" label="配置项描述" />
|
||||
<el-table-column prop="is_public" label="是否公开" >
|
||||
<template #default="scope">
|
||||
{{ scope.row.is_public ? '是' : '否' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" >
|
||||
<template #default="scope">
|
||||
{{ datetimeFormat(scope.row.created_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="updated_at" label="更新时间" >
|
||||
<template #default="scope">
|
||||
{{ datetimeFormat(scope.row.updated_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" >
|
||||
<template #default="scope">
|
||||
<el-button size="mini" plain @click="update(scope.row)">修改</el-button>
|
||||
<el-button type="danger" size="mini" plain @click="remove(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-dialog v-model="addModal" :title="modalTitle" >
|
||||
<system-config-add ref="addForm" :formData="formData" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="addModal = false">取消</el-button>
|
||||
<el-button type="primary" @click="save" :loading="modalLoading">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component'
|
||||
import { AxiosResponse } from 'axios'
|
||||
import { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElDialog, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
import moment from 'moment'
|
||||
import SystemConfigAdd from './SystemConfigAdd.vue'
|
||||
import { SystemConfigModel } from '../../model/system/system-config'
|
||||
|
||||
@Options({
|
||||
name: 'SystemConfig',
|
||||
components: { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElDialog, SystemConfigAdd }
|
||||
})
|
||||
export default class SystemConfig extends Vue {
|
||||
private readonly addForm: any = ref('addForm')
|
||||
modalLoading: boolean = false
|
||||
loading: boolean = false
|
||||
search: {name?:string} = {}
|
||||
systemConfigData: SystemConfigModel[] = []
|
||||
addModal: boolean = false
|
||||
modalTitle: string = ''
|
||||
formData: SystemConfigModel = {
|
||||
name: '',
|
||||
value: '',
|
||||
description: '',
|
||||
is_public: false
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.search = {}
|
||||
this.loadData()
|
||||
}
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
this.systemConfigData = (await this.$http.get('/api/system/config/list', {params:this.search})).data
|
||||
this.loading = false
|
||||
}
|
||||
add() {
|
||||
// 清空表单
|
||||
this.formData = {
|
||||
name: '',
|
||||
value: '',
|
||||
description: '',
|
||||
is_public: false
|
||||
}
|
||||
this.modalTitle = '新增配置项'
|
||||
this.addModal = true
|
||||
}
|
||||
update(row: SystemConfigModel) {
|
||||
const formData = Object.assign({}, row)
|
||||
formData.value = JSON.stringify(formData.value, null, ' ')
|
||||
this.formData = formData
|
||||
this.modalTitle = '修改配置项'
|
||||
this.addModal = true
|
||||
}
|
||||
async save() {
|
||||
this.addForm.$refs.configForm.validate(async (valid: boolean) => {
|
||||
if(!valid) return
|
||||
this.modalLoading = true
|
||||
const { data } = await this.$http.post<SystemConfigModel, AxiosResponse<any>>('/api/system/config/save', this.formData)
|
||||
this.modalLoading = false
|
||||
this.addModal = false
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
})
|
||||
}
|
||||
remove(row: SystemConfigModel) {
|
||||
ElMessageBox.confirm(`是否确认删除 ${row.name} 配置项?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
const { data } = await this.$http.delete<{params: {id: string}}, AxiosResponse<any>>('/api/system/config/delete', {params: {id: row._id}})
|
||||
if(data.status) {
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
} else {
|
||||
ElMessage.warning(data.message)
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
created() {
|
||||
this.loadData()
|
||||
}
|
||||
clearValidate() {
|
||||
this.$nextTick(() => {
|
||||
this.addForm.$refs.configForm.clearValidate()
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 日期时间格式化
|
||||
* @param dateStr 日期时间
|
||||
*/
|
||||
datetimeFormat(dateStr: string) {
|
||||
return moment(dateStr).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
67
src/views/system/SystemConfigAdd.vue
Normal file
67
src/views/system/SystemConfigAdd.vue
Normal file
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="configForm" :model="formData" :rules="ruleValidate" :label-width="80">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="formData.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="值" prop="value">
|
||||
<el-input v-model="formData.value" type="textarea" placeholder="必须符合JSON字符串格式" :rows="4"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="formData.description" />
|
||||
</el-form-item>
|
||||
<el-form-item label="公开">
|
||||
<el-switch
|
||||
v-model="formData.is_public"
|
||||
active-text="是"
|
||||
inactive-text="否" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component'
|
||||
import { SystemConfigModel } from '../../model/system/system-config'
|
||||
import { ElForm, ElFormItem, ElInput, ElSwitch } from 'element-plus'
|
||||
import { AxiosResponse } from 'axios'
|
||||
|
||||
@Options({
|
||||
name: 'SystemConfigAdd',
|
||||
components: { ElForm, ElFormItem, ElInput, ElSwitch },
|
||||
props: {
|
||||
formData: Object
|
||||
}
|
||||
})
|
||||
export default class SystenConfigAdd extends Vue {
|
||||
formData!: SystemConfigModel
|
||||
get ruleValidate() {
|
||||
return {
|
||||
name: [
|
||||
{ required: true, message: '请输入配置项名称', trigger: 'blur' },
|
||||
{ validator: (rule: object, value: string, callback: Function) => {
|
||||
this.$http.get<any, AxiosResponse<any>>('/api/system/config/exists', {params: {name: value, id: this.formData._id}}).then(({data}) => {
|
||||
if(data.data.exists) {
|
||||
callback(new Error('配置项名称已存在'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}, trigger: 'blur'
|
||||
}
|
||||
],
|
||||
value: [
|
||||
{ required: true, message: '请输入配置项值', trigger: 'blur' },
|
||||
{ validator: (rule: object, value: string, callback: Function) => {
|
||||
try {
|
||||
JSON.parse(value)
|
||||
callback()
|
||||
} catch (e) {
|
||||
callback(new Error('值不符合JSON字符串格式'))
|
||||
}
|
||||
}, trigger: 'blur'
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -41,6 +41,7 @@
|
||||
<div class="page-container">
|
||||
<el-pagination background
|
||||
:page-sizes="$store.state.pageSizeOpts"
|
||||
:layout="$store.state.pageLayout"
|
||||
:total="total"
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageChange">
|
||||
@ -152,6 +153,7 @@ export default class SystemRole extends BaseList<SystemRolePage> {
|
||||
}
|
||||
this.modalTitle = '新增角色'
|
||||
this.addModal = true
|
||||
this.clearValidate()
|
||||
}
|
||||
addUri(fieldName: 'include_uri' | 'exclude_uri', uri: string | null) {
|
||||
if(!uri) return
|
||||
@ -176,6 +178,7 @@ export default class SystemRole extends BaseList<SystemRolePage> {
|
||||
this.formData.exclude_uri = row.exclude_uri
|
||||
this.modalTitle = '修改角色'
|
||||
this.addModal = true
|
||||
this.clearValidate()
|
||||
}
|
||||
remove(row: SystemRoleModel) {
|
||||
ElMessageBox.confirm(`是否确认删除 ${row.name} 角色?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
@ -199,6 +202,11 @@ export default class SystemRole extends BaseList<SystemRolePage> {
|
||||
this.loadData()
|
||||
})
|
||||
}
|
||||
clearValidate() {
|
||||
this.$nextTick(() => {
|
||||
this.roleForm.clearValidate()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class SystemRolePage extends Page {
|
||||
|
||||
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form inline :model="search" @submit.prevent>
|
||||
<el-form-item label="用户名/昵称:">
|
||||
<el-input size="small" v-model="search.username" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="btn-container">
|
||||
<el-button size="small" type="primary" @click="add">添加</el-button>
|
||||
<div class="search-btn">
|
||||
<el-button type="primary" @click="loadDataBase(true)" size="small" icon="el-icon-search">搜索</el-button>
|
||||
<el-button @click="reset" size="small" icon="el-icon-refresh-right">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="systemUserData" v-loading="loading" stripe height="520">
|
||||
<el-table-column prop="username" label="用户名" />
|
||||
<el-table-column prop="realname" label="昵称" />
|
||||
<el-table-column prop="created_at" label="创建时间" >
|
||||
<template #default="scope">
|
||||
{{ datetimeFormat(scope.row.created_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="updated_at" label="更新时间" >
|
||||
<template #default="scope">
|
||||
{{ datetimeFormat(scope.row.updated_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" >
|
||||
<template #default="scope">
|
||||
<el-button size="mini" plain @click="update(scope.row)">修改</el-button>
|
||||
<el-button type="danger" size="mini" plain @click="remove(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<el-pagination background
|
||||
:page-sizes="$store.state.pageSizeOpts"
|
||||
:layout="$store.state.pageLayout"
|
||||
:total="total"
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageChange">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<el-dialog v-model="addModal" :title="modalTitle" >
|
||||
<el-form ref="userForm" :model="formData" :rules="ruleValidate" :label-width="120">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="formData.username" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="formData.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" prop="realname">
|
||||
<el-input v-model="formData.realname" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色" prop="realname">
|
||||
<el-select v-model="formData.role_ids" multiple >
|
||||
<el-option v-for="role in roles" :key="role._id" :value="role._id" :label="role.name"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="addModal = false">取消</el-button>
|
||||
<el-button type="primary" @click="save" :loading="modalLoading">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Options } from 'vue-class-component'
|
||||
import BaseList from '../../model/baselist'
|
||||
import { Page } from '../../model/common.dto'
|
||||
import { SystemUserModel } from '../../model/system/system-user'
|
||||
import { SystemRoleModel } from '../../model/system/system-role'
|
||||
import { AxiosResponse } from 'axios'
|
||||
import { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElPagination, ElDialog, ElSelect, ElOption, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
@Options({
|
||||
name: 'SystemUser',
|
||||
components: { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElPagination, ElDialog, ElSelect, ElOption }
|
||||
})
|
||||
export default class SystemUser extends BaseList<SystemUserPage> {
|
||||
private readonly userForm: any = ref('userForm')
|
||||
get ruleValidate() {
|
||||
return {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ validator: async (rule: object, value: string, callback: Function) => {
|
||||
const { data } = await this.$http.get<any, AxiosResponse<any>>('/api/system/user/exists', {params: {username: value, id: this.formData._id}})
|
||||
if(data.data.exists) {
|
||||
callback(new Error('用户名已存在'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}, trigger: 'blur'
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 8, max: 16, message: '密码长度8~16位', trigger: 'blur' },
|
||||
{ pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![-=+_.,]+$)[\da-zA-Z-=+_.,]{8,16}$/, message: '密码由字母、数字、特殊字符中的任意两种组成', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
}
|
||||
search = new SystemUserPage()
|
||||
systemUserData: SystemUserModel[] = []
|
||||
roles: SystemRoleModel[] = []
|
||||
addModal: boolean = false
|
||||
modalTitle: string | null = null
|
||||
formData: SystemUserModel = {
|
||||
_id: null,
|
||||
username: null,
|
||||
password: null,
|
||||
realname: null,
|
||||
role_ids: []
|
||||
}
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
const { data } = await this.$http.get<{params: SystemUserPage}, AxiosResponse<any>>('/api/system/user/list', {params:this.search})
|
||||
this.loading = false
|
||||
this.total = data.total
|
||||
this.systemUserData = data.data
|
||||
}
|
||||
add() {
|
||||
// 清空表单
|
||||
this.formData = {
|
||||
_id: null,
|
||||
username: null,
|
||||
password: null,
|
||||
realname: null,
|
||||
role_ids: []
|
||||
}
|
||||
this.modalTitle = '新增用户'
|
||||
this.addModal = true
|
||||
this.clearValidate()
|
||||
}
|
||||
update(row: SystemUserModel) {
|
||||
this.formData._id = row._id
|
||||
this.formData.username = row.username
|
||||
this.formData.realname = row.realname
|
||||
this.formData.role_ids = row.role_ids
|
||||
this.modalTitle = '修改用户'
|
||||
this.addModal = true
|
||||
this.clearValidate()
|
||||
}
|
||||
async save() {
|
||||
console.log(this.userForm)
|
||||
this.userForm.validate(async (valid: boolean) => {
|
||||
if(!valid) return
|
||||
this.modalLoading = true
|
||||
const { data } = await this.$http.post<SystemUserModel, AxiosResponse<any>>('/api/system/user/save', this.formData)
|
||||
this.modalLoading = false
|
||||
this.addModal = false
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
})
|
||||
}
|
||||
remove(row: SystemUserModel) {
|
||||
ElMessageBox.confirm(`是否确认删除 ${row.username} 用户?`, '确认删除', {type: 'warning'}).then(async () => {
|
||||
const { data } = await this.$http.delete<{params: {id: string}}, AxiosResponse<any>>('/api/system/user/delete', {params: {id: row._id}})
|
||||
if(data.status) {
|
||||
ElMessage.success(data.message)
|
||||
this.loadData()
|
||||
} else {
|
||||
ElMessage.warning(data.message)
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
created() {
|
||||
this.loadData()
|
||||
this.$http.get('/api/system/role/listAll').then(({data}) => {
|
||||
this.roles = data
|
||||
})
|
||||
}
|
||||
clearValidate() {
|
||||
this.$nextTick(() => {
|
||||
this.userForm.clearValidate()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class SystemUserPage extends Page {
|
||||
username?: string
|
||||
reset() {
|
||||
super.reset()
|
||||
this.username = undefined
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -731,6 +731,11 @@ postcss@^8.1.10, postcss@^8.3.8:
|
||||
nanoid "^3.1.25"
|
||||
source-map-js "^0.6.2"
|
||||
|
||||
pretty-bytes@^5.6.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
||||
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
|
||||
|
||||
promise@^7.0.1:
|
||||
version "7.3.1"
|
||||
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user