照片墙&图片资源库

This commit is contained in:
灌糖包子 2021-10-04 23:19:40 +08:00
parent c751f26122
commit 8dd1916a3d
6 changed files with 363 additions and 7 deletions

View File

@ -11,6 +11,8 @@ import Statistics from './views/system/Statistics.vue'
import Music from './views/api/Music.vue'
import Hitokoto from './views/api/Hitokoto.vue'
import PhotoWall from './views/api/PhotoWall.vue'
import SourceImage from './views/api/SourceImage.vue'
import SqlReplace from './views/tool/SqlReplace.vue'
const routes: Array<RouteRecordRaw> = [
@ -25,7 +27,8 @@ const routes: Array<RouteRecordRaw> = [
{ path: '/api/music', name: 'Music', component: Music },
{ path: '/api/hitokoto', name: 'Hitokoto', component: Hitokoto },
{ path: '/api/photoWall', name: 'PhotoWall', component: PhotoWall },
{ path: '/api/sourceImage', name: 'SourceImage', component: SourceImage },
{ path: '/tool/sqlReplace', name: 'SqlReplace', component: SqlReplace },
]}
]

View File

@ -1,6 +1,6 @@
<template>
<div>
<el-form inline :model="search" @submit.prevent>
<el-form inline :model="search">
<el-form-item label="内容">
<el-input size="small" v-model="search.content" />
</el-form-item>

View File

@ -1,6 +1,6 @@
<template>
<div>
<el-form inline :model="search" @submit.prevent>
<el-form inline :model="search">
<el-form-item label="名称">
<el-input size="small" v-model="search.name" />
</el-form-item>

173
src/views/api/PhotoWall.vue Normal file
View File

