初始化
parent
a54362bbb8
commit
494929c7c4
|
@ -0,0 +1,217 @@
|
||||||
|
<template>
|
||||||
|
<div class="upload-file">
|
||||||
|
<el-upload
|
||||||
|
ref="fileUpload"
|
||||||
|
:action="uploadFileUrl"
|
||||||
|
:before-upload="handleBeforeUpload"
|
||||||
|
:file-list="fileList"
|
||||||
|
:headers="headers"
|
||||||
|
:limit="limit"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
:on-success="handleUploadSuccess"
|
||||||
|
:show-file-list="false"
|
||||||
|
class="upload-file-uploader"
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
<!-- 上传按钮 -->
|
||||||
|
<el-button size="mini" type="primary">选取文件</el-button>
|
||||||
|
<!-- 上传提示 -->
|
||||||
|
<div v-if="showTip" slot="tip" class="el-upload__tip">
|
||||||
|
请上传
|
||||||
|
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b></template>
|
||||||
|
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b></template>
|
||||||
|
的文件
|
||||||
|
</div>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<!-- 文件列表 -->
|
||||||
|
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||||
|
<li v-for="(file, index) in fileList" :key="file.url" class="el-upload-list__item ele-upload-list__item-content">
|
||||||
|
<el-link :href="file.url" :underline="false" target="_blank">
|
||||||
|
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
||||||
|
</el-link>
|
||||||
|
<div class="ele-upload-list__item-content-action">
|
||||||
|
<el-link :underline="false" type="danger" @click="handleDelete(index)">删除</el-link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getToken} from "@/utils/auth";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "FileUpload",
|
||||||
|
props: {
|
||||||
|
// 值
|
||||||
|
value: [String, Object, Array],
|
||||||
|
// 数量限制
|
||||||
|
limit: {
|
||||||
|
type: Number,
|
||||||
|
default: 5,
|
||||||
|
},
|
||||||
|
// 大小限制(MB)
|
||||||
|
fileSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 5,
|
||||||
|
},
|
||||||
|
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||||
|
fileType: {
|
||||||
|
type: Array,
|
||||||
|
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
|
||||||
|
},
|
||||||
|
// 是否显示提示
|
||||||
|
isShowTip: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
number: 0,
|
||||||
|
uploadList: [],
|
||||||
|
uploadFileUrl: process.env.VUE_APP_BASE_API + "/file/upload", // 上传文件服务器地址
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + getToken(),
|
||||||
|
},
|
||||||
|
fileList: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
let temp = 1;
|
||||||
|
// 首先将值转为数组
|
||||||
|
const list = Array.isArray(val) ? val : this.value.split(',');
|
||||||
|
// 然后将数组转为对象数组
|
||||||
|
this.fileList = list.map(item => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
item = {name: item, url: item};
|
||||||
|
}
|
||||||
|
item.uid = item.uid || new Date().getTime() + temp++;
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.fileList = [];
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 是否显示提示
|
||||||
|
showTip() {
|
||||||
|
return this.isShowTip && (this.fileType || this.fileSize);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 上传前校检格式和大小
|
||||||
|
handleBeforeUpload(file) {
|
||||||
|
// 校检文件类型
|
||||||
|
if (this.fileType) {
|
||||||
|
const fileName = file.name.split('.');
|
||||||
|
const fileExt = fileName[fileName.length - 1];
|
||||||
|
const isTypeOk = this.fileType.indexOf(fileExt) >= 0;
|
||||||
|
if (!isTypeOk) {
|
||||||
|
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 校检文件大小
|
||||||
|
if (this.fileSize) {
|
||||||
|
const isLt = file.size / 1024 / 1024 < this.fileSize;
|
||||||
|
if (!isLt) {
|
||||||
|
this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$modal.loading("正在上传文件,请稍候...");
|
||||||
|
this.number++;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 文件个数超出
|
||||||
|
handleExceed() {
|
||||||
|
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
|
||||||
|
},
|
||||||
|
// 上传失败
|
||||||
|
handleUploadError(err) {
|
||||||
|
this.$modal.msgError("上传文件失败,请重试");
|
||||||
|
this.$modal.closeLoading()
|
||||||
|
},
|
||||||
|
// 上传成功回调
|
||||||
|
handleUploadSuccess(res, file) {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
this.uploadList.push({name: res.data.url, url: res.data.url});
|
||||||
|
this.uploadedSuccessfully();
|
||||||
|
} else {
|
||||||
|
this.number--;
|
||||||
|
this.$modal.closeLoading();
|
||||||
|
this.$modal.msgError(res.data.msg);
|
||||||
|
this.$refs.fileUpload.handleRemove(file);
|
||||||
|
this.uploadedSuccessfully();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 删除文件
|
||||||
|
handleDelete(index) {
|
||||||
|
this.fileList.splice(index, 1);
|
||||||
|
this.$emit("input", this.listToString(this.fileList));
|
||||||
|
},
|
||||||
|
// 上传结束处理
|
||||||
|
uploadedSuccessfully() {
|
||||||
|
if (this.number > 0 && this.uploadList.length === this.number) {
|
||||||
|
this.fileList = this.fileList.concat(this.uploadList);
|
||||||
|
this.uploadList = [];
|
||||||
|
this.number = 0;
|
||||||
|
this.$emit("input", this.listToString(this.fileList));
|
||||||
|
this.$modal.closeLoading();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取文件名称
|
||||||
|
getFileName(name) {
|
||||||
|
if (name.lastIndexOf("/") > -1) {
|
||||||
|
return name.slice(name.lastIndexOf("/") + 1);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 对象转成指定字符串分隔
|
||||||
|
listToString(list, separator) {
|
||||||
|
let strs = "";
|
||||||
|
separator = separator || ",";
|
||||||
|
for (let i in list) {
|
||||||
|
strs += list[i].url + separator;
|
||||||
|
}
|
||||||
|
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.upload-file-uploader {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-file-list .el-upload-list__item {
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
line-height: 2;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-file-list .ele-upload-list__item-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ele-upload-list__item-content-action .el-link {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<div style="padding: 0 15px;" @click="toggleClick">
|
||||||
|
<svg
|
||||||
|
:class="{'is-active':isActive}"
|
||||||
|
class="hamburger"
|
||||||
|
height="64"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
width="64"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Hamburger',
|
||||||
|
props: {
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleClick() {
|
||||||
|
this.$emit('toggleClick')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.hamburger {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger.is-active {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,201 @@
|
||||||
|
<template>
|
||||||
|
<div :class="{'show':show}" class="header-search">
|
||||||
|
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click"/>
|
||||||
|
<el-select
|
||||||
|
ref="headerSearchSelect"
|
||||||
|
v-model="search"
|
||||||
|
:remote-method="querySearch"
|
||||||
|
class="header-search-select"
|
||||||
|
default-first-option
|
||||||
|
filterable
|
||||||
|
placeholder="Search"
|
||||||
|
remote
|
||||||
|
@change="change"
|
||||||
|
>
|
||||||
|
<el-option v-for="option in options" :key="option.item.path" :label="option.item.title.join(' > ')"
|
||||||
|
:value="option.item"/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// fuse is a lightweight fuzzy-search module
|
||||||
|
// make search results more in line with expectations
|
||||||
|
import Fuse from 'fuse.js/dist/fuse.min.js'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HeaderSearch',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
search: '',
|
||||||
|
options: [],
|
||||||
|
searchPool: [],
|
||||||
|
show: false,
|
||||||
|
fuse: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
routes() {
|
||||||
|
return this.$store.getters.permission_routes
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
routes() {
|
||||||
|
this.searchPool = this.generateRoutes(this.routes)
|
||||||
|
},
|
||||||
|
searchPool(list) {
|
||||||
|
this.initFuse(list)
|
||||||
|
},
|
||||||
|
show(value) {
|
||||||
|
if (value) {
|
||||||
|
document.body.addEventListener('click', this.close)
|
||||||
|
} else {
|
||||||
|
document.body.removeEventListener('click', this.close)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.searchPool = this.generateRoutes(this.routes)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
click() {
|
||||||
|
this.show = !this.show
|
||||||
|
if (this.show) {
|
||||||
|
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
|
||||||
|
this.options = []
|
||||||
|
this.show = false
|
||||||
|
},
|
||||||
|
change(val) {
|
||||||
|
const path = val.path;
|
||||||
|
const query = val.query;
|
||||||
|
if (this.ishttp(val.path)) {
|
||||||
|
// http(s):// 路径新窗口打开
|
||||||
|
const pindex = path.indexOf("http");
|
||||||
|
window.open(path.substr(pindex, path.length), "_blank");
|
||||||
|
} else {
|
||||||
|
if (query) {
|
||||||
|
this.$router.push({path: path, query: JSON.parse(query)});
|
||||||
|
} else {
|
||||||
|
this.$router.push(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.search = ''
|
||||||
|
this.options = []
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.show = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initFuse(list) {
|
||||||
|
this.fuse = new Fuse(list, {
|
||||||
|
shouldSort: true,
|
||||||
|
threshold: 0.4,
|
||||||
|
location: 0,
|
||||||
|
distance: 100,
|
||||||
|
minMatchCharLength: 1,
|
||||||
|
keys: [{
|
||||||
|
name: 'title',
|
||||||
|
weight: 0.7
|
||||||
|
}, {
|
||||||
|
name: 'path',
|
||||||
|
weight: 0.3
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// Filter out the routes that can be displayed in the sidebar
|
||||||
|
// And generate the internationalized title
|
||||||
|
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
||||||
|
let res = []
|
||||||
|
|
||||||
|
for (const router of routes) {
|
||||||
|
// skip hidden router
|
||||||
|
if (router.hidden) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path,
|
||||||
|
title: [...prefixTitle]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (router.meta && router.meta.title) {
|
||||||
|
data.title = [...data.title, router.meta.title]
|
||||||
|
|
||||||
|
if (router.redirect !== 'noRedirect') {
|
||||||
|
// only push the routes with title
|
||||||
|
// special case: need to exclude parent router without redirect
|
||||||
|
res.push(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (router.query) {
|
||||||
|
data.query = router.query
|
||||||
|
}
|
||||||
|
|
||||||
|
// recursive child routes
|
||||||
|
if (router.children) {
|
||||||
|
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
|
||||||
|
if (tempRoutes.length >= 1) {
|
||||||
|
res = [...res, ...tempRoutes]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
querySearch(query) {
|
||||||
|
if (query !== '') {
|
||||||
|
this.options = this.fuse.search(query)
|
||||||
|
} else {
|
||||||
|
this.options = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ishttp(url) {
|
||||||
|
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.header-search {
|
||||||
|
font-size: 0 !important;
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-search-select {
|
||||||
|
font-size: 18px;
|
||||||
|
transition: width 0.2s;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
::v-deep .el-input__inner {
|
||||||
|
border-radius: 0;
|
||||||
|
border: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-bottom: 1px solid #d9d9d9;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
.header-search-select {
|
||||||
|
width: 210px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,115 @@
|
||||||
|
<!-- @author zhengjie -->
|
||||||
|
<template>
|
||||||
|
<div class="icon-body">
|
||||||
|
<el-input v-model="name" class="icon-search" clearable placeholder="请输入图标名称" @clear="filterIcons"
|
||||||
|
@input="filterIcons">
|
||||||
|
<i slot="suffix" class="el-icon-search el-input__icon"/>
|
||||||
|
</el-input>
|
||||||
|
<div class="icon-list">
|
||||||
|
<div class="list-container">
|
||||||
|
<div v-for="(item, index) in iconList" :key="index" class="icon-item-wrapper" @click="selectedIcon(item)">
|
||||||
|
<div :class="['icon-item', { active: activeIcon === item }]">
|
||||||
|
<svg-icon :icon-class="item" class-name="icon" style="height: 25px;width: 16px;"/>
|
||||||
|
<span>{{ item }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import icons from './requireIcons'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'IconSelect',
|
||||||
|
props: {
|
||||||
|
activeIcon: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
iconList: icons
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
filterIcons() {
|
||||||
|
this.iconList = icons
|
||||||
|
if (this.name) {
|
||||||
|
this.iconList = this.iconList.filter(item => item.includes(this.name))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectedIcon(name) {
|
||||||
|
this.$emit('selected', name)
|
||||||
|
document.body.click()
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.name = ''
|
||||||
|
this.iconList = icons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||||
|
.icon-body {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.icon-search {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-list {
|
||||||
|
height: 200px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.icon-item-wrapper {
|
||||||
|
width: calc(100% / 3);
|
||||||
|
height: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.icon-item {
|
||||||
|
display: flex;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 5px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #ececec;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
padding-left: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-item.active {
|
||||||
|
background: #ececec;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<el-image
|
||||||
|
:preview-src-list="realSrcList"
|
||||||
|
:src="`${realSrc}`"
|
||||||
|
:style="`width:${realWidth};height:${realHeight};`"
|
||||||
|
fit="cover"
|
||||||
|
>
|
||||||
|
<div slot="error" class="image-slot">
|
||||||
|
<i class="el-icon-picture-outline"></i>
|
||||||
|
</div>
|
||||||
|
</el-image>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ImagePreview",
|
||||||
|
props: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
realSrc() {
|
||||||
|
if (!this.src) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let real_src = this.src.split(",")[0];
|
||||||
|
return real_src;
|
||||||
|
},
|
||||||
|
realSrcList() {
|
||||||
|
if (!this.src) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let real_src_list = this.src.split(",");
|
||||||
|
let srcList = [];
|
||||||
|
real_src_list.forEach(item => {
|
||||||
|
return srcList.push(item);
|
||||||
|
});
|
||||||
|
return srcList;
|
||||||
|
},
|
||||||
|
realWidth() {
|
||||||
|
return typeof this.width == "string" ? this.width : `${this.width}px`;
|
||||||
|
},
|
||||||
|
realHeight() {
|
||||||
|
return typeof this.height == "string" ? this.height : `${this.height}px`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-image {
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #ebeef5;
|
||||||
|
box-shadow: 0 0 5px 1px #ccc;
|
||||||
|
|
||||||
|
::v-deep .el-image__inner {
|
||||||
|
transition: all 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .image-slot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<template>
|
||||||
|
<div v-loading="loading" :style="'height:' + height">
|
||||||
|
<iframe
|
||||||
|
:src="src"
|
||||||
|
frameborder="no"
|
||||||
|
scrolling="auto"
|
||||||
|
style="width: 100%; height: 100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
height: document.documentElement.clientHeight - 94.5 + "px;",
|
||||||
|
loading: true,
|
||||||
|
url: this.src
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = false;
|
||||||
|
}, 300);
|
||||||
|
const that = this;
|
||||||
|
window.onresize = function temp() {
|
||||||
|
that.height = document.documentElement.clientHeight - 94.5 + "px;";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,23 @@
|
||||||
|
import hasRole from './permission/hasRole'
|
||||||
|
import hasPermi from './permission/hasPermi'
|
||||||
|
import dialogDrag from './dialog/drag'
|
||||||
|
import dialogDragWidth from './dialog/dragWidth'
|
||||||
|
import dialogDragHeight from './dialog/dragHeight'
|
||||||
|
import clipboard from './module/clipboard'
|
||||||
|
|
||||||
|
const install = function (Vue) {
|
||||||
|
Vue.directive('hasRole', hasRole)
|
||||||
|
Vue.directive('hasPermi', hasPermi)
|
||||||
|
Vue.directive('clipboard', clipboard)
|
||||||
|
Vue.directive('dialogDrag', dialogDrag)
|
||||||
|
Vue.directive('dialogDragWidth', dialogDragWidth)
|
||||||
|
Vue.directive('dialogDragHeight', dialogDragHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.Vue) {
|
||||||
|
window['hasRole'] = hasRole
|
||||||
|
window['hasPermi'] = hasPermi
|
||||||
|
Vue.use(install); // eslint-disable-line
|
||||||
|
}
|
||||||
|
|
||||||
|
export default install
|
|
@ -0,0 +1,168 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Router from 'vue-router'
|
||||||
|
/* Layout */
|
||||||
|
import Layout from '@/layout'
|
||||||
|
|
||||||
|
Vue.use(Router)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: 路由配置项
|
||||||
|
*
|
||||||
|
* hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
|
||||||
|
* alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
|
||||||
|
* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
|
||||||
|
* // 若你想不管路由下面的 children 声明的个数都显示你的根路由
|
||||||
|
* // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
|
||||||
|
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
|
||||||
|
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
|
||||||
|
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
|
||||||
|
* roles: ['admin', 'common'] // 访问路由的角色权限
|
||||||
|
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
|
||||||
|
* meta : {
|
||||||
|
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
|
||||||
|
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
|
||||||
|
icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg
|
||||||
|
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
|
||||||
|
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 公共路由
|
||||||
|
export const constantRoutes = [
|
||||||
|
{
|
||||||
|
path: '/redirect',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/redirect/:path(.*)',
|
||||||
|
component: () => import('@/views/redirect')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
component: () => import('@/views/login'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register',
|
||||||
|
component: () => import('@/views/register'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/404',
|
||||||
|
component: () => import('@/views/error/404'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/401',
|
||||||
|
component: () => import('@/views/error/401'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: Layout,
|
||||||
|
redirect: 'index',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
component: () => import('@/views/index'),
|
||||||
|
name: 'Index',
|
||||||
|
meta: {title: '首页', icon: 'dashboard', affix: true}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/user',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
redirect: 'noredirect',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'profile',
|
||||||
|
component: () => import('@/views/system/user/profile/index'),
|
||||||
|
name: 'Profile',
|
||||||
|
meta: {title: '个人中心', icon: 'user'}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 动态路由,基于用户权限动态去加载
|
||||||
|
export const dynamicRoutes = [
|
||||||
|
{
|
||||||
|
path: '/system/user-auth',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
permissions: ['system:user:edit'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'role/:userId(\\d+)',
|
||||||
|
component: () => import('@/views/system/user/authRole'),
|
||||||
|
name: 'AuthRole',
|
||||||
|
meta: {title: '分配角色', activeMenu: '/system/user'}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/role-auth',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
permissions: ['system:role:edit'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'user/:roleId(\\d+)',
|
||||||
|
component: () => import('@/views/system/role/authUser'),
|
||||||
|
name: 'AuthUser',
|
||||||
|
meta: {title: '分配用户', activeMenu: '/system/role'}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/dict-data',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
permissions: ['system:dict:list'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index/:dictId(\\d+)',
|
||||||
|
component: () => import('@/views/system/dict/data'),
|
||||||
|
name: 'Data',
|
||||||
|
meta: {title: '字典数据', activeMenu: '/system/dict'}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/tool/gen-edit',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
permissions: ['tool:gen:edit'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index/:tableId(\\d+)',
|
||||||
|
component: () => import('@/views/tool/gen/editTable'),
|
||||||
|
name: 'GenEdit',
|
||||||
|
meta: {title: '修改生成配置', activeMenu: '/tool/gen'}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 防止连续点击多次路由报错
|
||||||
|
let routerPush = Router.prototype.push;
|
||||||
|
let routerReplace = Router.prototype.replace;
|
||||||
|
// push
|
||||||
|
Router.prototype.push = function push(location) {
|
||||||
|
return routerPush.call(this, location).catch(err => err)
|
||||||
|
}
|
||||||
|
// replace
|
||||||
|
Router.prototype.replace = function push(location) {
|
||||||
|
return routerReplace.call(this, location).catch(err => err)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Router({
|
||||||
|
mode: 'history', // 去掉url中的#
|
||||||
|
scrollBehavior: () => ({y: 0}),
|
||||||
|
routes: constantRoutes
|
||||||
|
})
|
Loading…
Reference in New Issue