refactor(system): 重构系统模块代码

- 重命名接口和服务类,统一命名规范
- 优化代码结构,提高可维护性
- 异步处理用户JWT和在线用户相关操作
- 更新MyBatis Plus相关类名和方法名
This commit is contained in:
2025-06-23 12:02:00 +08:00
parent 169df00551
commit 0783a140b3
65 changed files with 241 additions and 371 deletions
@@ -0,0 +1,16 @@
<template>
<elFrame :src="druidSqlApi" />
</template>
<script>
import { mapGetters } from 'vuex'
import elFrame from '@/components/Iframe/index'
export default {
name: 'DruidSqlConsole',
components: { elFrame },
computed: {
...mapGetters([
'druidSqlApi'
])
}
}
</script>
@@ -0,0 +1,91 @@
<template>
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
<el-form-item label="发件人邮箱" prop="fromUser">
<el-input v-model="form.fromUser" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">Sender mailbox</span>
</el-form-item>
<el-form-item label="发件用户名" prop="user">
<el-input v-model="form.user" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">Sender usernamex</span>
</el-form-item>
<el-form-item label="邮箱密码" prop="pass">
<el-input v-model="form.pass" type="password" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">email Password</span>
</el-form-item>
<el-form-item label="SMTP地址" prop="host">
<el-input v-model="form.host" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">SMTP address</span>
</el-form-item>
<el-form-item label="SMTP端口" prop="port">
<el-input v-model="form.port" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">SMTP port</span>
</el-form-item>
<el-form-item label="">
<el-button :loading="loading" size="medium" type="primary" @click="doSubmit">保存配置</el-button>
</el-form-item>
</el-form>
</template>
<script>
import { queryEmailConfig, modifyEmailConfig } from '@/api/tools/email'
export default {
name: 'Config',
data() {
return {
loading: false, form: { id: 1, fromUser: '', user: '', pass: '', host: '', port: '', sslEnable: '' },
rules: {
fromUser: [
{ required: true, message: '请输入发件人邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
user: [
{ required: true, message: '请输入发件用户名', trigger: 'blur' }
],
pass: [
{ required: true, message: '密码不能为空', trigger: 'blur' }
],
host: [
{ required: true, message: 'SMTP地址不能为空', trigger: 'blur' }
],
port: [
{ required: true, message: 'SMTP端口不能为空', trigger: 'blur' }
]
}
}
},
created() {
this.init()
},
methods: {
init() {
queryEmailConfig().then(res => {
this.form = res
})
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
modifyEmailConfig(this.form).then(res => {
this.$notify({
title: '修改成功',
type: 'success',
duration: 2500
})
this.loading = false
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,41 @@
<template>
<el-tabs v-model="activeName" style="padding-left: 8px;">
<el-tab-pane label="邮箱配置" name="first">
<Config />
</el-tab-pane>
<el-tab-pane label="发送邮件" name="second">
<Send />
</el-tab-pane>
<el-tab-pane label="使用说明" name="third">
<div>
<blockquote class="my-blockquote"> 邮件服务器配置</blockquote>
<pre class="my-code">
# 邮件服务器的SMTP地址,可选,默认为smtp
# 邮件服务器的SMTP端口,可选,默认465或者25
# 发件人(必须正确,否则发送失败)
# 用户名,默认为发件人邮箱前缀
# 密码(注意,某些邮箱需要为SMTP服务单独设置密码,如QQ和163等等)
# 是否开启ssl,默认开启</pre>
<blockquote class="my-blockquote">更多帮助</blockquote>
<pre class="my-code">更多帮助请查看文档:<a style="color:#009688" href="http://hutool.mydoc.io/#text_319499" target="_black">hutool工具包</a></pre>
</div>
</el-tab-pane>
</el-tabs>
</template>
<script>
import Config from './config.vue'
import Send from './send.vue'
export default {
name: 'Email',
components: { Config, Send },
data() {
return {
activeName: 'second'
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,98 @@
<template>
<div>
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
<el-form-item label="邮件标题" prop="subject">
<el-input v-model="form.subject" style="width: 646px" placeholder="请输入邮件标题,标题不能为空" />
</el-form-item>
<el-form-item label="收件地址" prop="tos">
<el-input v-model="form.tos" style="width: 646px" placeholder="请输入收件地址,多个地址英文逗号,隔开" />
</el-form-item>
<div ref="editor" class="editor" />
<el-button :loading="loading" style="margin-left:1.6%;margin-bottom: 30px" size="medium" type="primary" @click="doSubmit">发送邮件</el-button>
</el-form>
</div>
</template>
<script>
import { sendEmail } from '@/api/tools/email'
import { upload } from '@/utils/upload'
import { mapGetters } from 'vuex'
import E from 'wangeditor'
export default {
name: 'Index',
data() {
return {
loading: false, form: { subject: '', tos: '', content: '' },
rules: {
subject: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
tos: [
{ required: true, message: '收件人不能为空', trigger: 'blur' }
]
}
}
},
computed: {
...mapGetters([
'imagesUploadApi',
'baseApi'
])
},
mounted() {
const _this = this
var editor = new E(this.$refs.editor)
// 自定义菜单配置
editor.config.zIndex = 10
// 文件上传
editor.config.customUploadImg = function(files, insert) {
// files 是 input 中选中的文件列表
// insert 是获取图片 url 后,插入到编辑器的方法
files.forEach(image => {
upload(_this.imagesUploadApi, image).then(res => {
const data = res.data
const url = _this.baseApi + '/file/' + data.type + '/' + data.realName
insert(url)
})
})
}
editor.config.onchange = (html) => {
this.form.content = html
}
editor.create()
},
methods: {
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
sendEmail(this.form).then(res => {
this.$notify({
title: '发送成功',
type: 'success',
duration: 2500
})
this.loading = false
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
.editor{
text-align:left;
margin: 20px;
width: 730px;
}
::v-deep .w-e-text-container {
height: 360px !important;
}
</style>
@@ -0,0 +1,122 @@
<template>
<div class="app-container">
<div class="head-container">
<div v-if="crud.props.searchToggle">
<el-input v-model="query.username" clearable size="small" placeholder="输入用户名称查询" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<rrOperation />
</div>
<crudOperation>
<el-button
slot="left"
class="filter-item"
type="danger"
icon="el-icon-delete"
size="mini"
:loading="delLoading"
:disabled="crud.selections.length === 0"
@click="doDelete(crud.selections)"
>
强退
</el-button>
</crudOperation>
</div>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column :show-overflow-tooltip="true" prop="uid" label="会话编号" />
<el-table-column prop="userName" label="用户名" />
<el-table-column prop="nickName" label="用户昵称" />
<el-table-column prop="dept" label="部门" />
<el-table-column prop="ip" label="登录IP" />
<el-table-column :show-overflow-tooltip="true" prop="address" label="登录地点" />
<el-table-column prop="browser" label="浏览器" />
<el-table-column prop="loginTime" label="登录时间" />
<el-table-column label="操作" width="70px" fixed="right">
<template slot-scope="scope">
<el-popover
:ref="scope.$index"
v-permission="['admin']"
placement="top"
width="180"
>
<p>确定强制退出该用户吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs[scope.$index].doClose()">取消</el-button>
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.key, scope.$index)">确定</el-button>
</div>
<el-button slot="reference" size="mini" type="text">强退</el-button>
</el-popover>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import { kickOutUser } from '@/api/monitor/online'
import CRUD, { presenter, header, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
export default {
name: 'OnlineUser',
components: { pagination, crudOperation, rrOperation },
cruds() {
return CRUD({ url: 'auth/online', title: '在线用户' })
},
mixins: [presenter(), header(), crud()],
data() {
return {
delLoading: false,
permission: {}
}
},
created() {
this.crud.msg.del = '强退成功!'
this.crud.optShow = {
add: false,
edit: false,
del: false,
download: true
}
},
methods: {
doDelete(datas) {
this.$confirm(`确认强退选中的${datas.length}个用户?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.delMethod(datas)
}).catch(() => {})
},
// 踢出用户
delMethod(key, index) {
const ids = []
if (key instanceof Array) {
key.forEach(val => {
ids.push(val.key)
})
} else ids.push(key)
this.delLoading = true
kickOutUser(ids).then(() => {
this.delLoading = false
if (this.$refs[index]) {
this.$refs[index].doClose()
}
this.crud.dleChangePage(1)
this.crud.delSuccessNotify()
this.crud.toQuery()
}).catch(() => {
this.delLoading = false
if (this.$refs[index]) {
this.$refs[index].doClose()
}
})
}
}
}
</script>
@@ -0,0 +1,291 @@
<template>
<div v-loading="!show" element-loading-text="数据加载中..." :style="!show ? 'height: 500px' : 'height: 100%'" class="app-container">
<div v-if="show">
<el-card class="box-card">
<div style="color: #666;font-size: 13px;">
<svg-icon icon-class="system" style="margin-right: 5px" />
<span>
系统:{{ data.sys.os }}
</span>
<span>
IP:{{ data.sys.ip }}
</span>
<span>
项目已不间断运行:{{ data.sys.day }}
</span>
<i class="el-icon-refresh" style="margin-left: 40px" @click="init" />
</div>
</el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">状态</span>
</div>
<div>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">CPU使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
{{ data.cpu.name }}
</div>
<div style="padding: 3px">
{{ data.cpu.package }}
</div>
<div style="padding: 3px">
{{ data.cpu.core }}
</div>
<div style="padding: 3px">
{{ data.cpu.logic }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.cpu.used)" />
</div>
</el-tooltip>
<div class="footer">{{ data.cpu.coreNumber }} 核心</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">内存使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
总量:{{ data.memory.total }}
</div>
<div style="padding: 3px">
已使用:{{ data.memory.used }}
</div>
<div style="padding: 3px">
空闲:{{ data.memory.available }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.memory.usageRate)" />
</div>
</el-tooltip>
<div class="footer">{{ data.memory.used }} / {{ data.memory.total }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">交换区使用率</div>
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px;">
总量:{{ data.swap.total }}
</div>
<div style="padding: 3px">
已使用:{{ data.swap.used }}
</div>
<div style="padding: 3px">
空闲:{{ data.swap.available }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.swap.usageRate)" />
</div>
</el-tooltip>
<div class="footer">{{ data.swap.used }} / {{ data.swap.total }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
<div class="title">磁盘使用率</div>
<div class="content">
<el-tooltip placement="top-end">
<div slot="content" style="font-size: 12px;">
<div style="padding: 3px">
总量:{{ data.disk.total }}
</div>
<div style="padding: 3px">
空闲:{{ data.disk.available }}
</div>
</div>
<div class="content">
<el-progress type="dashboard" :percentage="parseFloat(data.disk.usageRate)" />
</div>
</el-tooltip>
</div>
<div class="footer">{{ data.disk.used }} / {{ data.disk.total }}</div>
</el-col>
</div>
</el-card>
<div>
<el-row :gutter="6">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">CPU使用率监控</span>
</div>
<div>
<v-chart :options="cpuInfo" />
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span style="font-weight: bold;color: #666;font-size: 15px">内存使用率监控</span>
</div>
<div>
<v-chart :options="memoryInfo" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<script>
import ECharts from 'vue-echarts'
import 'echarts/lib/chart/line'
import 'echarts/lib/component/polar'
import { initDataByPost } from '@/api/data'
export default {
name: 'ServerMonitor',
components: {
'v-chart': ECharts
},
data() {
return {
show: false,
monitor: null,
url: 'api/monitor/queryServerMonitorInfo',
data: {},
cpuInfo: {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
min: 0,
max: 100,
interval: 20
},
series: [{
data: [],
type: 'line',
areaStyle: {
normal: {
color: 'rgb(32, 160, 255)' // 改变区域颜色
}
},
itemStyle: {
normal: {
color: '#6fbae1',
lineStyle: {
color: '#6fbae1' // 改变折线颜色
}
}
}
}]
},
memoryInfo: {
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
min: 0,
max: 100,
interval: 20
},
series: [{
data: [],
type: 'line',
areaStyle: {
normal: {
color: 'rgb(32, 160, 255)' // 改变区域颜色
}
},
itemStyle: {
normal: {
color: '#6fbae1',
lineStyle: {
color: '#6fbae1' // 改变折线颜色
}
}
}
}]
}
}
},
created() {
this.init()
this.monitor = window.setInterval(() => {
setTimeout(() => {
this.init()
}, 2)
}, 3500)
},
destroyed() {
clearInterval(this.monitor)
},
methods: {
init() {
initDataByPost(this.url, {}).then(data => {
this.data = data
this.show = true
if (this.cpuInfo.xAxis.data.length >= 8) {
this.cpuInfo.xAxis.data.shift()
this.memoryInfo.xAxis.data.shift()
this.cpuInfo.series[0].data.shift()
this.memoryInfo.series[0].data.shift()
}
this.cpuInfo.xAxis.data.push(data.time)
this.memoryInfo.xAxis.data.push(data.time)
this.cpuInfo.series[0].data.push(parseFloat(data.cpu.used))
this.memoryInfo.series[0].data.push(parseFloat(data.memory.usageRate))
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .box-card {
margin-bottom: 5px;
span {
margin-right: 28px;
}
.el-icon-refresh {
margin-right: 10px;
float: right;
cursor:pointer;
}
}
.cpu, .memory, .swap, .disk {
width: 20%;
float: left;
padding-bottom: 20px;
margin-right: 5%;
}
.title {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-bottom: 16px;
}
.footer {
text-align: center;
font-size: 15px;
font-weight: 500;
color: #999;
margin-top: -5px;
margin-bottom: 10px;
}
.content {
text-align: center;
margin-top: 5px;
margin-bottom: 5px;
}
</style>
@@ -0,0 +1,36 @@
<template>
<el-tabs v-model="activeName" style="padding-left: 8px;" @tab-click="tabClick">
<el-tab-pane label="本地存储" name="first">
<Local ref="local" />
</el-tab-pane>
<el-tab-pane label="七牛云存储" name="second">
<QiNiu ref="qiNiu" />
</el-tab-pane>
</el-tabs>
</template>
<script>
import QiNiu from './qiniu/index.vue'
import Local from './local/index.vue'
export default {
name: 'Storage',
components: { QiNiu, Local },
data() {
return {
activeName: 'first'
}
},
methods: {
tabClick(name) {
if (this.activeName === 'first') {
this.$refs.local.crud.toQuery()
} else {
this.$refs.qiNiu.crud.toQuery()
}
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,184 @@
<template>
<div class="app-container" style="padding: 8px;">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable size="small" placeholder="输入内容模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<!-- 新增 -->
<el-button
slot="left"
v-permission="['admin','storage:add']"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-upload"
@click="crud.toAdd"
>上传
</el-button>
</crudOperation>
</div>
<!--表单组件-->
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.add ? '文件上传' : '编辑文件'" width="500px">
<el-form ref="form" :model="form" size="small" label-width="80px">
<el-form-item label="文件名">
<el-input v-model="form.name" style="width: 370px;" />
</el-form-item>
<!-- 上传文件 -->
<el-form-item v-if="crud.status.add" label="上传">
<el-upload
ref="upload"
:limit="1"
:before-upload="beforeUpload"
:auto-upload="false"
:headers="headers"
:on-success="handleSuccess"
:on-error="handleError"
:action="fileUploadApi + '?name=' + form.name"
>
<div class="my-upload"><i class="el-icon-upload" /> 添加文件</div>
<div slot="tip" class="el-upload__tip">可上传任意格式文件,且不超过100M</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button v-if="crud.status.add" :loading="loading" type="primary" @click="upload">确认</el-button>
<el-button v-else :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="文件名">
<template slot-scope="scope">
<el-popover
:content="'file/' + scope.row.type + '/' + scope.row.realName"
placement="top-start"
title="路径"
width="200"
trigger="hover"
>
<a
slot="reference"
:href="baseApi + '/file/' + scope.row.type + '/' + scope.row.realName"
class="el-link--primary"
style="word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color: #1890ff;font-size: 13px;"
target="_blank"
>
{{ scope.row.name }}
</a>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="path" label="预览图">
<template slot-scope="{row}">
<el-image
:src=" baseApi + '/file/' + row.type + '/' + row.realName"
:preview-src-list="[baseApi + '/file/' + row.type + '/' + row.realName]"
fit="contain"
lazy
class="el-avatar"
>
<div slot="error">
<i class="el-icon-document" />
</div>
</el-image>
</template>
</el-table-column>
<el-table-column prop="suffix" label="文件类型" />
<el-table-column prop="type" label="类别" />
<el-table-column prop="size" label="大小" />
<el-table-column prop="createBy" label="操作人" />
<el-table-column prop="createTime" label="创建日期" />
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
import crudFile from '@/api/tools/localStorage'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
const defaultForm = { id: null, name: '' }
export default {
components: { pagination, crudOperation, rrOperation, DateRangePicker },
cruds() {
return CRUD({ title: '文件', url: 'api/localStorage', crudMethod: { ...crudFile }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
delAllLoading: false,
loading: false,
headers: { 'Authorization': getToken() },
permission: {
edit: ['admin', 'storage:edit'],
del: ['admin', 'storage:del']
}
}
},
computed: {
...mapGetters([
'baseApi',
'fileUploadApi'
])
},
created() {
this.crud.optShow.add = false
},
methods: {
// 上传文件
upload() {
this.$refs.upload.submit()
},
beforeUpload(file) {
let isLt2M = true
isLt2M = file.size / 1024 / 1024 < 100
if (!isLt2M) {
this.loading = false
this.$message.error('上传文件大小不能超过 100MB!')
}
this.form.name = file.name
return isLt2M
},
handleSuccess(response, file, fileList) {
this.crud.notify('上传成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
this.$refs.upload.clearFiles()
this.crud.status.add = CRUD.STATUS.NORMAL
this.crud.resetForm()
this.crud.toQuery()
},
// 监听上传失败
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.$notify({
title: msg.message,
type: 'error',
duration: 2500
})
this.loading = false
}
}
}
</script>
<style scoped>
::v-deep .el-image__error, .el-image__placeholder{
background: none;
}
::v-deep .el-image-viewer__wrapper{
top: 55px;
}
</style>
@@ -0,0 +1,98 @@
<template>
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" title="七牛云配置" append-to-body width="580px">
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="110px">
<el-form-item label="Access Key" prop="accessKey">
<el-input v-model="form.accessKey" style="width: 95%" placeholder="accessKey,在安全中心,秘钥管理中查看" />
</el-form-item>
<el-form-item label="Secret Key" prop="secretKey">
<el-input v-model="form.secretKey" type="password" style="width: 95%;" placeholder="secretKey,在安全中心,秘钥管理中查看" />
</el-form-item>
<el-form-item label="空间名称" prop="bucket">
<el-input v-model="form.bucket" style="width: 95%;" placeholder="存储空间名称作为唯一的 Bucket 识别符" />
</el-form-item>
<el-form-item label="外链域名" prop="host">
<el-input v-model="form.host" style="width: 95%;" placeholder="外链域名,可自定义,需在七牛云绑定" />
</el-form-item>
<el-form-item label="存储区域">
<el-select v-model="form.zone" placeholder="请选择存储区域">
<el-option
v-for="item in zones"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item label="空间类型" prop="type">
<el-radio v-model="form.type" label="公开">公开</el-radio>
<el-radio v-model="form.type" label="私有">私有</el-radio>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="dialog = false">取消</el-button>
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
</div>
</el-dialog>
</template>
<script>
import { queryQiniuConfig, modifyQiniuConfig } from '@/api/tools/qiniu'
export default {
data() {
return {
zones: ['华东', '华北', '东南亚'], dialog: false,
loading: false, form: { accessKey: '', secretKey: '', bucket: '', host: '', zone: '', type: '' },
rules: {
accessKey: [
{ required: true, message: '请输入accessKey', trigger: 'blur' }
],
secretKey: [
{ required: true, message: '请输入secretKey', trigger: 'blur' }
],
bucket: [
{ required: true, message: '请输入空间名称', trigger: 'blur' }
],
host: [
{ required: true, message: '请输入外链域名', trigger: 'blur' }
],
type: [
{ required: true, message: '空间类型不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init() {
queryQiniuConfig().then(res => {
this.form = res
})
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
modifyQiniuConfig(this.form).then(res => {
this.$notify({
title: '修改成功',
type: 'success',
duration: 2500
})
this.$parent.crud.toQuery()
this.loading = false
this.dialog = false
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,189 @@
<template>
<div class="app-container" style="padding: 8px;">
<!--表单组件-->
<eForm ref="form" />
<!-- 工具栏 -->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.key" clearable size="small" placeholder="输入文件名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
<date-range-picker v-model="query.createTime" class="date-item" />
<rrOperation />
</div>
<crudOperation :permission="permission">
<template slot="left">
<!-- 上传 -->
<el-button class="filter-item" size="mini" type="primary" icon="el-icon-upload" @click="dialog = true">上传</el-button>
<!-- 同步 -->
<el-button :icon="icon" class="filter-item" size="mini" type="warning" @click="synchronize">同步</el-button>
<!-- 配置 -->
<el-button
class="filter-item"
size="mini"
type="success"
icon="el-icon-s-tools"
@click="doConfig"
>配置</el-button>
</template>
</crudOperation>
<!-- 文件上传 -->
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" append-to-body width="500px" @close="doSubmit">
<el-upload
:before-remove="handleBeforeRemove"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:headers="headers"
:action="qiNiuUploadApi"
class="upload-demo"
multiple
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" style="display: block;" class="el-upload__tip">请勿上传违法文件,且文件不超过15M</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" :show-overflow-tooltip="true" label="文件名">
<template slot-scope="scope">
<a href="JavaScript:" class="el-link el-link--primary" target="_blank" type="primary" @click="download(scope.row.id)">{{ scope.row.key }}</a>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="suffix" label="文件类型" @selection-change="crud.selectionChangeHandler" />
<el-table-column prop="bucket" label="空间名称" />
<el-table-column prop="size" label="文件大小" />
<el-table-column prop="type" label="空间类型" />
<el-table-column prop="updateTime" label="创建日期" />
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudQiNiu from '@/api/tools/qiniu'
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
import eForm from './form.vue'
import CRUD, { presenter, header, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import pagination from '@crud/Pagination'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { eForm, pagination, crudOperation, rrOperation, DateRangePicker },
cruds() {
return CRUD({ title: '七牛云文件', url: 'api/qiNiuContent', crudMethod: { ...crudQiNiu }})
},
mixins: [presenter(), header(), crud()],
data() {
return {
permission: {
del: ['admin', 'storage:del']
},
title: '文件', dialog: false,
icon: 'el-icon-refresh',
url: '', headers: { 'Authorization': getToken() },
dialogImageUrl: '', dialogVisible: false, fileList: [], files: [], newWin: null
}
},
computed: {
...mapGetters([
'qiNiuUploadApi'
])
},
watch: {
url(newVal, oldVal) {
if (newVal && this.newWin) {
this.newWin.sessionStorage.clear()
this.newWin.location.href = newVal
// 重定向后把url和newWin重置
this.url = ''
this.newWin = null
}
}
},
created() {
this.crud.optShow.add = false
this.crud.optShow.edit = false
},
methods: {
// 七牛云配置
doConfig() {
const _this = this.$refs.form
_this.init()
_this.dialog = true
},
handleSuccess(response, file, fileList) {
const uid = file.uid
const id = response.id
this.files.push({ uid, id })
},
handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) {
if (this.files[i].uid === file.uid) {
crudQiNiu.del([this.files[i].id]).then(res => {})
return true
}
}
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
// 刷新列表数据
doSubmit() {
this.fileList = []
this.dialogVisible = false
this.dialogImageUrl = ''
this.dialog = false
this.crud.toQuery()
},
// 监听上传失败
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR)
},
// 下载文件
download(id) {
this.downloadLoading = true
// 先打开一个空的新窗口,再请求
this.newWin = window.open()
crudQiNiu.createFilePreviewUrl(id).then(res => {
this.downloadLoading = false
this.url = res.url
}).catch(err => {
this.downloadLoading = false
console.log(err.response.data.message)
})
},
// 同步数据
synchronize() {
this.icon = 'el-icon-loading'
crudQiNiu.sync().then(res => {
this.icon = 'el-icon-refresh'
this.$message({
showClose: true,
message: '数据同步成功',
type: 'success',
duration: 1500
})
this.crud.toQuery()
}).catch(err => {
this.icon = 'el-icon-refresh'
console.log(err.response.data.message)
})
}
}
}
</script>
<style scoped>
</style>
@@ -57,7 +57,7 @@
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" @keydown.native="keydown($event)" />
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-form-item label="手机号" prop="phone">
<el-input v-model.number="form.phone" />
</el-form-item>
<el-form-item label="昵称" prop="nickName">
@@ -138,7 +138,7 @@
<el-table-column :show-overflow-tooltip="true" prop="username" label="用户名" />
<el-table-column :show-overflow-tooltip="true" prop="nickName" label="昵称" />
<el-table-column prop="gender" label="性别" />
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="电话" />
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="手机号" />
<el-table-column :show-overflow-tooltip="true" width="135" prop="email" label="邮箱" />
<el-table-column :show-overflow-tooltip="true" prop="dept" label="部门">
<template slot-scope="scope">