优化文件管理器
This commit is contained in:
parent
c45bc8ca29
commit
cf035f824e
|
|
@ -1,3 +1,4 @@
|
|||
@charset "UTF-8";
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -6,6 +7,14 @@ body.page-filemanager {
|
|||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* CSS3属性 */
|
||||
}
|
||||
body.page-filemanager [class*=" el-icon-"], body.page-filemanager [class^=el-icon-] {
|
||||
font-weight: 600;
|
||||
}
|
||||
body.page-filemanager .filemanager-wrap {
|
||||
display: flex;
|
||||
|
|
@ -17,6 +26,15 @@ body.page-filemanager .filemanager-wrap .filemanager-navbar {
|
|||
background-color: #293042;
|
||||
overflow-y: auto;
|
||||
}
|
||||
body.page-filemanager .filemanager-wrap .filemanager-navbar::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
}
|
||||
body.page-filemanager .filemanager-wrap .filemanager-navbar::-webkit-scrollbar-thumb {
|
||||
background: #409EFF;
|
||||
}
|
||||
body.page-filemanager .filemanager-wrap .filemanager-navbar::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
body.page-filemanager .filemanager-wrap .filemanager-navbar .el-tree {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
|
@ -139,7 +157,6 @@ body.page-filemanager .filemanager-wrap .filemanager-content .content-center .im
|
|||
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list .text .el-icon-check {
|
||||
color: #409EFF;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
body.page-filemanager .filemanager-wrap .filemanager-content .content-footer {
|
||||
height: 56px;
|
||||
|
|
|
|||
|
|
@ -2102,17 +2102,18 @@ __webpack_require__.r(__webpack_exports__);
|
|||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
window.axios = __webpack_require__(/*! axios */ "./node_modules/axios/index.js");
|
||||
var token = document.querySelector('meta[name="csrf-token"]').content;
|
||||
var base = document.querySelector('base').href;
|
||||
var instance = axios.create({
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
||||
'X-CSRF-TOKEN': token
|
||||
}
|
||||
});
|
||||
axios.defaults.timeout = 5000; // 请求超时
|
||||
// axios.defaults.baseURL = process.env.NODE_ENV == 'production' ? process.env.VUE_APP_BASE_URL + '/' : '/';
|
||||
// console.log(process.env.VUE_APP_BASE_URL)
|
||||
|
||||
console.log($('base').attr('href'));
|
||||
axios.defaults.baseURL = $('base').attr('href');
|
||||
axios.defaults.baseURL = base;
|
||||
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
|
||||
/**
|
||||
* get 请求
|
||||
|
|
|
|||
|
|
@ -2068,17 +2068,18 @@ __webpack_require__.r(__webpack_exports__);
|
|||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
window.axios = __webpack_require__(/*! axios */ "./node_modules/axios/index.js");
|
||||
var token = document.querySelector('meta[name="csrf-token"]').content;
|
||||
var base = document.querySelector('base').href;
|
||||
var instance = axios.create({
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
||||
'X-CSRF-TOKEN': token
|
||||
}
|
||||
});
|
||||
axios.defaults.timeout = 5000; // 请求超时
|
||||
// axios.defaults.baseURL = process.env.NODE_ENV == 'production' ? process.env.VUE_APP_BASE_URL + '/' : '/';
|
||||
// console.log(process.env.VUE_APP_BASE_URL)
|
||||
|
||||
console.log($('base').attr('href'));
|
||||
axios.defaults.baseURL = $('base').attr('href');
|
||||
axios.defaults.baseURL = base;
|
||||
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
|
||||
/**
|
||||
* get 请求
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
// 自 https://blog.wy310.cn/2020/12/17/vue-drag-batch-select/ 修改
|
||||
Vue.directive('batch-select', {
|
||||
// 当被绑定的元素插入到 DOM 中时……
|
||||
// inserted: (el, binding) => {}
|
||||
componentUpdated: (el, binding) => {
|
||||
// 设置被绑定元素el(即上述的box)的position为relative,目的是让蓝色半透明遮罩area相对其定位
|
||||
el.style.position = 'relative';
|
||||
// 记录el在视窗中的位置elPos
|
||||
const { x, y } = el.getBoundingClientRect()
|
||||
const elPos = { x, y }
|
||||
// 获取该指令调用者传递过来的参数:className / selectArr 是选中的 index 要放的数组
|
||||
// v-batch-select="{ className: '.el-checkbox', selectArr: [] }"
|
||||
// 表示要使用鼠标框选类名为'.el-checkbox'的元素
|
||||
const optionClassName = binding.value.className
|
||||
const options = [].slice.call(el.querySelectorAll(optionClassName))
|
||||
// 获取被框选对象们的x、y、width、height
|
||||
const optionsXYWH = []
|
||||
options.forEach(v => {
|
||||
const obj = v.getBoundingClientRect()
|
||||
optionsXYWH.push({
|
||||
x: obj.x - elPos.x,
|
||||
y: obj.y - elPos.y,
|
||||
w: obj.width,
|
||||
h: obj.height
|
||||
})
|
||||
})
|
||||
// 创建一个div作为area区域,注意定位是absolute,visibility初始值是hidden
|
||||
const area = document.createElement('div')
|
||||
area.style = 'position: absolute; border: 1px solid #409eff; background-color: rgba(64, 158, 255, 0.1); z-index: 10; visibility: hidden;'
|
||||
area.className = 'area'
|
||||
area.innerHTML = ''
|
||||
el.appendChild(area)
|
||||
el.onmousedown = (e) => {
|
||||
// 获取鼠标按下时相对box的坐标
|
||||
const startX = e.clientX - elPos.x
|
||||
const startY = e.clientY - elPos.y
|
||||
// 判断鼠标按下后是否发生移动的标识
|
||||
let hasMove = false
|
||||
|
||||
|
||||
document.onmousemove = (e) => {
|
||||
hasMove = true
|
||||
binding.value.setSelectStatus(true)
|
||||
// 显示area
|
||||
area.style.visibility = 'visible'
|
||||
// 获取鼠标移动过程中指针的实时位置
|
||||
const endX = e.clientX - elPos.x
|
||||
const endY = e.clientY - elPos.y
|
||||
// 这里使用绝对值是为了兼容鼠标从各个方向框选的情况
|
||||
const width = Math.abs(endX - startX)
|
||||
const height = Math.abs(endY - startY)
|
||||
// 根据初始位置和实时位置计算出area的left、top、width、height
|
||||
const left = Math.min(startX, endX)
|
||||
const top = Math.min(startY, endY)
|
||||
// 实时绘制
|
||||
area.style.left = `${left}px`
|
||||
area.style.top = `${top}px`
|
||||
area.style.width = `${width}px`
|
||||
area.style.height = `${height}px`
|
||||
|
||||
const areaTop = parseInt(top)
|
||||
const areaRight = parseInt(left) + parseInt(width)
|
||||
const areaBottom = parseInt(top) + parseInt(height)
|
||||
const areaLeft = parseInt(left)
|
||||
binding.value.selectImageIndex.length = 0
|
||||
optionsXYWH.forEach((v, i) => {
|
||||
const optionTop = v.y
|
||||
const optionRight = v.x + v.w
|
||||
const optionBottom = v.y + v.h
|
||||
const optionLeft = v.x
|
||||
if (!(areaTop > optionBottom || areaRight < optionLeft || areaBottom < optionTop || areaLeft > optionRight)) {
|
||||
// 该指令的调用者可以监听到selectIdxs的变化
|
||||
binding.value.selectImageIndex.push(i)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
document.onmouseup = (e) => {
|
||||
document.onmousemove = document.onmouseup = null
|
||||
if (hasMove) {
|
||||
// 鼠标抬起时,如果之前发生过移动,则执行碰撞检测
|
||||
const { left, top, width, height } = area.style
|
||||
setTimeout(() => {
|
||||
binding.value.setSelectStatus(false)
|
||||
}, 100);
|
||||
}
|
||||
// 恢复以下数据
|
||||
hasMove = false
|
||||
area.style.visibility = 'hidden'
|
||||
area.style.left = 0
|
||||
area.style.top = 0
|
||||
area.style.width = 0
|
||||
area.style.height = 0
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
// update: (el, binding) => {}
|
||||
})
|
||||
|
|
@ -11,6 +11,11 @@ body.page-filemanager {
|
|||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
user-select:none; /* CSS3属性 */
|
||||
|
||||
[class*=" el-icon-"], [class^=el-icon-] {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.filemanager-wrap {
|
||||
display: flex;
|
||||
|
|
@ -22,6 +27,18 @@ body.page-filemanager {
|
|||
background-color: #293042;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $primary;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.el-tree {
|
||||
background-color: transparent;
|
||||
.el-tree-node__content {
|
||||
|
|
@ -206,7 +223,6 @@ body.page-filemanager {
|
|||
.el-icon-check {
|
||||
color: $primary;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,9 +115,9 @@
|
|||
that.loading = false;
|
||||
|
||||
if (that.isLanguage) {
|
||||
that.src[that.tabActiveId] = images.path;
|
||||
that.src[that.tabActiveId] = images[0].path;
|
||||
} else {
|
||||
that.src = images.path;
|
||||
that.src = images[0].path;
|
||||
}
|
||||
// console.log(that.src);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,19 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<base href="{{ $admin_base_url }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<script src="{{ asset('vendor/vue/2.6.12/vue.js') }}"></script>
|
||||
<script src="{{ asset('vendor/element-ui/2.15.9/index.js') }}"></script>
|
||||
{{-- <script src="{{ asset('vendor/element-ui/2.15.6/js.js') }}"></script> --}}
|
||||
<script src="{{ asset('vendor/jquery/jquery-3.6.0.min.js') }}"></script>
|
||||
<script src="{{ asset('vendor/layer/3.5.1/layer.js') }}"></script>
|
||||
<script src="{{ asset('vendor/vue/batch_select.js') }}"></script>
|
||||
<link href="{{ mix('/build/beike/admin/css/bootstrap.css') }}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ asset('vendor/element-ui/2.15.9/index.css') }}">
|
||||
<link href="{{ mix('build/beike/admin/css/filemanager.css') }}" rel="stylesheet">
|
||||
<script src="{{ mix('build/beike/admin/js/app.js') }}"></script>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>beike filemanager</title>
|
||||
</head>
|
||||
<body class="page-filemanager">
|
||||
|
|
@ -54,15 +56,16 @@
|
|||
<div class="filemanager-content" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0.5)">
|
||||
<div class="content-head">
|
||||
<div class="left">
|
||||
<el-link :underline="false" :disabled="editingImageIndex === null" icon="el-icon-download">下载</el-link>
|
||||
<el-link :underline="false" :disabled="editingImageIndex === null" @click="deleteFile" icon="el-icon-delete">删除</el-link>
|
||||
<el-link :underline="false" :disabled="editingImageIndex === null" @click="openInputBox('image')" icon="el-icon-edit">重命名</el-link>
|
||||
<el-link :underline="false" :disabled="!!!selectImageIndex.length" icon="el-icon-download" @click="downloadImages">下载</el-link>
|
||||
<el-link :underline="false" :disabled="!!!selectImageIndex.length" @click="deleteFile" icon="el-icon-delete">删除</el-link>
|
||||
<el-link :underline="false" :disabled="selectImageIndex.length == 1 ? false : true" @click="openInputBox('image')" icon="el-icon-edit">重命名</el-link>
|
||||
<el-link :underline="false" :disabled="!!!images.length && !!!selectImageIndex.length" @click="selectAll()" icon="el-icon-finished">全选</el-link>
|
||||
</div>
|
||||
<div class="right">
|
||||
<el-button size="small" type="primary" @click="openUploadFile" icon="el-icon-upload2">上传文件</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center">
|
||||
<div class="content-center" v-batch-select="{ className: '.image-list', selectImageIndex, setSelectStatus: updateSelectStatus }">
|
||||
<div :class="['image-list', file.selected ? 'active' : '']" v-for="file, index in images" :key="index" @click="checkedImage(index)">
|
||||
<div class="img"><img :src="file.url"></div>
|
||||
<div class="text">
|
||||
|
|
@ -81,7 +84,7 @@
|
|||
:total="image_total">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<div class="right"><el-button size="small" icon="el-icon-check" type="primary" @click="fileChecked" :disabled="editingImageIndex === null">选择</el-button></div>
|
||||
<div class="right"><el-button size="small" icon="el-icon-check" type="primary" @click="fileChecked" :disabled="!!!selectImageIndex.length">选择</el-button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -133,10 +136,13 @@
|
|||
max: 40,
|
||||
paneLengthPercent: 26,
|
||||
triggerLength: 10,
|
||||
isShift: false,
|
||||
|
||||
ssss:[],
|
||||
loading: false,
|
||||
isBatchSelect: false,
|
||||
|
||||
editingImageIndex: null,
|
||||
selectImageIndex: [],
|
||||
|
||||
treeData: [{name: '图片空间', path: '/', children: @json($folders)}],
|
||||
|
||||
|
|
@ -145,6 +151,7 @@
|
|||
label: 'name',
|
||||
isLeaf: 'leaf'
|
||||
},
|
||||
selectIdxs: [],
|
||||
|
||||
uploadFileDialog: {
|
||||
show: false,
|
||||
|
|
@ -168,7 +175,20 @@
|
|||
},
|
||||
// 侦听器
|
||||
watch: {
|
||||
images: {
|
||||
handler(val) {
|
||||
if (this.isBatchSelect) return;
|
||||
// 将选中的图片索引放入 selectImageIndex,未选中则清空
|
||||
this.selectImageIndex = val.filter(item => item.selected).map(e => this.images.indexOf(e));
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
|
||||
selectImageIndex(indexs) {
|
||||
this.images.forEach((item, index) => {
|
||||
item.selected = indexs.includes(index);
|
||||
});
|
||||
},
|
||||
},
|
||||
// 组件方法
|
||||
methods: {
|
||||
|
|
@ -181,6 +201,10 @@
|
|||
this.loadData(e, node)
|
||||
},
|
||||
|
||||
updateSelectStatus(status) {
|
||||
this.isBatchSelect = status
|
||||
},
|
||||
|
||||
pageCurrentChange(e) {
|
||||
this.image_page = e
|
||||
this.loadData()
|
||||
|
|
@ -298,13 +322,32 @@
|
|||
},
|
||||
|
||||
checkedImage(index) {
|
||||
this.editingImageIndex = index;
|
||||
this.images.map(e => !e.index ? e.selected = false : '')
|
||||
// 获取当前选中的 index
|
||||
const selectedIndex = this.images.findIndex(e => e.selected);
|
||||
|
||||
if (this.isShift) {
|
||||
// 获取 selectedIndex 与 index 之间的所有图片
|
||||
let selectedImages = this.images.slice(Math.min(selectedIndex, index), Math.max(selectedIndex, index) + 1);
|
||||
selectedImages.map(e => e.selected = true)
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isCtrl) {
|
||||
this.images[index].selected = !this.images[index].selected;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectImageIndex.length > 1) {
|
||||
this.images.map((e,i) => i != index ? e.selected = false : e.selected = true)
|
||||
return;
|
||||
}
|
||||
|
||||
this.images.map((e,i) => i != index ? e.selected = false : '')
|
||||
this.images[index].selected = !this.images[index].selected
|
||||
},
|
||||
|
||||
fileChecked() {
|
||||
let typedFiles = this.images[this.editingImageIndex];
|
||||
let typedFiles = this.images.filter(e => e.selected)
|
||||
|
||||
if (callback !== null) {
|
||||
callback(typedFiles);
|
||||
|
|
@ -319,7 +362,10 @@
|
|||
this.$confirm('是否要删除选中文件', '提示', {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.images.splice(this.editingImageIndex, 1);
|
||||
// 删除选中的文件
|
||||
console.log(this.selectImageIndex);
|
||||
|
||||
// this.images.splice(this.selectImageIndex, 1);
|
||||
this.$message({type: 'success',message: '删除成功!'});
|
||||
}).catch(_=>{});
|
||||
},
|
||||
|
|
@ -340,13 +386,34 @@
|
|||
}
|
||||
},
|
||||
|
||||
selectAll() {
|
||||
// 获取 this.images 中的 selected 是否全部为 true
|
||||
const isAllSelected = this.images.every(e => e.selected);
|
||||
this.images.map(e => e.selected = !isAllSelected)
|
||||
},
|
||||
|
||||
downloadImages() {
|
||||
// 获取选中的图片
|
||||
const selectedImages = this.images.filter(e => e.selected);
|
||||
// 创建 a 标签
|
||||
selectedImages.forEach(e => {
|
||||
const a = document.createElement('a');
|
||||
// 设置 a 标签的 href 属性
|
||||
a.href = e.origin_url;
|
||||
// 设置 a 标签的 download 属性
|
||||
a.download = e.name;
|
||||
// 触发 a 标签的 click 事件
|
||||
a.click();
|
||||
});
|
||||
},
|
||||
|
||||
openInputBox(type, node, data) {
|
||||
this.$prompt('', type == 'addFolder' ? '新建文件夹' : '重命名', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPattern: /^.+$/,
|
||||
closeOnClickModal: false,
|
||||
inputValue: type == 'image' ? this.images[this.editingImageIndex].name : (type == 'renameFolder' ? data.name : '新建文件夹'),
|
||||
inputValue: type == 'image' ? this.images[this.selectImageIndex].name : (type == 'renameFolder' ? data.name : '新建文件夹'),
|
||||
inputErrorMessage: '不能为空'
|
||||
}).then(({ value }) => {
|
||||
|
||||
|
|
@ -396,10 +463,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
// 在实例初始化之后,组件属性计算之前,如data属性等
|
||||
beforeCreate () {
|
||||
},
|
||||
// 在实例创建完成后被立即同步调用
|
||||
|
||||
created () {
|
||||
const defaultkeyarr = sessionStorage.getItem('defaultkeyarr');
|
||||
|
||||
|
|
@ -407,22 +471,31 @@
|
|||
// this.defaultkeyarr = defaultkeyarr.split(',');
|
||||
}
|
||||
},
|
||||
// 在挂载开始之前被调用:相关的 render 函数首次被调用
|
||||
beforeMount () {
|
||||
},
|
||||
// 实例被挂载后调用
|
||||
mounted () {
|
||||
// 获取键盘事件 是否按住 shift/ctrl 键 兼容 mac 和 windows
|
||||
document.addEventListener('keydown', (e) => {
|
||||
this.isShift = e.shiftKey;
|
||||
this.isCtrl = e.ctrlKey || e.metaKey;
|
||||
})
|
||||
|
||||
// 获取键盘事件 是否松开 shift/ctrl 键
|
||||
document.addEventListener('keyup', (e) => {
|
||||
this.isShift = e.shiftKey;
|
||||
this.isCtrl = e.ctrlKey || e.metaKey;
|
||||
})
|
||||
|
||||
// 判断鼠标是否点击 .image-list 元素
|
||||
document.addEventListener('click', (e) => {
|
||||
if (this.isBatchSelect) return;
|
||||
const targets = ['filemanager-navbar', 'content-center']
|
||||
if (targets.indexOf(e.target.className) > -1) {
|
||||
this.selectImageIndex = [];
|
||||
this.images.map(e => e.selected = false)
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
$(document).ready(function() {
|
||||
$(document).on('click', function (e) {
|
||||
if ($(e.target).closest('.content-center .image-list, .content-head, .content-footer, .el-message-box').length === 0) {
|
||||
app.editingImageIndex = null;
|
||||
app.images.map(e => e.selected = false)
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
window.axios = require('axios');
|
||||
|
||||
const token = document.querySelector('meta[name="csrf-token"]').content;
|
||||
const base = document.querySelector('base').href;
|
||||
|
||||
const instance = axios.create({
|
||||
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
|
||||
headers: {'X-CSRF-TOKEN': token},
|
||||
});
|
||||
|
||||
axios.defaults.timeout = 5000; // 请求超时
|
||||
// axios.defaults.baseURL = process.env.NODE_ENV == 'production' ? process.env.VUE_APP_BASE_URL + '/' : '/';
|
||||
// console.log(process.env.VUE_APP_BASE_URL)
|
||||
console.log($('base').attr('href'))
|
||||
axios.defaults.baseURL = $('base').attr('href');
|
||||
axios.defaults.baseURL = base;
|
||||
export default {
|
||||
/**
|
||||
* get 请求
|
||||
|
|
|
|||
Loading…
Reference in New Issue