@ -0,0 +1,173 @@
<template>
<div>
<el-alert type="info" show-icon :closable="false" >
<template #title>上传要求</template>
图片格式为{{allowUploadExt.join('、')}}文件大小不超过10MB
</el-alert>
<el-form inline :model="search">
<el-form-item label="文件名">
<el-input size="small" v-model="search.name" />
</el-form-item>
<el-form-item label="宽度" >
<el-input size="small" v-model="search.widthMin" type="number" style="vertical-align: middle;" >
<template #prepend></template>
</el-input>
</el-form-item>
<el-form-item>
<el-input size="small" v-model="search.widthMax" type="number" style="vertical-align: middle;" >
<template #prepend></template>
</el-input>
</el-form-item>
<el-form-item label="高度" >
<el-input size="small" v-model="search.heightMin" type="number" style="vertical-align: middle;" >
<template #prepend></template>
</el-input>
</el-form-item>
<el-form-item>
<el-input size="small" v-model="search.heightMax" type="number" style="vertical-align: middle;" >
<template #prepend></template>
</el-input>
</el-form-item>
</el-form>
<div class="btn-container">
<el-upload
action="/api/photowall/upload"
accept="image/jpeg,image/png"
name="image"
:headers="uploadHeaders"
:before-upload="beforeUpload"
:on-success="uploadSuccess"
:on-error="uploadError"
auto-upload
:show-file-list="false"
style="display: inline-block;margin-right: 10px;">
<el-button size="small" type="primary" icon="el-icon-upload" :loading="isUploading">上传图片</el-button>
</el-upload>
<el-button size="small" type="danger" @click="deleteAll">删除</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="photowallData" v-loading="loading" stripe height="520" @selection-change="dataSelect">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="文件名" />
<el-table-column prop="md5" label="md5" width="300" />
<el-table-column prop="thumbnail" label="缩略图" />
<el-table-column prop="width" label="宽度" width="70" />
<el-table-column prop="height" label="高度" width="70" />
<el-table-column label="操作" width="100" >
<template #default="scope">
<el-button size="mini" plain @click="preview(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>
</div>
</template>
<script lang="ts">
import { Options } from 'vue-class-component'
import { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElPagination, ElAlert, ElUpload, ElMessage, ElMessageBox } from 'element-plus'
import { AxiosResponse } from 'axios'
import { MsgResult, Page } from '../../model/common.dto'
import BaseList from '../../model/baselist'
import PhotoWallModel from '../../model/api/photowall'
import { h } from 'vue'
let selectedData: string[] = []
@Options({
name: 'PhotoWall',
components: { ElButton, ElForm, ElFormItem, ElInput, ElTable, ElTableColumn, ElPagination, ElAlert, ElUpload }
})
export default class PhotoWall extends BaseList<PhotoWallPage> {
uploadHeaders = {token: localStorage.getItem('login_token')}
search = new PhotoWallPage()
allowUploadExt = ['jpg','jpeg','png']
photowallData = []
isUploading: boolean = false
async loadData() {
this.loading = true
const { data } = await this.$http.get<PhotoWallPage, AxiosResponse<any>>('/api/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/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.status) {
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/common/config/picture_cdn')).data
ElMessageBox({
title: '图片预览',
message: h('img', { style: `width:500px;height:${previewHeight}px;`, src: `${pictureCdn}/${row.name}` }, ''),
showCancelButton: false,
confirmButtonText: '关闭',
customStyle: {width: '530px'}
}).catch(() => {})
}
created() {
this.loadData()
}
}
class PhotoWallPage extends Page {
name?: string
widthMin?: number = 0
widthMax?: number = 0
heightMin?: number = 0
heightMax?: number = 0
reset() {
super.reset()
this.name = undefined
this.widthMin = 0
this.widthMax = 0
this.heightMin = 0
this.heightMax = 0
}
}
</script>

View File

@ -0,0 +1,180 @@
<template>
<div>
<el-alert type="info" show-icon :closable="false" >
<template #title>上传要求</template>
图片格式为{{allowUploadExt.join('、')}}文件大小不超过10MB
</el-alert>
<div class="btn-container" style="margin-top:10px;">
<el-upload
action="/api/source-image/upload"
accept="image/jpeg,image/png,image/svg+xml,image/x-icon"
name="image"
:headers="uploadHeaders"
:before-upload="beforeUpload"
:on-success="uploadSuccess"
:on-error="uploadError"
auto-upload
:show-file-list="false"
style="display: inline-block;margin-right: 10px;">
<el-button size="small" type="primary" icon="el-icon-upload" :loading="isUploading">上传图片</el-button>
</el-upload>
<el-button size="small" type="danger" @click="deleteAll">删除</el-button>
</div>
<div class="table-container">
<el-table :data="sourceImageData" v-loading="loading" stripe height="520" @selection-change="dataSelect">
<el-table-column type="selection" width="55" />
<el-table-column prop="hash" label="md5" width="300" />
<el-table-column prop="size" label="文件大小" >
<template #default="scope">
{{ prettyBytes(scope.row.size) }}
</template>
</el-table-column>
<el-table-column prop="mime" label="MIME" />
<el-table-column prop="label" label="标签" >
<template #default="scope">
<el-tag v-for="label in scope.row.label" :key="label" effect="plain">{{label}}</el-tag>
</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 label="操作" >
<template #default="scope">
<el-button size="mini" type="primary" plain @click="modifyTags(scope.row)">修改标签</el-button>
<el-button size="mini" plain @click="preview(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="modifyModal" title="修改标签" width="630px" @closed="loadData">
<el-transfer
v-model="curModifyLabels"
filterable
:render-content="renderFunc"
:titles="['可选标签', '已选标签']"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}',
}"
@change="tarnsferChange"
:data="labels"
></el-transfer>
</el-dialog>
</div>
</template>
<script lang="ts">
import { Options } from 'vue-class-component'
import { ElButton, ElTable, ElTableColumn, ElPagination, ElAlert, ElUpload, ElTag, ElTransfer, ElDialog, ElMessage, ElMessageBox } from 'element-plus'
import prettyBytes from 'pretty-bytes'
import { MsgResult, Page } from '../../model/common.dto'
import BaseList from '../../model/baselist'
import { SourceImageModel, ImageLabel } from '../../model/api/source-image'
import { AxiosResponse } from 'axios'
import { h } from 'vue'
let selectedData: string[] = []
@Options({
name: 'SourceImage',
components: { ElButton, ElTable, ElTableColumn, ElPagination, ElAlert, ElUpload, ElTag, ElTransfer, ElDialog }
})
export default class SourceImage extends BaseList<Page> {
prettyBytes = prettyBytes
uploadHeaders = {token: localStorage.getItem('login_token')}
search = new Page()
allowUploadExt = ['jpg','jpeg','png','svg','ico']
sourceImageData: SourceImageModel[] = []
curModifyLabels: string[] = []
labelList: ImageLabel[] = []
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.name, label: item.name }
})
}
async loadData(): Promise<void> {
this.loading = true
const { data } = await this.$http.get<Page, AxiosResponse<any>>('/api/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/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/common/randomBg?id=${row._id}` }, ''),
showCancelButton: false,
confirmButtonText: '关闭',
customStyle: {width: '530px'}
}).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/source-image/updateLabel', {id: this.curId, labels: newTargetKeys})
}
created() {
this.$http.get<never, AxiosResponse<any>>('/api/common/config/image_label').then(({data}) => {
this.labelList.push(...data)
this.loadData()
})
}
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<div>
<div class="search-panel">
<el-form inline :model="search" @submit.prevent>
<el-form inline :model="search">
<el-form-item label="标题">
<el-input size="small" v-model="search.title" />
</el-form-item>
@ -176,7 +176,7 @@ export default class Article extends BaseList<ArticlePage> {
dataSelect(selection: ArticleModel[]) {
selectedData = selection.map(item => item._id)
}
beforeUpload(file: File) {
beforeUpload(file: File): boolean {
this.isUploading = true
return true
}
@ -188,9 +188,9 @@ export default class Article extends BaseList<ArticlePage> {
}
this.isUploading = false
}
uploadError() {
uploadError(error: Error) {
this.isUploading = false
ElMessage.error('上传失败')
ElMessage.error(error.message)
}
readonly treeProps = {
label: 'name',