fix 添加附件管理器,代码生成支持附件选择器,人员选择器,部门选择器,移除旧版大文件上传,大文件上传与附件管理器合并
This commit is contained in:
parent
08fa5817f3
commit
bf3bec9b63
547
package-lock.json
generated
547
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@
|
||||
"echarts": "^5.5.0",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"element-plus": "^2.6.3",
|
||||
"element-plus": "^2.8.7",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsplumb": "^2.15.6",
|
||||
"lodash": "^4.17.21",
|
||||
@ -57,8 +57,8 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.72.0",
|
||||
"sass-loader": "^13.0.2",
|
||||
"sass": "^1.80.7",
|
||||
"sass-loader": "^16.0.3",
|
||||
"typescript": "^5.4.2",
|
||||
"vite": "5.4.6",
|
||||
"vite-plugin-cdn-import": "^0.3.5",
|
||||
|
@ -937,21 +937,18 @@
|
||||
|
||||
if (!_this.listEnd && !this.isLoadingData) {
|
||||
this.isLoadingData = true;
|
||||
var url = editor.getActionUrl(editor.getOpt('imageManagerActionName')),
|
||||
isJsonp = utils.isCrossDomainUrl(url);
|
||||
var url = editor.getActionUrl(editor.getOpt('imageManagerActionName'));
|
||||
ajax.request(url, {
|
||||
'timeout': 100000,
|
||||
'dataType': isJsonp ? 'jsonp' : '',
|
||||
'headers': editor.options.serverHeaders || {},
|
||||
'data': utils.extend({
|
||||
timeout: 100000,
|
||||
data: utils.extend({
|
||||
start: this.listIndex,
|
||||
size: this.listSize
|
||||
}, editor.queryCommandValue('serverparam')),
|
||||
'method': 'get',
|
||||
headers: editor.options.serverHeaders || {},
|
||||
method: 'get',
|
||||
'onsuccess': function (r) {
|
||||
try {
|
||||
var json = isJsonp ? r : eval('(' + r.responseText + ')');
|
||||
json = editor.options.serverResponsePrepare(json);
|
||||
var json = eval('(' + r.responseText + ')');
|
||||
if (json.state === 'SUCCESS') {
|
||||
_this.pushData(json.list);
|
||||
_this.listIndex = parseInt(json.start) + parseInt(json.list.length);
|
||||
|
@ -5,7 +5,6 @@
|
||||
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
|
||||
<CloseFull v-if="!themeConfig.isLockScreen" />
|
||||
</el-config-provider>
|
||||
<BigUploader></BigUploader>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -20,11 +19,10 @@ import setIntroduction from '/@/utils/setIconfont';
|
||||
import LockScreen from '/@/layout/lockScreen/index.vue';
|
||||
import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
|
||||
import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
|
||||
import BigUploader from '/@/components/bigUploader/index.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'app',
|
||||
components: { LockScreen, Setings, CloseFull,BigUploader },
|
||||
components: { LockScreen, Setings, CloseFull},
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const setingsRef = ref();
|
||||
|
90
src/api/system/sysAttachment.ts
Normal file
90
src/api/system/sysAttachment.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import request from '/@/utils/request'
|
||||
import axios, {AxiosProgressEvent, CancelTokenSource} from "axios";
|
||||
import {baseURL} from "/@/utils/gfast";
|
||||
// 查询附件管理列表
|
||||
export function listSysAttachment(query:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysAttachment/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询附件管理详细
|
||||
export function getSysAttachment(id:number) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysAttachment/get',
|
||||
method: 'get',
|
||||
params: {
|
||||
id: id.toString()
|
||||
}
|
||||
})
|
||||
}
|
||||
// 新增附件管理
|
||||
export function addSysAttachment(data:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysAttachment/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 修改附件管理
|
||||
export function updateSysAttachment(data:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysAttachment/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 删除附件管理
|
||||
export function delSysAttachment(ids:number[]) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysAttachment/delete',
|
||||
method: 'delete',
|
||||
data:{
|
||||
ids:ids
|
||||
}
|
||||
})
|
||||
}
|
||||
// 附件管理状态修改
|
||||
export function changeSysAttachmentStatus(id:number,status:boolean) {
|
||||
const data = {
|
||||
id,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/api/v1/system/sysAttachment/changeStatus',
|
||||
method: 'put',
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
// 检查文件分片
|
||||
export function checkMultipart(data:Object) {
|
||||
return request({
|
||||
url: '/api/v1/system/upload/checkMultipart',
|
||||
method: 'post',
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
type Callback = (result: AxiosProgressEvent) => void;
|
||||
//上传分片
|
||||
export function uploadPart(data:Object,progress:Callback,cancelToken:CancelTokenSource) {
|
||||
/*return axios.post(baseURL+'/api/v1/system/upload/uploadPart', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})*/
|
||||
return axios({
|
||||
url: baseURL+'/api/v1/system/upload/uploadPart',
|
||||
method: 'post',
|
||||
data:data,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
cancelToken:cancelToken.token,
|
||||
onUploadProgress: function (e) {
|
||||
progress(e)
|
||||
}
|
||||
})
|
||||
}
|
@ -1,371 +0,0 @@
|
||||
<template>
|
||||
|
||||
<div class="uploader-min" v-show="!panelShow" @click="openHandle">
|
||||
<el-icon style="vertical-align: middle"><ele-DArrowLeft/></el-icon>
|
||||
</div>
|
||||
|
||||
<div class="uploader-container" v-show="panelShow">
|
||||
<div class="header">
|
||||
<span>文件列表</span>
|
||||
<span class="shrink" @click="closeHandle">
|
||||
<el-icon style="vertical-align: middle"><ele-DArrowRight /></el-icon>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<uploader :options="options"
|
||||
:file-status-text="handleFileStatusText"
|
||||
:autoStart="false"
|
||||
@file-added="onFileAdded"
|
||||
@file-success="onFileSuccess"
|
||||
@file-progress="onFileProgress"
|
||||
@file-error="onFileError"
|
||||
class="uploader-example">
|
||||
|
||||
<uploader-unsupport></uploader-unsupport>
|
||||
|
||||
<div v-show="false">
|
||||
<uploader-btn ref="uploadBtn" :attrs="all">选择文件</uploader-btn>
|
||||
<uploader-btn :attrs="image">选择图片</uploader-btn>
|
||||
<uploader-btn :directory="true">选择文件夹</uploader-btn>
|
||||
</div>
|
||||
|
||||
<uploader-list>
|
||||
<template v-slot="props">
|
||||
<ul class="file-list">
|
||||
<li v-for="file in props.fileList" :key="file.id">
|
||||
<uploader-file :ref="'file_' + file.id" :class="'file_' + file.id" :file="file" :list="true"></uploader-file>
|
||||
</li>
|
||||
<div class="no-file" v-if="!props.fileList.length">暂无待上传文件</div>
|
||||
</ul>
|
||||
</template>
|
||||
</uploader-list>
|
||||
</uploader>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SparkMD5 from 'spark-md5';
|
||||
import axios from "axios"
|
||||
import {bigUpload} from "/@/stores/bigUpload";
|
||||
import {getToken} from "/@/utils/gfast"
|
||||
const stores = bigUpload();
|
||||
|
||||
const baseURL = import.meta.env.VITE_API_URL
|
||||
const instance = axios.create({
|
||||
baseURL: baseURL,
|
||||
timeout: 1000 * 30,
|
||||
});
|
||||
const acceptConfig = {
|
||||
image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
|
||||
video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
|
||||
audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
|
||||
document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx'],
|
||||
all() {
|
||||
return [...this.image, ...this.video, ...this.audio, ...this.document]
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: "bigUploader",
|
||||
data () {
|
||||
return {
|
||||
options: {
|
||||
target: baseURL + 'api/v1/system/bigUpload/upload', // 目标上传 URL
|
||||
chunkSize: '2048000', // 分片大小
|
||||
fileParameterName: 'upfile', //上传文件时文件的参数名,默认 file
|
||||
maxChunkRetries: 3, //最大自动失败重试上传次数
|
||||
testChunks: true, //是否开启服务器分片校验
|
||||
// 服务器分片校验函数,秒传及断点续传基础
|
||||
// 可选的函数用于根据 XHR 响应内容检测每个块是否上传成功了,传入的参数是:Uploader.Chunk 实例以及请求响应信息。
|
||||
// 这样就没必要上传(测试)所有的块了
|
||||
checkChunkUploadedByResponse: function (chunk, message) {
|
||||
try {
|
||||
let objMessage = JSON.parse(message)
|
||||
const {code, data} = objMessage
|
||||
if (code === 0) {
|
||||
if (data.skipUpload) {
|
||||
return true
|
||||
}
|
||||
return (data.uploaded || []).indexOf(chunk.offset + 1) >= 0
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
headers: {
|
||||
Authorization:null
|
||||
},
|
||||
query: (file, chunk) => {
|
||||
return {
|
||||
...file.params,
|
||||
}
|
||||
},
|
||||
},
|
||||
image: {
|
||||
accept: 'image/*'
|
||||
},
|
||||
all: {
|
||||
accept: acceptConfig.all()
|
||||
},
|
||||
statusText: {
|
||||
success: '成功了',
|
||||
error: '出错了',
|
||||
uploading: '上传中',
|
||||
paused: '暂停中',
|
||||
waiting: '等待中'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(()=> {
|
||||
this.mittBus.on("bigUploader.uploadFile", this.uploadFile)
|
||||
})
|
||||
},
|
||||
computed:{
|
||||
panelShow() {
|
||||
return stores.panelShow
|
||||
},
|
||||
uploadBtn () {
|
||||
return this.$refs.uploadBtn && this.$refs.uploadBtn.btn
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openHandle () {
|
||||
stores.setPanelShow(true)
|
||||
},
|
||||
closeHandle() {
|
||||
stores.setPanelShow(false)
|
||||
},
|
||||
uploadFile () {
|
||||
// console.log("uploadFile")
|
||||
if(!this.options.headers.Authorization){
|
||||
this.options.headers.Authorization = "Bearer "+getToken()
|
||||
}
|
||||
if (this.uploadBtn) {
|
||||
this.uploadBtn.click()
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
handleFileStatusText (status, response) {
|
||||
//console.log(status, response)
|
||||
if (status === "success") {
|
||||
const {code, data} = response
|
||||
if (code === 0 && data.needMerge === true) {
|
||||
return "文件合并中..."
|
||||
} else {
|
||||
return this.statusText[status]
|
||||
}
|
||||
} else {
|
||||
return this.statusText[status]
|
||||
}
|
||||
},
|
||||
|
||||
//上传过程中出错了
|
||||
onFileError(rootFile, file, response, chunk) {
|
||||
console.error(rootFile, file, response, chunk)
|
||||
},
|
||||
|
||||
// 设置自定义状态
|
||||
setStatus (id, text) {
|
||||
this.$nextTick(()=> {
|
||||
const el = this.$refs['file_'+ id][0].$el.getElementsByClassName("uploader-file-status")[0].getElementsByTagName("span")[0]
|
||||
const parent = el.parentNode
|
||||
const para = document.createElement("span")
|
||||
para.appendChild(document.createTextNode(text));
|
||||
para.className = "para"
|
||||
el.style.display = 'none'
|
||||
parent.appendChild(para)
|
||||
})
|
||||
},
|
||||
// 删除自定义状态
|
||||
removeStatus (id) {
|
||||
try{
|
||||
const els = this.$refs['file_'+ id][0].$el.getElementsByClassName("uploader-file-status")[0].getElementsByClassName("para")
|
||||
if (els && els.length > 0) {
|
||||
const parent = els[0].parentNode
|
||||
for (let i = 0; i < els.length; i++) {
|
||||
parent.removeChild(els[i])
|
||||
}
|
||||
}
|
||||
const firstSpan = this.$refs['file_'+ id][0].$el.getElementsByClassName("uploader-file-status")[0].getElementsByTagName("span")[0]
|
||||
firstSpan.style.display = ''
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
文件上传成功事件
|
||||
第一个参数 rootFile 就是成功上传的文件所属的根 Uploader.File 对象,它应该包含或者等于成功上传文件;
|
||||
第二个参数 file 就是当前成功的 Uploader.File 对象本身;
|
||||
第三个参数 message 就是服务端响应内容,永远都是字符串;
|
||||
第四个参数 chunk 就是 Uploader.Chunk 实例, 它就是该文件的最后一个块实例,如果你想得到请求响应码的话,chunk.xhr.status 就是。
|
||||
*/
|
||||
onFileSuccess(rootFile, file, response, chunk) {
|
||||
try{
|
||||
// console.log(rootFile, file, response, chunk)
|
||||
let res = JSON.parse(response);
|
||||
// console.log(res)
|
||||
const {code, data} = res
|
||||
if (code === 0) {
|
||||
// 如果服务端返回需要合并
|
||||
if (data.needMerge) {
|
||||
// 文件合并操作
|
||||
instance.post(baseURL+'api/v1/system/bigUpload/uploadMerge', {
|
||||
identifier: data.identifier,
|
||||
totalChunks: data.totalChunks,
|
||||
totalSize: data.totalSize,
|
||||
filename: data.filename,
|
||||
token:getToken()
|
||||
}).then(res=> {
|
||||
const {status, data} = res
|
||||
return data
|
||||
}).then(res => {
|
||||
let {code, data} = res
|
||||
if(code === 0) {
|
||||
this.setStatus(file.id, this.statusText["success"])
|
||||
this.mittBus.emit("bigUploader.uploadFileSuccess", {...data, fileType: file.fileType})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 不需要合并
|
||||
// 秒传或普通上传
|
||||
this.mittBus.emit("bigUploader.uploadFileSuccess", {...data, fileType: file.fileType})
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
// 一个文件在上传中
|
||||
onFileProgress(rootFile, file, chunk) {
|
||||
console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
|
||||
},
|
||||
// 这个事件一般用作文件校验,如果说返回了 false,那么这个文件就会被忽略,不会添加到文件上传列表中。
|
||||
onFileAdded(file) {
|
||||
this.computeMD5(file);
|
||||
// 2022/1/10 将额外的参数赋值到每个文件上,解决了不同文件使用不同params的需求
|
||||
file.params = this.params
|
||||
|
||||
this.openHandle()
|
||||
},
|
||||
computeMD5(file) {
|
||||
let fileReader = new FileReader();
|
||||
let time = new Date().getTime();
|
||||
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
|
||||
// 分片序号
|
||||
let currentChunk = 0;
|
||||
// 分片大小
|
||||
const chunkSize = 10 * 1024 * 1000;
|
||||
// 分片总数量
|
||||
let chunks = Math.ceil(file.size / chunkSize);
|
||||
let spark = new SparkMD5.ArrayBuffer();
|
||||
|
||||
this.setStatus(file.id, "md5计算中")
|
||||
file.pause();
|
||||
|
||||
loadNext();
|
||||
|
||||
fileReader.onload = (e => {
|
||||
spark.append(e.target.result);
|
||||
|
||||
if (currentChunk < chunks) {
|
||||
currentChunk++;
|
||||
loadNext();
|
||||
// 实时展示MD5的计算进度
|
||||
this.$nextTick(() => {
|
||||
console.log('校验MD5 '+ ((currentChunk/chunks)*100).toFixed(0)+'%')
|
||||
})
|
||||
} else {
|
||||
let md5 = spark.end();
|
||||
this.computeMD5Success(md5, file);
|
||||
this.removeStatus(file.id)
|
||||
console.log(`MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`);
|
||||
}
|
||||
});
|
||||
|
||||
fileReader.onerror = function () {
|
||||
console.error(`文件${file.name}读取出错,请检查该文件`)
|
||||
file.cancel();
|
||||
};
|
||||
|
||||
function loadNext() {
|
||||
let start = currentChunk * chunkSize;
|
||||
let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
|
||||
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
|
||||
}
|
||||
},
|
||||
// md5 计算完毕
|
||||
computeMD5Success(md5, file) {
|
||||
file.uniqueIdentifier = md5;
|
||||
file.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.uploader-min {
|
||||
background-color:#fff;
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom:10px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
|
||||
padding:15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.uploader-container {
|
||||
background-color:#fff;
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom:10px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color:#fff;
|
||||
height:35px;
|
||||
line-height: 35px;
|
||||
display: flex;
|
||||
padding: 0 10px;
|
||||
justify-content:space-between;
|
||||
.shrink {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.box {
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.uploader-example {
|
||||
width: 550px;
|
||||
padding: 15px;
|
||||
font-size: 12px;
|
||||
|
||||
|
||||
}
|
||||
.uploader-example .uploader-btn {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.uploader-example .uploader-list {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
56
src/components/selectDept/index.vue
Normal file
56
src/components/selectDept/index.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<el-cascader
|
||||
filterable
|
||||
:options="deptData"
|
||||
:props="{ checkStrictly: true,emitPath: false, value: 'deptId', label: 'deptName',multiple: multiple }"
|
||||
placeholder="请选择部门"
|
||||
clearable
|
||||
class="w100"
|
||||
v-model="deptIds"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.deptName }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {getDeptTree} from "/@/api/system/user";
|
||||
|
||||
defineOptions({ name: "selectDept"})
|
||||
const props = defineProps({
|
||||
multiple:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
},
|
||||
modelValue:{
|
||||
type:[Array,Number],
|
||||
default:()=>[]
|
||||
}
|
||||
})
|
||||
const deptIds = computed({
|
||||
get: () => {
|
||||
if(props.multiple){
|
||||
return props.modelValue as number[]
|
||||
}else{
|
||||
return props.modelValue instanceof Array ? props.modelValue[0] : props.modelValue
|
||||
}
|
||||
},
|
||||
set: (value) => {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const deptData = ref([])
|
||||
onMounted(()=>{
|
||||
getDeptTree().then((res)=>{
|
||||
deptData.value = res.data.deps
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@ -1,5 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="deptUser.length > 0">
|
||||
<el-tag closable :disable-transitions="false" class="u-m-r-10" v-for="(item,index) in deptUser" :key="'sel_'+index" @close="handleClose(index)" >{{item.userNickname}}</el-tag>
|
||||
</div>
|
||||
<el-button
|
||||
style="padding-left: 10px;"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleSelectUser" >请选择</el-button>
|
||||
<el-dialog title="选择用户" v-model="visible" width="80%" top="5vh" append-to-body :close-on-click-modal="false">
|
||||
<div class="system-user-container">
|
||||
<el-row :gutter="10" style="width: 100%;">
|
||||
@ -61,7 +69,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<UserList ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="param" :multiple="multiple" @ok="handleSelectUserOk"/>
|
||||
<UserList ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="param" @ok="handleSelectUserOk"/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@ -97,11 +105,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {toRefs, reactive, ref, defineComponent, watch, getCurrentInstance, nextTick, computed} from 'vue';
|
||||
import {toRefs, reactive, ref, defineComponent, watch, getCurrentInstance, nextTick, computed, onMounted} from 'vue';
|
||||
import {ElTree,FormInstance} from 'element-plus';
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import UserList from './component/userList.vue';
|
||||
import {getDeptTree} from '/@/api/system/user/index';
|
||||
import {getDeptTree, getUserByIds} from '/@/api/system/user/index';
|
||||
|
||||
interface QueryParam {
|
||||
ids:number[];
|
||||
@ -122,14 +130,14 @@ export default defineComponent({
|
||||
props:{
|
||||
multiple:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
default:true
|
||||
},
|
||||
selectedUsers:{
|
||||
type:Array,
|
||||
modelValue:{
|
||||
type:Array<number>,
|
||||
default:()=>[]
|
||||
}
|
||||
},
|
||||
emits:['selectUser','okBack'],
|
||||
emits:['update:modelValue'],
|
||||
setup(prop,{emit}) {
|
||||
const selectedUsersPage = ref(1)
|
||||
const visible = ref(false)
|
||||
@ -140,6 +148,39 @@ export default defineComponent({
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
const search = Search
|
||||
const deptUser = ref<any>([]);
|
||||
const selectedUsers = computed({
|
||||
get: ()=>{
|
||||
return prop.modelValue??[]
|
||||
},
|
||||
set:(val:any[])=>{
|
||||
emit('update:modelValue',val)
|
||||
}
|
||||
})
|
||||
const selectedUserInfo = computed(()=>{
|
||||
let start = (selectedUsersPage.value-1)*10
|
||||
let end = start+10
|
||||
return deptUser.value.slice(start,end)
|
||||
});
|
||||
const initData = ()=>{
|
||||
if(prop.modelValue&&prop.modelValue.length>0){
|
||||
getUserByIds({ids:prop.modelValue}).then((res:any)=>{
|
||||
if(res.code === 0){
|
||||
deptUser.value = res.data.userList;
|
||||
}
|
||||
});
|
||||
}else{
|
||||
deptUser.value = []
|
||||
}
|
||||
};
|
||||
onMounted(()=>{
|
||||
initData()
|
||||
})
|
||||
watch(selectedUsers,(value, oldValue)=>{
|
||||
if(value!=oldValue){
|
||||
initData()
|
||||
}
|
||||
})
|
||||
const state = reactive<QueryParam>({
|
||||
ids:[],
|
||||
deptProps:{
|
||||
@ -156,16 +197,6 @@ export default defineComponent({
|
||||
dateRange:[]
|
||||
},
|
||||
});
|
||||
const selectedUserInfo = computed({
|
||||
get:()=>{
|
||||
let start = (selectedUsersPage.value-1)*10
|
||||
let end = start+10
|
||||
return prop.selectedUsers.slice(start,end)
|
||||
} ,
|
||||
set:(v)=>{
|
||||
emit("selectUser",v);
|
||||
}
|
||||
});
|
||||
const getUserList = ()=>{
|
||||
userListRef.value.setUserList();
|
||||
};
|
||||
@ -202,21 +233,35 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
const handleSelectUserOk = (row:any)=>{
|
||||
selectedUserInfo.value = [...selectedUserInfo.value,row]
|
||||
if(!prop.multiple){
|
||||
selectedUsers.value = [row.id]
|
||||
}else{
|
||||
if(!selectedUsers.value.includes(row.id)){
|
||||
selectedUsers.value = [...selectedUsers.value!,row.id]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
const goBack = ()=>{
|
||||
visible.value = false;
|
||||
emit("okBack");
|
||||
}
|
||||
const removeAll = ()=>{
|
||||
selectedUserInfo.value = []
|
||||
selectedUsers.value = []
|
||||
}
|
||||
const remove = (index:number)=>{
|
||||
index = (selectedUsersPage.value-1)*10+index
|
||||
let newSel:any = [...selectedUserInfo.value]
|
||||
selectedUserInfo.value = []
|
||||
let newSel:any = [...selectedUsers.value!]
|
||||
selectedUsers.value = []
|
||||
newSel.splice(index,1)
|
||||
selectedUserInfo.value = newSel
|
||||
selectedUsers.value = newSel
|
||||
}
|
||||
const handleSelectUser = ()=>{
|
||||
openDialog()
|
||||
}
|
||||
const handleClose = (index:number)=>{
|
||||
let newSel:any = [...selectedUsers.value!]
|
||||
newSel.splice(index, 1);
|
||||
selectedUsers.value = newSel
|
||||
}
|
||||
return {
|
||||
selectedUsersPage,
|
||||
@ -228,6 +273,8 @@ export default defineComponent({
|
||||
treeRef,
|
||||
search,
|
||||
sys_user_sex,
|
||||
selectedUsers,
|
||||
deptUser,
|
||||
selectedUserInfo,
|
||||
openDialog,
|
||||
getUserList,
|
||||
@ -237,8 +284,15 @@ export default defineComponent({
|
||||
goBack,
|
||||
removeAll,
|
||||
remove,
|
||||
handleClose,
|
||||
handleSelectUser,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.u-m-r-10{
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
|
@ -30,7 +30,7 @@ export default defineComponent({
|
||||
initialFrameHeight: 400,
|
||||
maximumWords: 5000,
|
||||
topOffset: 80,
|
||||
zIndex:2020
|
||||
zIndex:2050
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -41,6 +41,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
},
|
||||
emits:['update:modelValue'],
|
||||
setup(props,{emit}){
|
||||
const config = Object.assign({
|
||||
elementPathEnabled: false,
|
||||
@ -59,7 +60,7 @@ export default defineComponent({
|
||||
return props.modelValue
|
||||
},
|
||||
set:(newVal)=>{
|
||||
emit('setEditContent',newVal)
|
||||
emit('update:modelValue',newVal)
|
||||
}
|
||||
})
|
||||
return {
|
||||
|
273
src/components/uploadBigFile/index.vue
Normal file
273
src/components/uploadBigFile/index.vue
Normal file
@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<el-upload
|
||||
ref="upBigFileRef"
|
||||
v-model:file-list="dataFileList"
|
||||
class="upload-demo"
|
||||
:multiple="multiple"
|
||||
:drag="drag"
|
||||
:on-remove="handleRemove"
|
||||
:before-remove="beforeRemove"
|
||||
:limit="limit"
|
||||
:on-exceed="handleExceed"
|
||||
:on-change="handleChange"
|
||||
:on-preview="handlePreview"
|
||||
:http-request="handleRequest"
|
||||
:disabled="uploadStatus!==0"
|
||||
>
|
||||
<template v-if="uploadStatus===0">
|
||||
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
拖拽文件至此 或<em>点击上传</em>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="uploadStatus===1">
|
||||
<el-icon class="el-icon--upload"><ele-Loading /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
文件解析中...{{uploadStatusContent}}
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="uploadStatus===2">
|
||||
<el-icon class="el-icon--upload"><ele-Loading /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
文件上传中...({{progress}}%)文件大小:{{fileFormatSize}}
|
||||
</div>
|
||||
</template>
|
||||
<template #tip>
|
||||
<div class="progress-show" v-if="uploadStatus===2">
|
||||
<el-progress :percentage="progress" />
|
||||
</div>
|
||||
<div class="el-upload__tip">
|
||||
大文件上传:支持分片上传,断点续传及秒传功能
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SparkMD5 from 'spark-md5';
|
||||
import {computed, getCurrentInstance, reactive, ref} from "vue";
|
||||
import type {UploadFile, UploadProps, UploadRawFile, UploadRequestOptions, UploadUserFile} from 'element-plus'
|
||||
import { ElMessage ,ElMessageBox} from 'element-plus'
|
||||
import {getToken} from "/@/utils/gfast";
|
||||
import _ from 'lodash'
|
||||
import {checkMultipart, uploadPart} from "/@/api/system/sysAttachment";
|
||||
import axios, {AxiosProgressEvent} from "axios";
|
||||
defineOptions({ name: "uploadBigFile"})
|
||||
const props = defineProps({
|
||||
name: { type : String, default : 'file' },//上传文件类型
|
||||
method: { type : String, default : 'post' },//设置上传请求方法
|
||||
multiple: { type : Boolean, default : true },//是否支持多选文件
|
||||
showFileList: { type : Boolean, default : true },//是否显示已上传文件列表
|
||||
drag: { type : Boolean, default : true },//是否启用拖拽上传
|
||||
disabled: { type : Boolean, default : false },//是否禁止
|
||||
listType: { type : String, default : 'picture-card' },//
|
||||
limit: { type : Number, default : 5 },//上传最大数量
|
||||
modelValue:{
|
||||
type:Array,
|
||||
default:function(){
|
||||
return []
|
||||
}
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const upBigFileRef = ref()
|
||||
const uploadStatus = ref(0); // 上传状态 0等待上传 1解析中 2上传中 3已取消
|
||||
const uploadStatusContent= ref('')
|
||||
const progress = ref(0)
|
||||
const fileFormatSize = ref('0byte')
|
||||
let uploadedFile:Array<any> = [] ;
|
||||
const {proxy} = <any>getCurrentInstance();
|
||||
const dataFileList = computed({
|
||||
get: () => {
|
||||
let value:Array<UploadUserFile> = props.modelValue as UploadUserFile[]|| [];
|
||||
value.map((item: UploadUserFile)=>{
|
||||
if(item.url){
|
||||
item.url = proxy.getUpFileUrl(item.url)
|
||||
}
|
||||
return item
|
||||
})
|
||||
uploadedFile = _.cloneDeep(value)
|
||||
return value
|
||||
},
|
||||
set: val => {
|
||||
emit('update:modelValue', val)
|
||||
}
|
||||
});
|
||||
const handleRequest = (options: UploadRequestOptions) => {}
|
||||
const cancelToken = axios.CancelToken.source();
|
||||
const handleChange: UploadProps['onChange'] = async (uploadFile: UploadFile) => {
|
||||
const {name,size,raw:file} = uploadFile
|
||||
fileFormatSize.value = formatFileSize(size as number)
|
||||
try{
|
||||
//计算文件md5
|
||||
uploadStatus.value = 1 //文件解析中
|
||||
const md5 = await calculateMD5(file as File)
|
||||
uploadStatus.value = 2
|
||||
let currentChunkIndex = 1
|
||||
//分片大小
|
||||
const chunkSize = 1024 * 1024 * 2; // 2M
|
||||
//计算分片数量
|
||||
const shards = Array.from({length: Math.ceil(size as number / chunkSize)}, (_, index) => index+1);
|
||||
//检查分片
|
||||
const checkPartRes:any = await checkMultipart({
|
||||
fileName: name,
|
||||
size: size,
|
||||
md5: md5,
|
||||
shardsCount: shards.length,
|
||||
})
|
||||
// 已存在(秒传)
|
||||
if (!checkPartRes.data.waitUploadIndex || checkPartRes.data.waitUploadIndex.length == 0) {
|
||||
upOver(checkPartRes.data.attachment,uploadFile)
|
||||
return;
|
||||
}
|
||||
// 断点续传,过滤掉已上传成功的分片文件
|
||||
const upShards = shards.filter((shard) => checkPartRes.data.waitUploadIndex.includes(shard));
|
||||
if (upShards.length == 0) {
|
||||
upOver(checkPartRes.data.attachment,uploadFile)
|
||||
return;
|
||||
}
|
||||
let uploadedSize = (shards.length-upShards.length-1)*chunkSize;
|
||||
for (const index of upShards) {
|
||||
if (uploadStatus.value == 3) {
|
||||
break;
|
||||
}
|
||||
let start = (index-1) * chunkSize;
|
||||
let end = index * chunkSize;
|
||||
uploadedSize+=chunkSize
|
||||
const progressFn = (result: AxiosProgressEvent)=>{
|
||||
// 实时展示上传进度
|
||||
progress.value = Math.round((uploadedSize+result.loaded)/size!*100);
|
||||
}
|
||||
const res = await uploadPart({
|
||||
fileName: name,
|
||||
size: size,
|
||||
md5: md5,
|
||||
shardsCount: shards.length,
|
||||
index: index,
|
||||
file: file!.slice(start, end),
|
||||
token:getToken(),
|
||||
},progressFn,cancelToken);
|
||||
currentChunkIndex = index;
|
||||
if (res.data.data.finish){
|
||||
upOver(res.data.data.attachment,uploadFile)
|
||||
}
|
||||
}
|
||||
}catch (e:any){
|
||||
if(e.code==="ERR_CANCELED"){
|
||||
ElMessage.error("已关闭上传,下次上传将继续从断点处上传")
|
||||
}else{
|
||||
console.log('error:',e)
|
||||
}
|
||||
clearUpStatus();
|
||||
}
|
||||
};
|
||||
const upOver = (attachment:any,uploadFile: UploadFile)=>{
|
||||
uploadedFile=uploadedFile.filter((item:UploadUserFile)=>{
|
||||
return item.raw?.uid != uploadFile.raw?.uid
|
||||
})
|
||||
uploadedFile.push({
|
||||
name:attachment.name,
|
||||
url:attachment.path,
|
||||
fullUrl:attachment.fullPath,
|
||||
fileType:attachment.type,
|
||||
size:attachment.size
|
||||
})
|
||||
setDataFileList();
|
||||
clearUpStatus();
|
||||
}
|
||||
const handleExceed = () => {
|
||||
ElMessage.error('最多可上传'+props.limit+'个文件,已超出最大限制数。');
|
||||
}
|
||||
|
||||
const beforeRemove: UploadProps['beforeRemove'] = (uploadFile) => {
|
||||
return ElMessageBox.confirm(
|
||||
`您确定要删除 ${uploadFile.name} ?`,
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(
|
||||
() => true,
|
||||
() => false
|
||||
)
|
||||
};
|
||||
const handleRemove: UploadProps['onRemove'] = (file) => {//移除后
|
||||
uploadedFile.splice(uploadedFile.findIndex((item: any) => item.name === file.name),1)
|
||||
setDataFileList()
|
||||
};
|
||||
const setDataFileList = () => {
|
||||
dataFileList.value = uploadedFile
|
||||
};
|
||||
const handlePreview = (file:UploadUserFile)=>{
|
||||
window.open(file.url)
|
||||
}
|
||||
|
||||
const stopUpBigFile = ()=>{
|
||||
uploadStatus.value = 3
|
||||
cancelToken.cancel()
|
||||
}
|
||||
|
||||
const calculateMD5 = (file: File): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunkSize = 1024 * 1024; // 1 MB
|
||||
const chunks = Math.ceil(file.size / chunkSize);
|
||||
const spark = new SparkMD5.ArrayBuffer();
|
||||
let currentChunk = 0;
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e: ProgressEvent<FileReader>) => {
|
||||
spark.append(e.target!.result as ArrayBuffer);
|
||||
currentChunk++;
|
||||
|
||||
if (currentChunk < chunks) {
|
||||
uploadStatusContent.value = '校验MD5 '+ ((currentChunk/chunks)*100).toFixed(0)+'%'
|
||||
loadNextChunk();
|
||||
} else {
|
||||
// 所有块都读取完毕,生成最终的 MD5 值
|
||||
resolve(spark.end());
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = (error) => {
|
||||
reject(error);
|
||||
};
|
||||
|
||||
function loadNextChunk() {
|
||||
const start = currentChunk * chunkSize;
|
||||
const end = Math.min(start + chunkSize, file.size);
|
||||
const blob = file.slice(start, end);
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
loadNextChunk(); // 开始读取文件的第一个块
|
||||
});
|
||||
}
|
||||
|
||||
const formatFileSize = (bytes:number):string => {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const index = Math.floor(Math.log(bytes) / Math.log(1024)); // 计算索引
|
||||
const value = (bytes / Math.pow(1024, index)).toFixed(2); // 计算具体数值并保留两位小数
|
||||
return `${value} ${sizes[index]}`;
|
||||
}
|
||||
|
||||
const clearUpStatus = ()=>{
|
||||
uploadStatus.value = 0
|
||||
uploadStatusContent.value = ''
|
||||
progress.value = 0
|
||||
fileFormatSize.value = '0byte'
|
||||
}
|
||||
|
||||
defineExpose({stopUpBigFile})
|
||||
</script>
|
||||
<style>
|
||||
.el-upload.is-drag {
|
||||
display: block;
|
||||
width: 200px;height: 200px;
|
||||
}
|
||||
.progress-show{
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
@ -14,6 +14,7 @@
|
||||
:on-success="handleSuccess"
|
||||
:data="dataParam"
|
||||
:on-preview="handlePreview"
|
||||
ref="upFileRef"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
@ -54,9 +55,11 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
},
|
||||
emits:['update:modelValue'],
|
||||
setup(props,{ emit }) {
|
||||
let uploadedFile:Array<any> = [] ;
|
||||
const {proxy} = <any>getCurrentInstance();
|
||||
const upFileRef = ref()
|
||||
const dataParam = reactive({
|
||||
token:getToken(),
|
||||
})
|
||||
@ -73,7 +76,7 @@ export default defineComponent({
|
||||
return value
|
||||
},
|
||||
set: val => {
|
||||
emit('upFileData', val)
|
||||
emit('update:modelValue', val)
|
||||
}
|
||||
});
|
||||
const beforeUpload: UploadProps['beforeUpload'] = () => {
|
||||
@ -130,6 +133,9 @@ export default defineComponent({
|
||||
const handlePreview = (file:UploadUserFile)=>{
|
||||
window.open(file.url)
|
||||
}
|
||||
const stopUpFile = ()=>{
|
||||
upFileRef.value.abort()
|
||||
}
|
||||
return {
|
||||
dataFileList,
|
||||
handleSuccess,
|
||||
@ -139,6 +145,8 @@ export default defineComponent({
|
||||
handleChange,
|
||||
handleExceed,
|
||||
handlePreview,
|
||||
upFileRef,
|
||||
stopUpFile,
|
||||
dataParam
|
||||
};
|
||||
},
|
||||
|
@ -12,6 +12,7 @@
|
||||
:on-exceed = "handleExceed"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
:data="dataParam"
|
||||
ref="upImageRef"
|
||||
>
|
||||
<el-icon><ele-Plus /></el-icon>
|
||||
</el-upload>
|
||||
@ -66,7 +67,9 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
},
|
||||
emits:['update:modelValue'],
|
||||
setup(props,{ emit }) {
|
||||
const upImageRef = ref()
|
||||
const baseURL:string|undefined|boolean = import.meta.env.VITE_API_URL
|
||||
const {proxy} = <any>getCurrentInstance();
|
||||
const dialogImageUrl = ref('')
|
||||
@ -93,7 +96,7 @@ export default defineComponent({
|
||||
return value
|
||||
},
|
||||
set: val => {
|
||||
emit('uploadData', val)
|
||||
emit('update:modelValue', val)
|
||||
}
|
||||
});
|
||||
|
||||
@ -144,7 +147,11 @@ export default defineComponent({
|
||||
const setDataFileList = () => {
|
||||
dataFileList.value = uploadedFile
|
||||
};
|
||||
const stopUpImage = ()=>{
|
||||
upImageRef.value.abort()
|
||||
}
|
||||
return {
|
||||
upImageRef,
|
||||
dataFileList,
|
||||
imageUrl,
|
||||
baseURL,
|
||||
@ -155,6 +162,7 @@ export default defineComponent({
|
||||
handleRemove,
|
||||
handlePictureCardPreview,
|
||||
handleAvatarSuccess,
|
||||
stopUpImage,
|
||||
dataParam
|
||||
};
|
||||
},
|
||||
|
400
src/components/uploadSelector/index.vue
Normal file
400
src/components/uploadSelector/index.vue
Normal file
@ -0,0 +1,400 @@
|
||||
<template>
|
||||
<div class="system-sysAttachment-container">
|
||||
<div class="up-selector" @click="openDialog">
|
||||
<el-icon class="uploader-icon"><ele-Plus /></el-icon>
|
||||
</div>
|
||||
<div class="img-list" v-if="dataFileList.length>0">
|
||||
<el-upload
|
||||
v-model:file-list="dataFileList"
|
||||
ref="upImageRef"
|
||||
:list-type="fileType=='image'?'picture-card':'text'"
|
||||
></el-upload>
|
||||
</div>
|
||||
<el-dialog title="选择文件" v-model="isShowDialog" width="1000px" :close-on-click-modal="false" :destroy-on-close="true">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-sysAttachment-search mb15">
|
||||
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="12" class="form-item">
|
||||
<el-form-item label="文件原始名" prop="name">
|
||||
<el-input
|
||||
v-model="tableData.param.name"
|
||||
placeholder="请输入文件原始名"
|
||||
clearable
|
||||
@keyup.enter.native="sysAttachmentList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="form-item">
|
||||
<el-form-item label="扩展类型" prop="mimeType">
|
||||
<el-input
|
||||
v-model="tableData.param.mimeType"
|
||||
placeholder="请输入扩展类型"
|
||||
clearable
|
||||
@keyup.enter.native="sysAttachmentList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="form-item">
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
clearable style="width: 200px"
|
||||
v-model="tableData.param.createdAt"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" class="form-item">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="sysAttachmentList"><el-icon><ele-Search /></el-icon>搜索</el-button>
|
||||
<el-button @click="resetQuery(queryRef)"><el-icon><ele-Refresh /></el-icon>重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-row :gutter="10" class="mb8" style="margin-top: 8px;">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleAdd('file')"
|
||||
><el-icon><ele-Upload /></el-icon>上传文件</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
color="#626aef"
|
||||
:dark="true"
|
||||
type="success"
|
||||
@click="handleAdd('image')"
|
||||
><el-icon><ele-PictureFilled /></el-icon>上传图片</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
@click="handleAdd('bigFile')"
|
||||
><i class="iconfont icon-shangchuan"></i>上传大文件</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="multiple"
|
||||
@click="handleConfirm()"
|
||||
v-auth="'api/v1/system/sysAttachment/delete'"
|
||||
><el-icon><ele-Delete /></el-icon>确认返回</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-table ref="uploadTableRef" v-loading="loading" :data="tableData.data" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="文件原始名" align="center" prop="name" show-overflow-tooltip
|
||||
min-width="260px"
|
||||
/>
|
||||
<el-table-column label="上传类型" align="center" prop="kind" :formatter="kindFormat"
|
||||
min-width="100px"
|
||||
/>
|
||||
<el-table-column label="本地路径" align="center" prop="path" min-width="100px">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="scope.row.kind=='image'"
|
||||
style="width: 60px; height: 60px"
|
||||
:src="proxy.getUpFileUrl(scope.row.path)"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="[proxy.getUpFileUrl(scope.row.path)]"
|
||||
:initial-index="0"
|
||||
preview-teleported
|
||||
hide-on-click-modal
|
||||
fit="fill"
|
||||
/>
|
||||
<el-image
|
||||
v-else
|
||||
style="width: 60px; height: 60px">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
{{getExt(scope.row.name)}}
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件大小" align="center" prop="size" :formatter="formatFileSize"
|
||||
min-width="100px"
|
||||
/>
|
||||
<el-table-column label="上传时间" align="center" prop="updatedAt"
|
||||
min-width="150px"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.updatedAt, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding" min-width="100px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleSelect(scope.row)"
|
||||
><el-icon><ele-Select /></el-icon>选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="tableData.total>0"
|
||||
:total="tableData.total"
|
||||
v-model:page="tableData.param.pageNum"
|
||||
v-model:limit="tableData.param.pageSize"
|
||||
@pagination="sysAttachmentList"
|
||||
/>
|
||||
</el-card>
|
||||
<ApiV1SystemSysAttachmentEdit
|
||||
ref="editRef"
|
||||
@sysAttachmentList="sysAttachmentList"
|
||||
></ApiV1SystemSysAttachmentEdit>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {toRefs, reactive, ref, computed,getCurrentInstance} from 'vue';
|
||||
import {FormInstance, UploadUserFile} from 'element-plus';
|
||||
import {
|
||||
listSysAttachment,
|
||||
} from "/@/api/system/sysAttachment";
|
||||
import {
|
||||
SysAttachmentTableColumns,
|
||||
SysAttachmentInfoData,
|
||||
SysAttachmentTableDataState,
|
||||
} from "/@/views/system/sysAttachment/list/component/model"
|
||||
import ApiV1SystemSysAttachmentEdit from "/@/views/system/sysAttachment/list/component/edit.vue"
|
||||
defineOptions({ name: "apiV1SystemSysAttachmentList"})
|
||||
const props = defineProps({
|
||||
fileType:{
|
||||
type:String,
|
||||
default:function(){
|
||||
return 'image'
|
||||
}
|
||||
},
|
||||
modelValue:{
|
||||
type:Array,
|
||||
default:function(){
|
||||
return []
|
||||
}
|
||||
},
|
||||
limit:{
|
||||
type:Number,
|
||||
default:function(){
|
||||
return 10
|
||||
}
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const dataFileList = computed({
|
||||
get: () => {
|
||||
let value:Array<UploadUserFile> = props.modelValue as UploadUserFile[]|| []
|
||||
value.map((item: UploadUserFile)=>{
|
||||
if(item.url){
|
||||
item.url = proxy.getUpFileUrl(item.url)
|
||||
}
|
||||
return item
|
||||
})
|
||||
return value
|
||||
},
|
||||
set: val => {
|
||||
console.log('setVal',val)
|
||||
emit('update:modelValue', val)
|
||||
}
|
||||
});
|
||||
const uploadTableRef = ref()
|
||||
const {proxy} = <any>getCurrentInstance()
|
||||
const isShowDialog = ref(false)
|
||||
const loading = ref(false)
|
||||
const queryRef = ref()
|
||||
const editRef = ref();
|
||||
// 是否显示所有搜索选项
|
||||
const showAll = ref(false)
|
||||
// 非单个禁用
|
||||
const single = ref(true)
|
||||
// 非多个禁用
|
||||
const multiple =ref(true)
|
||||
// 字典选项数据
|
||||
const {sys_upload_file_type} = proxy.useDict( 'sys_upload_file_type')
|
||||
const state = reactive<SysAttachmentTableDataState>({
|
||||
ids:[],
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
appId: undefined,
|
||||
drive: undefined,
|
||||
name: undefined,
|
||||
kind: props.fileType=='image'?'image':undefined,
|
||||
mimeType: undefined,
|
||||
status: undefined,
|
||||
createdAt: [],
|
||||
dateRange: []
|
||||
},
|
||||
},
|
||||
});
|
||||
const { tableData } = toRefs(state);
|
||||
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
sysAttachmentList()
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
state.tableData.param.status = true
|
||||
sysAttachmentList()
|
||||
};
|
||||
// 获取列表数据
|
||||
const sysAttachmentList = ()=>{
|
||||
loading.value = true
|
||||
listSysAttachment(state.tableData.param).then((res:any)=>{
|
||||
state.tableData.data = res.data.list??[];
|
||||
state.tableData.total = res.data.total;
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
// 上传类型字典翻译
|
||||
const kindFormat = (row:SysAttachmentTableColumns) => {
|
||||
return proxy.selectDictLabel(sys_upload_file_type.value, row.kind);
|
||||
}
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection:Array<SysAttachmentInfoData>) => {
|
||||
state.ids = selection.map(item => item.id)
|
||||
single.value = selection.length!=1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
const handleAdd = (upType:string)=>{
|
||||
editRef.value.openDialog(upType)
|
||||
}
|
||||
const handleSelect = (row: SysAttachmentTableColumns) => {
|
||||
let selected = true
|
||||
if(state.ids.includes(row.id)){
|
||||
selected = false
|
||||
state.ids.splice(state.ids.indexOf(row.id),1)
|
||||
}else{
|
||||
state.ids.push(row.id)
|
||||
}
|
||||
uploadTableRef.value.toggleRowSelection(row,selected)
|
||||
};
|
||||
const handleConfirm = () => {
|
||||
let ids:number[] = Array.from(new Set(state.ids));
|
||||
if(dataFileList.value.length+ids.length>props.limit){
|
||||
proxy.$message.error('最多只能选择'+props.limit+'个文件')
|
||||
return
|
||||
}
|
||||
let list :UploadUserFile[] = []
|
||||
state.tableData.data.map((item: SysAttachmentTableColumns)=>{
|
||||
if(ids.includes(item.id)){
|
||||
list.push({
|
||||
name: item.name,
|
||||
url:item.path
|
||||
})
|
||||
}
|
||||
})
|
||||
console.log('dataFileList.value=',dataFileList.value)
|
||||
dataFileList.value.push(...list)
|
||||
closeDialog()
|
||||
}
|
||||
const getExt = (fileName:string):string=>{
|
||||
// 查找最后一个点的位置
|
||||
const lastDotIndex = fileName.lastIndexOf('.');
|
||||
// 如果没有找到点,或者点在第一个字符(没有扩展名)
|
||||
if (lastDotIndex === -1 || lastDotIndex === 0) {
|
||||
return '';
|
||||
}
|
||||
// 提取并返回点后面部分的后缀(不带点)
|
||||
return fileName.slice(lastDotIndex + 1);
|
||||
}
|
||||
const formatFileSize = (row:SysAttachmentTableColumns):string =>{
|
||||
const bytes:number = row.size;
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const index = Math.floor(Math.log(bytes) / Math.log(1024)); // 计算索引
|
||||
const value = (bytes / Math.pow(1024, index)).toFixed(2); // 计算具体数值并保留两位小数
|
||||
return `${value} ${sizes[index]}`;
|
||||
}
|
||||
const openDialog = ()=>{
|
||||
reset()
|
||||
initTableData();
|
||||
isShowDialog.value = true
|
||||
}
|
||||
const closeDialog = ()=>{
|
||||
isShowDialog.value = false
|
||||
}
|
||||
const reset = ()=>{
|
||||
resetQuery(queryRef.value)
|
||||
state.ids = []
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.system-sysAttachment-container{
|
||||
width: 100%;
|
||||
}
|
||||
.colBlock {
|
||||
display: block;
|
||||
}
|
||||
.colNone {
|
||||
display: none;
|
||||
}
|
||||
.ml-2{margin: 3px;}
|
||||
.form-item{
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.image-slot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--el-fill-color-light);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.up-selector {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
vertical-align: top;
|
||||
|
||||
.uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 50px;
|
||||
height: 96px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.up-selector:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
.img-list{
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.img-list :deep(.el-upload.el-upload--picture-card){
|
||||
display: none;
|
||||
}
|
||||
.img-list:deep(.el-upload.el-upload--text){
|
||||
display: none;
|
||||
}
|
||||
</style>
|
@ -152,8 +152,9 @@ const getData = (barName: number | undefined) => {
|
||||
})*/
|
||||
let noticeParam = {
|
||||
pageNum: 1,
|
||||
pageSize: 5,
|
||||
pageSize: 10,
|
||||
type: barName,
|
||||
isTrim:true
|
||||
}
|
||||
listShowNotice(noticeParam).then((res: any) => {
|
||||
state.noticeList = res.data.list || []
|
||||
@ -225,12 +226,14 @@ const handleRead = (item: SysNoticeInfoData) => {
|
||||
getData(item.type)
|
||||
ElMessage.success("已读");
|
||||
})
|
||||
getUnReadCount()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-navbars-breadcrumb-user-news {
|
||||
|
||||
height: calc(100vh - 150px);
|
||||
overflow-y: auto;
|
||||
.content-box {
|
||||
font-size: 13px;
|
||||
|
||||
|
@ -60,38 +60,6 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
|
||||
roles: ['admin'],
|
||||
icon: 'iconfont icon-diannao',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/bigUpload',
|
||||
name: 'bigUpload',
|
||||
component: () => import('/@/layout/routerView/parent.vue'),
|
||||
meta:{
|
||||
title: '大文件上传',
|
||||
isLink: '',
|
||||
isHide: false,
|
||||
isKeepAlive: true,
|
||||
isAffix: false,
|
||||
isIframe: false,
|
||||
roles: ['admin'],
|
||||
icon: 'iconfont icon-diannao',
|
||||
},
|
||||
children:[
|
||||
{
|
||||
path: '/bigUpload/list',
|
||||
name: 'bigUploadList',
|
||||
component: () => import('/@/views/bigUpload/index.vue'),
|
||||
meta: {
|
||||
title: '大文件上传',
|
||||
isLink: '',
|
||||
isHide: false,
|
||||
isKeepAlive: true,
|
||||
isAffix: false,
|
||||
isIframe: false,
|
||||
roles: ['admin', 'common'],
|
||||
icon: 'iconfont icon-shouye',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import {bigUploadStates} from "/@/stores/interface";
|
||||
|
||||
export const bigUpload = defineStore('bigUpload', {
|
||||
state:():bigUploadStates=>({
|
||||
panelShow:false
|
||||
}),
|
||||
actions: {
|
||||
async setPanelShow(bool: boolean) {
|
||||
this.panelShow = bool;
|
||||
}
|
||||
},
|
||||
})
|
@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<el-dialog v-model="isShowDialog" width="769px">
|
||||
<template #header>修改</template>
|
||||
<el-form :model="ruleForm" ref="formRef" :rules="rules" size="default" label-width="90px">
|
||||
<el-form-item label="标题" prop="name">
|
||||
<el-input v-model="ruleForm.name" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="describe">
|
||||
<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="closeDialog" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">修改</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, defineComponent} from 'vue';
|
||||
import {getBigFile, editBigFile} from "/@/api/system/bigFile";
|
||||
|
||||
import {ElMessage} from "element-plus";
|
||||
defineOptions({ name: "editBigUpload"})
|
||||
const emit = defineEmits(["success"])
|
||||
const formRef = ref<HTMLElement | null>(null);
|
||||
const isShowDialog = ref<boolean>(false)
|
||||
const ruleForm = reactive<any>({id:0, name:"", describe:""})
|
||||
const rules = reactive<any>({
|
||||
name: [
|
||||
{ required: true, message: "标题不能为空", trigger: "blur" }
|
||||
],
|
||||
})
|
||||
const openDialog = async (id:number) => {
|
||||
resetForm()
|
||||
const result = await getBigFile(id).then((res:any) => res.code === 0? res.data || {} : {})
|
||||
const {name, describe} = result
|
||||
ruleForm.id = result.id
|
||||
ruleForm.name = name
|
||||
ruleForm.describe = describe
|
||||
isShowDialog.value = true
|
||||
}
|
||||
const closeDialog = () => {
|
||||
isShowDialog.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openDialog
|
||||
})
|
||||
|
||||
const resetForm = () => {
|
||||
ruleForm.id = 0
|
||||
ruleForm.name = ""
|
||||
ruleForm.describe = ""
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const formWrap = formRef.value as any
|
||||
if (!formWrap) return;
|
||||
|
||||
formWrap.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
const result:any = await editBigFile(ruleForm)
|
||||
if (result.code === 0) {
|
||||
ElMessage.success('修改成功');
|
||||
closeDialog()
|
||||
emit('success')
|
||||
} else {
|
||||
ElMessage.error("修改失败")
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,175 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card shadow="hover">
|
||||
<div class="system-user-search mb15">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
|
||||
<el-form-item label="文件名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入文件名称"
|
||||
clearable
|
||||
size="default"
|
||||
@keyup.enter.native="getBigFileList"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button size="default" type="primary" class="ml10" @click="getBigFileList">
|
||||
<el-icon>
|
||||
<ele-Search/>
|
||||
</el-icon>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button size="default" @click="resetQuery(queryRef)">
|
||||
<el-icon>
|
||||
<ele-Refresh/>
|
||||
</el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
<el-button size="default" type="success" class="ml10" @click="uploadHandle">
|
||||
<el-icon>
|
||||
<ele-FolderAdd/>
|
||||
</el-icon>
|
||||
文件上传
|
||||
</el-button>
|
||||
<el-button size="default" type="danger" class="ml10" @click="delMult">
|
||||
<el-icon>
|
||||
<ele-Delete/>
|
||||
</el-icon>
|
||||
删除文件
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column label="标题" align="center" prop="name"/>
|
||||
<el-table-column label="大小" align="center" prop="size">
|
||||
<template #default="scope">
|
||||
{{byteText(scope.row.size)}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件类型" align="center" prop="mimeType"/>
|
||||
<!-- <el-table-column label="文件描述" align="center" prop="describe"/>-->
|
||||
<el-table-column label="创建时间" align="center" prop="createdAt"/>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="edit(scope.row)">修改</el-button>
|
||||
<el-button size="small" text type="primary" @click="del(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getBigFileList"
|
||||
/>
|
||||
|
||||
|
||||
<EditBigUpload ref="editBigUploadRef" @success="getBigFileList"></EditBigUpload>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { onMounted, ref, defineComponent, getCurrentInstance} from 'vue';
|
||||
import getTableData from '/@/views/bigUpload/tableData'
|
||||
import {addBigFile, deleteBigFile} from "/@/api/system/bigFile";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import EditBigUpload from '/@/views/bigUpload/component/editBigUpload.vue'
|
||||
|
||||
defineOptions({ name: "bigUpload"})
|
||||
const {
|
||||
total,
|
||||
queryParams,
|
||||
tableData,
|
||||
queryRef,
|
||||
resetQuery,
|
||||
getBigFileList,
|
||||
resetBigFileList
|
||||
} = getTableData()
|
||||
const editBigUploadRef = ref();
|
||||
const {proxy} = <any>getCurrentInstance();
|
||||
const selected = ref<number[]>([])
|
||||
// 文件上传
|
||||
const uploadHandle = function () {
|
||||
proxy.mittBus.emit("bigUploader.uploadFile")
|
||||
}
|
||||
onMounted(()=> {
|
||||
proxy.mittBus.on("bigUploader.uploadFileSuccess", (res:any) => {
|
||||
//console.log(res)
|
||||
if (res.skipUpload === true) {
|
||||
// 秒传
|
||||
}
|
||||
const {filename, totalSize, url, identifier, fileType} = res
|
||||
addBigFile({
|
||||
name: filename,
|
||||
size: totalSize,
|
||||
path: url,
|
||||
mimeType: fileType,
|
||||
source: 0,
|
||||
md5: identifier
|
||||
}).then(() => {
|
||||
resetBigFileList()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const handleSelectionChange = (selection:any[]) => {
|
||||
selected.value = selection.map(item => item.id)
|
||||
}
|
||||
|
||||
const edit = function (row:any) {
|
||||
editBigUploadRef.value.openDialog(row.id)
|
||||
}
|
||||
|
||||
const del = function (row:any) {
|
||||
ElMessageBox.confirm(`是否删除文件:${row.name}`, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(()=> {
|
||||
deleteBigFile([row.id]).then(()=> {
|
||||
getBigFileList()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const delMult = function () {
|
||||
if(selected.value.length===0) {
|
||||
ElMessage.error('请选择要删除的数据。');
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`确定删除所选数据?`, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(()=> {
|
||||
deleteBigFile([...selected.value]).then(()=> {
|
||||
getBigFileList()
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const byteText = function (value:number) :string {
|
||||
if (value > 1048576 ) {
|
||||
return (value / 1024 / 1024).toFixed(2) + "MB"
|
||||
} else if (value > 0) {
|
||||
return (value / 1024).toFixed(2) + "KB"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@ -1,48 +0,0 @@
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {getBigFileList as apiGetBigFileList} from "/@/api/system/bigFile";
|
||||
import {FormInstance} from "element-plus/es";
|
||||
|
||||
export default function () {
|
||||
const tableData = ref<any[]>([])
|
||||
let total = ref<number>(0)
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: '',
|
||||
orderBy:'created_at desc'
|
||||
})
|
||||
const queryRef = ref<FormInstance>();
|
||||
|
||||
const resetQuery = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
getBigFileList()
|
||||
}
|
||||
|
||||
|
||||
const getBigFileList = async () => {
|
||||
const result:any = await apiGetBigFileList(queryParams).then((res:any) => res.code === 0 ? res.data : {})
|
||||
tableData.value = Array.isArray(result.list) ? result.list : []
|
||||
total.value = result.total || 0
|
||||
}
|
||||
|
||||
const resetBigFileList = async () => {
|
||||
queryParams.pageNum = 1
|
||||
queryParams.pageSize = 10
|
||||
queryParams.name = ''
|
||||
await getBigFileList()
|
||||
}
|
||||
|
||||
|
||||
onMounted(getBigFileList)
|
||||
|
||||
return {
|
||||
total,
|
||||
queryParams,
|
||||
tableData,
|
||||
queryRef,
|
||||
resetQuery,
|
||||
getBigFileList,
|
||||
resetBigFileList
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import {defineComponent,h} from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name:"baiduMap",
|
||||
props:{
|
||||
src:{
|
||||
type:String,
|
||||
default:'',
|
||||
}
|
||||
},
|
||||
setup(prop){
|
||||
return ()=>{
|
||||
return h('script',{src:prop.src,type:'text/javascript'})
|
||||
}
|
||||
},
|
||||
})
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<gf-ueditor v-if="show" editorId="demoEdit01" v-model="content" @setEditContent="setEditContent"></gf-ueditor>
|
||||
<gf-ueditor v-if="show" editorId="demoEdit01" v-model="content"></gf-ueditor>
|
||||
<h3>同步获取编辑器内容如下:</h3>
|
||||
<div v-html="content"></div>
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<el-radio
|
||||
v-for="dict in sysYesNoOptions"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
@ -27,15 +27,7 @@
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
|
||||
<el-form-item label="负责人">
|
||||
<div v-if="deptUser.length > 0">
|
||||
<el-tag closable :disable-transitions="false" class="u-m-r-10" v-for="(item,index) in deptUser" :key="index" @close="handleClose(item,index)" >{{item.userNickname}}</el-tag>
|
||||
</div>
|
||||
<!-- <el-input v-model="ruleForm.leader" placeholder="请输入负责人" clearable></el-input>-->
|
||||
<el-button
|
||||
style="padding-left: 10px;"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleSelectUser" >请选择</el-button>
|
||||
<select-user v-model="ruleForm.leader"></select-user>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
|
||||
@ -68,13 +60,11 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<select-user ref="selectUserRef" @selectUser="confirmUser" :selectedUsers="deptUser"></select-user>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {reactive, toRefs, defineComponent, getCurrentInstance,ref,unref} from 'vue';
|
||||
import {reactive, toRefs, getCurrentInstance,ref,unref} from 'vue';
|
||||
import {addDept,editDept, getDeptList} from "/@/api/system/dept";
|
||||
import {getUserByIds} from '/@/api/system/user';
|
||||
import {ElMessage} from "element-plus";
|
||||
import selectUser from "/@/components/selectUser/index.vue"
|
||||
|
||||
@ -105,8 +95,6 @@ defineOptions({ name: "systemEditDept"})
|
||||
const emit = defineEmits(['deptList'])
|
||||
const {proxy} = getCurrentInstance() as any;
|
||||
const formRef = ref<HTMLElement | null>(null);
|
||||
const selectUserRef = ref();
|
||||
const deptUser = ref([]);
|
||||
const state = reactive<DeptSate>({
|
||||
isShowDialog: false,
|
||||
ruleForm: {
|
||||
@ -135,24 +123,8 @@ const openDialog = (row?: RuleFormState|number) => {
|
||||
});
|
||||
if(row && typeof row === "object"){
|
||||
state.ruleForm = row;
|
||||
let leaders = row.leader??[]
|
||||
if (leaders.length > 0){
|
||||
//获取部门负责人信息
|
||||
getUserByIds({ids:leaders}).then((res:any)=>{
|
||||
if(res.code === 0){
|
||||
deptUser.value = res.data.userList;
|
||||
}
|
||||
});
|
||||
}else{
|
||||
//清空 deptUser, 避免缓存影响
|
||||
deptUser.value = [];
|
||||
}
|
||||
|
||||
}else if(row && typeof row === 'number'){
|
||||
}else if(row){
|
||||
state.ruleForm.parentId = row
|
||||
deptUser.value = [];
|
||||
}else{
|
||||
deptUser.value = [];
|
||||
}
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
@ -201,31 +173,6 @@ const resetForm = ()=>{
|
||||
status: 1,
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = (data:any,key:number) => {
|
||||
deptUser.value.splice(key, 1);
|
||||
state.ruleForm.leader = deptUser.value.map((item:any) => item.id)
|
||||
};
|
||||
const confirmUser = (data:any[]) => {
|
||||
let leaderArr = state.ruleForm.leader??[];
|
||||
if(data.length>0){
|
||||
data.map((item:any)=>{
|
||||
// 若存在某个用户 则不添加
|
||||
if (!leaderArr.includes(item.id)){
|
||||
deptUser.value.push(item as never)
|
||||
leaderArr.push(item.id)
|
||||
}
|
||||
})
|
||||
state.ruleForm.leader = leaderArr;
|
||||
}else{
|
||||
deptUser.value = []
|
||||
state.ruleForm.leader = []
|
||||
}
|
||||
};
|
||||
//选择用户
|
||||
const handleSelectUser = () =>{
|
||||
selectUserRef.value.openDialog()
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.u-m-r-10{
|
||||
|
@ -73,12 +73,12 @@
|
||||
<el-dialog :title="selectRow.name+'-用户列表'" v-model="isShowDialog" width="70vw">
|
||||
<UserList v-if="isShowDialog" ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="userListParam" @getUserList="userList"/>
|
||||
</el-dialog>
|
||||
<select-user ref="selectUserRef" @selectUser="confirmUser" @okBack="setRoleUser" :selectedUsers="roleUsers"></select-user>
|
||||
<select-user v-show="false" ref="selectUserRef" v-model="roleUsers"></select-user>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {toRefs, reactive, onMounted, ref, defineComponent, toRaw,getCurrentInstance} from 'vue';
|
||||
import {toRefs, reactive, onMounted, ref, defineComponent, toRaw, getCurrentInstance, watch} from 'vue';
|
||||
import { ElMessageBox, ElMessage,ElLoading } from 'element-plus';
|
||||
import EditRole from '/@/views/system/role/component/editRole.vue';
|
||||
import DataScope from '/@/views/system/role/component/dataScope.vue';
|
||||
@ -257,26 +257,10 @@ const handleCommand = (command: string )=>{
|
||||
break
|
||||
}
|
||||
}
|
||||
const confirmUser = (data:any[]) => {
|
||||
if(data.length>0){
|
||||
const ids = roleUsers.value.map((item:any)=>{
|
||||
return item.id
|
||||
})
|
||||
console.log('ids = ',ids)
|
||||
data.map((item:any)=>{
|
||||
// 若存在某个用户 则不添加
|
||||
if (!ids.includes(item.id)){
|
||||
roleUsers.value.push(item as never)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
roleUsers.value = []
|
||||
}
|
||||
};
|
||||
const setRoleUser = ()=>{
|
||||
const ids = roleUsers.value.map((item:any)=>{
|
||||
return item.id
|
||||
watch(roleUsers,(newVal) => {
|
||||
setRoleUser(newVal)
|
||||
})
|
||||
const setRoleUser = (ids:any)=>{
|
||||
//用户授权提交
|
||||
setRoleUsers({roleId:setRole.value,userIds:ids}).then((res:any)=>{
|
||||
roleList()
|
||||
|
249
src/views/system/sysAttachment/list/component/detail.vue
Normal file
249
src/views/system/sysAttachment/list/component/detail.vue
Normal file
@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<!-- 附件管理详情抽屉 -->
|
||||
<div class="system-sysAttachment-detail">
|
||||
<el-drawer v-model="isShowDialog" size="80%" direction="ltr">
|
||||
<template #header>
|
||||
<h4>附件管理详情</h4>
|
||||
</template>
|
||||
<el-descriptions
|
||||
class="margin-top"
|
||||
:column="3"
|
||||
border
|
||||
style="margin: 8px;"
|
||||
>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
文件ID
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.id }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
应用ID
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.appId }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
上传驱动
|
||||
</div>
|
||||
</template>
|
||||
{{ proxy.getOptionValue(formData.drive, driveOptions,'value','label') }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
文件原始名
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
上传类型
|
||||
</div>
|
||||
</template>
|
||||
{{ proxy.getOptionValue(formData.kind, kindOptions,'value','label') }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
扩展类型
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.mimeType }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
本地路径
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.path }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
文件大小
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.size }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
扩展名
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.ext }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
md5校验码
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.md5 }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
上传人ID
|
||||
</div>
|
||||
</template>
|
||||
{{ formData.createdBy }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
状态
|
||||
</div>
|
||||
</template>
|
||||
<el-switch v-model="formData.status" class="ml-2" disabled />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1">
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
创建时间
|
||||
</div>
|
||||
</template>
|
||||
{{ proxy.parseTime(formData.createdAt, '{y}-{m}-{d} {h}:{i}:{s}') }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, toRefs, defineComponent,ref,unref,getCurrentInstance,computed } from 'vue';
|
||||
import {ElMessageBox, ElMessage, FormInstance,UploadProps} from 'element-plus';
|
||||
import {
|
||||
listSysAttachment,
|
||||
getSysAttachment,
|
||||
delSysAttachment,
|
||||
addSysAttachment,
|
||||
updateSysAttachment,
|
||||
} from "/@/api/system/sysAttachment";
|
||||
import {
|
||||
SysAttachmentTableColumns,
|
||||
SysAttachmentInfoData,
|
||||
SysAttachmentTableDataState,
|
||||
SysAttachmentEditState
|
||||
} from "/@/views/system/sysAttachment/list/component/model"
|
||||
defineOptions({ name: "ApiV1SystemSysAttachmentDetail"})
|
||||
const props = defineProps({
|
||||
driveOptions:{
|
||||
type:Array,
|
||||
default:()=>[]
|
||||
},
|
||||
kindOptions:{
|
||||
type:Array,
|
||||
default:()=>[]
|
||||
},
|
||||
})
|
||||
const {proxy} = <any>getCurrentInstance()
|
||||
const formRef = ref<HTMLElement | null>(null);
|
||||
const menuRef = ref();
|
||||
const state = reactive<SysAttachmentEditState>({
|
||||
loading:false,
|
||||
isShowDialog: false,
|
||||
formData: {
|
||||
id: undefined,
|
||||
appId: undefined,
|
||||
drive: undefined,
|
||||
name: undefined,
|
||||
kind: undefined,
|
||||
mimeType: undefined,
|
||||
path: undefined,
|
||||
size: undefined,
|
||||
ext: undefined,
|
||||
md5: undefined,
|
||||
createdBy: undefined,
|
||||
status: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
id : [
|
||||
{ required: true, message: "文件ID不能为空", trigger: "blur" }
|
||||
],
|
||||
appId : [
|
||||
{ required: true, message: "应用ID不能为空", trigger: "blur" }
|
||||
],
|
||||
name : [
|
||||
{ required: true, message: "文件原始名不能为空", trigger: "blur" }
|
||||
],
|
||||
status : [
|
||||
{ required: true, message: "状态不能为空", trigger: "blur" }
|
||||
],
|
||||
}
|
||||
});
|
||||
const { isShowDialog,formData } = toRefs(state);
|
||||
// 打开弹窗
|
||||
const openDialog = (row?: SysAttachmentInfoData) => {
|
||||
resetForm();
|
||||
if(row) {
|
||||
getSysAttachment(row.id!).then((res:any)=>{
|
||||
const data = res.data;
|
||||
data.createdBy = data.createdUser?.userNickname
|
||||
state.formData = data;
|
||||
})
|
||||
}
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
const resetForm = ()=>{
|
||||
state.formData = {
|
||||
id: undefined,
|
||||
appId: undefined,
|
||||
drive: undefined,
|
||||
name: undefined,
|
||||
kind: undefined,
|
||||
mimeType: undefined,
|
||||
path: undefined,
|
||||
size: undefined,
|
||||
ext: undefined,
|
||||
md5: undefined,
|
||||
createdBy: undefined,
|
||||
status: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.system-sysAttachment-detail :deep(.el-form-item--large .el-form-item__label){
|
||||
font-weight: bolder;
|
||||
}
|
||||
.pic-block{
|
||||
margin-right: 8px;
|
||||
}
|
||||
.file-block{
|
||||
width: 100%;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
margin-bottom: 5px;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
.ml-2{margin-right: 5px;}
|
||||
</style>
|
76
src/views/system/sysAttachment/list/component/edit.vue
Normal file
76
src/views/system/sysAttachment/list/component/edit.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="system-sysAttachment-edit">
|
||||
<!-- 添加或修改附件管理对话框 -->
|
||||
<el-dialog v-model="isShowDialog" width="800px" :close-on-click-modal="false" :destroy-on-close="true">
|
||||
<template #header>
|
||||
<div v-drag="['.system-sysAttachment-edit .el-dialog', '.system-sysAttachment-edit .el-dialog__header']">上传</div>
|
||||
</template>
|
||||
<upload-img ref="upImageRef" v-if="upType==='image'" :action="baseURL+'api/v1/system/upload/singleImg'" v-model="images" :limit="10"></upload-img>
|
||||
<upload-file ref="upFileRef" v-else-if="upType==='file'" :action="baseURL+'api/v1/system/upload/singleFile'" v-model="files" :limit="10" :uploadSize="50"></upload-file>
|
||||
<upload-big-file ref="upBigFileRef" v-else-if="upType==='bigFile'" v-model="files" :limit="10"></upload-big-file>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="danger" @click="onCancel">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import uploadImg from "/@/components/uploadImg/index.vue"
|
||||
import uploadFile from "/@/components/uploadFile/index.vue"
|
||||
import uploadBigFile from "/@/components/uploadBigFile/index.vue"
|
||||
import {reactive, toRefs, ref, watch} from 'vue';
|
||||
import {
|
||||
SysAttachmentEditState
|
||||
} from "/@/views/system/sysAttachment/list/component/model"
|
||||
defineOptions({ name: "ApiV1SystemSysAttachmentEdit"})
|
||||
const baseURL:string|undefined|boolean = import.meta.env.VITE_API_URL
|
||||
const emit = defineEmits(['sysAttachmentList'])
|
||||
const upType = ref('')
|
||||
const upFileRef = ref()
|
||||
const upImageRef = ref()
|
||||
const upBigFileRef = ref()
|
||||
const state = reactive<SysAttachmentEditState>({
|
||||
isShowDialog: false,
|
||||
images: [],
|
||||
files:[]
|
||||
});
|
||||
const { isShowDialog,images,files } = toRefs(state);
|
||||
// 打开弹窗
|
||||
const openDialog = (upTp:string) => {
|
||||
reset()
|
||||
upType.value = upTp
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
if(upType.value=='image'){
|
||||
upImageRef.value.stopUpImage()
|
||||
}else if(upType.value=='file'){
|
||||
upFileRef.value.stopUpFile()
|
||||
}else if(upType.value=='bigFile'){
|
||||
upBigFileRef.value.stopUpBigFile()
|
||||
}
|
||||
};
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
watch(()=>[state.images,state.files],()=>{
|
||||
emit('sysAttachmentList')
|
||||
})
|
||||
const reset = ()=>{
|
||||
state.images = []
|
||||
state.files = []
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.kv-label{margin-bottom: 15px;font-size: 14px;}
|
||||
.mini-btn i.el-icon{margin: unset;}
|
||||
.kv-row{margin-bottom: 12px;}
|
||||
</style>
|
59
src/views/system/sysAttachment/list/component/model.ts
Normal file
59
src/views/system/sysAttachment/list/component/model.ts
Normal file
@ -0,0 +1,59 @@
|
||||
export interface SysAttachmentTableColumns {
|
||||
id:number; // 文件ID
|
||||
appId:string; // 应用ID
|
||||
drive:string; // 上传驱动
|
||||
name:string; // 文件原始名
|
||||
kind:string; // 上传类型
|
||||
path:string; // 本地路径
|
||||
size:number; // 文件大小
|
||||
ext:string; // 扩展名
|
||||
status:boolean; // 状态
|
||||
createdAt:string; // 创建时间
|
||||
}
|
||||
|
||||
|
||||
export interface SysAttachmentInfoData {
|
||||
id:number|undefined; // 文件ID
|
||||
appId:string|undefined; // 应用ID
|
||||
drive:string|undefined; // 上传驱动
|
||||
name:string|undefined; // 文件原始名
|
||||
kind:string|undefined; // 上传类型
|
||||
mimeType:string|undefined; // 扩展类型
|
||||
path:string|undefined; // 本地路径
|
||||
size:number|undefined; // 文件大小
|
||||
ext:string|undefined; // 扩展名
|
||||
md5:string|undefined; // md5校验码
|
||||
createdBy:number|undefined; // 上传人ID
|
||||
status:boolean; // 状态
|
||||
createdAt:string|undefined; // 创建时间
|
||||
updatedAt:string|undefined; // 修改时间
|
||||
}
|
||||
|
||||
|
||||
export interface SysAttachmentTableDataState {
|
||||
ids:any[];
|
||||
tableData: {
|
||||
data: Array<SysAttachmentTableColumns>;
|
||||
total: number;
|
||||
loading: boolean;
|
||||
param: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
appId: string|undefined;
|
||||
drive: string|undefined;
|
||||
name: string|undefined;
|
||||
kind: string|undefined;
|
||||
mimeType: string|undefined;
|
||||
status: boolean|undefined;
|
||||
createdAt: string[];
|
||||
dateRange: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export interface SysAttachmentEditState{
|
||||
isShowDialog: boolean;
|
||||
images:any[];
|
||||
files:any[];
|
||||
}
|
405
src/views/system/sysAttachment/list/index.vue
Normal file
405
src/views/system/sysAttachment/list/index.vue
Normal file
@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<div class="system-sysAttachment-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-sysAttachment-search mb15">
|
||||
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="8" class="colBlock">
|
||||
<el-form-item label="应用ID" prop="appId">
|
||||
<el-input
|
||||
v-model="tableData.param.appId"
|
||||
placeholder="请输入应用ID"
|
||||
clearable
|
||||
@keyup.enter.native="sysAttachmentList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" class="colBlock">
|
||||
<el-form-item label="上传驱动" prop="drive">
|
||||
<el-select v-model="tableData.param.drive" placeholder="请选择上传驱动" clearable style="width:200px;">
|
||||
<el-option
|
||||
v-for="dict in sys_upload_drive"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :class="!showAll ? 'colBlock' : 'colNone'">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="sysAttachmentList"><el-icon><ele-Search /></el-icon>搜索</el-button>
|
||||
<el-button @click="resetQuery(queryRef)"><el-icon><ele-Refresh /></el-icon>重置</el-button>
|
||||
<el-button type="primary" link @click="toggleSearch">
|
||||
{{ word }}
|
||||
<el-icon v-show="showAll"><ele-ArrowUp/></el-icon>
|
||||
<el-icon v-show="!showAll"><ele-ArrowDown /></el-icon>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
|
||||
<el-form-item label="文件原始名" prop="name">
|
||||
<el-input
|
||||
v-model="tableData.param.name"
|
||||
placeholder="请输入文件原始名"
|
||||
clearable
|
||||
@keyup.enter.native="sysAttachmentList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
|
||||
<el-form-item label="上传类型" prop="kind">
|
||||
<el-select v-model="tableData.param.kind" placeholder="请选择上传类型" clearable style="width:200px;">
|
||||
<el-option
|
||||
v-for="dict in sys_upload_file_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
|
||||
<el-form-item label="扩展类型" prop="mimeType">
|
||||
<el-input
|
||||
v-model="tableData.param.mimeType"
|
||||
placeholder="请输入扩展类型"
|
||||
clearable
|
||||
@keyup.enter.native="sysAttachmentList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-switch v-model="tableData.param.status" class="ml-2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
clearable style="width: 200px"
|
||||
v-model="tableData.param.createdAt"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="sysAttachmentList"><el-icon><ele-Search /></el-icon>搜索</el-button>
|
||||
<el-button @click="resetQuery(queryRef)"><el-icon><ele-Refresh /></el-icon>重置</el-button>
|
||||
<el-button type="primary" link @click="toggleSearch">
|
||||
{{ word }}
|
||||
<el-icon v-show="showAll"><ele-ArrowUp/></el-icon>
|
||||
<el-icon v-show="!showAll"><ele-ArrowDown /></el-icon>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleAdd('file')"
|
||||
><el-icon><ele-Upload /></el-icon>上传文件</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
color="#626aef"
|
||||
:dark="true"
|
||||
type="success"
|
||||
@click="handleAdd('image')"
|
||||
><el-icon><ele-PictureFilled /></el-icon>上传图片</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
@click="handleAdd('bigFile')"
|
||||
><i class="iconfont icon-shangchuan"></i>上传大文件</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete(null)"
|
||||
v-auth="'api/v1/system/sysAttachment/delete'"
|
||||
><el-icon><ele-Delete /></el-icon>删除</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="tableData.data" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="文件ID" align="center" prop="id"
|
||||
min-width="80px"
|
||||
/>
|
||||
<el-table-column label="应用ID" align="center" prop="appId"
|
||||
min-width="100px"
|
||||
/>
|
||||
<el-table-column label="上传驱动" align="center" prop="drive" :formatter="driveFormat"
|
||||
min-width="100px"
|
||||
/>
|
||||
<el-table-column label="文件原始名" align="center" prop="name" show-overflow-tooltip
|
||||
min-width="260px"
|
||||
/>
|
||||
<el-table-column label="上传类型" align="center" prop="kind" :formatter="kindFormat"
|
||||
min-width="100px"
|
||||
/>
|
||||
<el-table-column label="本地路径" align="center" prop="path" min-width="100px">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="scope.row.kind=='image'"
|
||||
style="width: 60px; height: 60px"
|
||||
:src="proxy.getUpFileUrl(scope.row.path)"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="[proxy.getUpFileUrl(scope.row.path)]"
|
||||
:initial-index="0"
|
||||
preview-teleported
|
||||
hide-on-click-modal
|
||||
fit="fill"
|
||||
/>
|
||||
<el-image
|
||||
v-else
|
||||
style="width: 60px; height: 60px">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
{{getExt(scope.row.name)}}
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件大小" align="center" prop="size" :formatter="formatFileSize"
|
||||
min-width="100px"
|
||||
/>
|
||||
<el-table-column label="状态" align="center" prop="status"
|
||||
min-width="150px"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" class="ml-2" @change="changeStatus(scope.row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="updatedAt"
|
||||
min-width="150px"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.updatedAt, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding" min-width="160px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleDownload(scope.row)"
|
||||
><el-icon><ele-Download /></el-icon>下载</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleDelete(scope.row)"
|
||||
v-auth="'api/v1/system/sysAttachment/delete'"
|
||||
><el-icon><ele-DeleteFilled /></el-icon>删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="tableData.total>0"
|
||||
:total="tableData.total"
|
||||
v-model:page="tableData.param.pageNum"
|
||||
v-model:limit="tableData.param.pageSize"
|
||||
@pagination="sysAttachmentList"
|
||||
/>
|
||||
</el-card>
|
||||
<ApiV1SystemSysAttachmentEdit
|
||||
ref="editRef"
|
||||
@sysAttachmentList="sysAttachmentList"
|
||||
></ApiV1SystemSysAttachmentEdit>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {toRefs, reactive, onMounted, ref, computed,getCurrentInstance,toRaw} from 'vue';
|
||||
import {ElMessageBox, ElMessage, FormInstance} from 'element-plus';
|
||||
import {
|
||||
listSysAttachment,
|
||||
delSysAttachment,
|
||||
changeSysAttachmentStatus,
|
||||
} from "/@/api/system/sysAttachment";
|
||||
import {
|
||||
SysAttachmentTableColumns,
|
||||
SysAttachmentInfoData,
|
||||
SysAttachmentTableDataState,
|
||||
} from "/@/views/system/sysAttachment/list/component/model"
|
||||
import ApiV1SystemSysAttachmentEdit from "/@/views/system/sysAttachment/list/component/edit.vue"
|
||||
defineOptions({ name: "apiV1SystemSysAttachmentList"})
|
||||
const {proxy} = <any>getCurrentInstance()
|
||||
const loading = ref(false)
|
||||
const queryRef = ref()
|
||||
const editRef = ref();
|
||||
// 是否显示所有搜索选项
|
||||
const showAll = ref(false)
|
||||
// 非单个禁用
|
||||
const single = ref(true)
|
||||
// 非多个禁用
|
||||
const multiple =ref(true)
|
||||
const word = computed(()=>{
|
||||
if(showAll.value === false) {
|
||||
//对文字进行处理
|
||||
return "展开搜索";
|
||||
} else {
|
||||
return "收起搜索";
|
||||
}
|
||||
})
|
||||
// 字典选项数据
|
||||
const {
|
||||
sys_upload_drive,
|
||||
sys_upload_file_type,
|
||||
} = proxy.useDict(
|
||||
'sys_upload_drive',
|
||||
'sys_upload_file_type',
|
||||
)
|
||||
const state = reactive<SysAttachmentTableDataState>({
|
||||
ids:[],
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
appId: undefined,
|
||||
drive: undefined,
|
||||
name: undefined,
|
||||
kind: undefined,
|
||||
mimeType: undefined,
|
||||
status: undefined,
|
||||
createdAt: [],
|
||||
dateRange: []
|
||||
},
|
||||
},
|
||||
});
|
||||
const { tableData } = toRefs(state);
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
sysAttachmentList()
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
sysAttachmentList()
|
||||
};
|
||||
// 获取列表数据
|
||||
const sysAttachmentList = ()=>{
|
||||
loading.value = true
|
||||
listSysAttachment(state.tableData.param).then((res:any)=>{
|
||||
let list = res.data.list??[];
|
||||
state.tableData.data = list;
|
||||
state.tableData.total = res.data.total;
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
const toggleSearch = () => {
|
||||
showAll.value = !showAll.value;
|
||||
}
|
||||
// 上传驱动字典翻译
|
||||
const driveFormat = (row:SysAttachmentTableColumns) => {
|
||||
return proxy.selectDictLabel(sys_upload_drive.value, row.drive);
|
||||
}
|
||||
// 上传类型字典翻译
|
||||
const kindFormat = (row:SysAttachmentTableColumns) => {
|
||||
return proxy.selectDictLabel(sys_upload_file_type.value, row.kind);
|
||||
}
|
||||
const changeStatus = (row:SysAttachmentTableColumns) => {
|
||||
changeSysAttachmentStatus(row.id,row.status)
|
||||
.catch(()=>{
|
||||
setTimeout(()=>{
|
||||
row.status = !row.status
|
||||
},300)
|
||||
})
|
||||
}
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection:Array<SysAttachmentInfoData>) => {
|
||||
state.ids = selection.map(item => item.id)
|
||||
single.value = selection.length!=1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
const handleAdd = (upType:string)=>{
|
||||
editRef.value.openDialog(upType)
|
||||
}
|
||||
const handleDownload = (row: SysAttachmentTableColumns) => {
|
||||
window.open(proxy.getUpFileUrl(row.path))
|
||||
};
|
||||
const handleDelete = (row: SysAttachmentTableColumns|null) => {
|
||||
let msg = '你确定要删除所选数据?';
|
||||
let id:number[] = [] ;
|
||||
if(row){
|
||||
msg = `此操作将永久删除数据,是否继续?`
|
||||
id = [row.id]
|
||||
}else{
|
||||
id = state.ids
|
||||
}
|
||||
if(id.length===0){
|
||||
ElMessage.error('请选择要删除的数据。');
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(msg, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
delSysAttachment(id).then(()=>{
|
||||
ElMessage.success('删除成功');
|
||||
sysAttachmentList();
|
||||
})
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
const getExt = (fileName:string):string=>{
|
||||
// 查找最后一个点的位置
|
||||
const lastDotIndex = fileName.lastIndexOf('.');
|
||||
// 如果没有找到点,或者点在第一个字符(没有扩展名)
|
||||
if (lastDotIndex === -1 || lastDotIndex === 0) {
|
||||
return '';
|
||||
}
|
||||
// 提取并返回点后面部分的后缀(不带点)
|
||||
return fileName.slice(lastDotIndex + 1);
|
||||
}
|
||||
const formatFileSize = (row:SysAttachmentTableColumns):string =>{
|
||||
const bytes:number = row.size;
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const index = Math.floor(Math.log(bytes) / Math.log(1024)); // 计算索引
|
||||
const value = (bytes / Math.pow(1024, index)).toFixed(2); // 计算具体数值并保留两位小数
|
||||
return `${value} ${sizes[index]}`;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.colBlock {
|
||||
display: block;
|
||||
}
|
||||
.colNone {
|
||||
display: none;
|
||||
}
|
||||
.ml-2{margin: 3px;}
|
||||
.image-slot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--el-fill-color-light);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 20px;
|
||||
}
|
||||
</style>
|
@ -50,8 +50,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="内容">
|
||||
<gf-ueditor editorId="ueSysNoticeContent" v-model="formData.content"
|
||||
@setEditContent="setContentEditContent"></gf-ueditor>
|
||||
<gf-ueditor editorId="ueSysNoticeContent" v-model="formData.content"></gf-ueditor>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<!-- <el-radio-group v-model="formData.status">
|
||||
|
@ -130,6 +130,12 @@
|
||||
<el-option label="多图上传" value="images" />
|
||||
<el-option label="单文件上传" value="file" />
|
||||
<el-option label="多文件上传" value="files" />
|
||||
<el-option label="图片选择器" value="imageSelector" />
|
||||
<el-option label="附件选择器" value="fileSelector" />
|
||||
<el-option label="用户选择器(单选)" value="userSelectorSingle" />
|
||||
<el-option label="用户选择器(多选)" value="userSelectorMultiple" />
|
||||
<el-option label="部门选择器(单选)" value="deptSelectorSingle" />
|
||||
<el-option label="部门选择器(多选)" value="deptSelectorMultiple" />
|
||||
<el-option label="键值对" value="keyValue" />
|
||||
</el-select>
|
||||
</template>
|
||||
|
Loading…
x
Reference in New Issue
Block a user