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": "^5.5.0",
|
||||||
"echarts-gl": "^2.0.9",
|
"echarts-gl": "^2.0.9",
|
||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "^2.6.3",
|
"element-plus": "^2.8.7",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsplumb": "^2.15.6",
|
"jsplumb": "^2.15.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@ -57,8 +57,8 @@
|
|||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-vue": "^9.23.0",
|
"eslint-plugin-vue": "^9.23.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"sass": "^1.72.0",
|
"sass": "^1.80.7",
|
||||||
"sass-loader": "^13.0.2",
|
"sass-loader": "^16.0.3",
|
||||||
"typescript": "^5.4.2",
|
"typescript": "^5.4.2",
|
||||||
"vite": "5.4.6",
|
"vite": "5.4.6",
|
||||||
"vite-plugin-cdn-import": "^0.3.5",
|
"vite-plugin-cdn-import": "^0.3.5",
|
||||||
|
@ -937,21 +937,18 @@
|
|||||||
|
|
||||||
if (!_this.listEnd && !this.isLoadingData) {
|
if (!_this.listEnd && !this.isLoadingData) {
|
||||||
this.isLoadingData = true;
|
this.isLoadingData = true;
|
||||||
var url = editor.getActionUrl(editor.getOpt('imageManagerActionName')),
|
var url = editor.getActionUrl(editor.getOpt('imageManagerActionName'));
|
||||||
isJsonp = utils.isCrossDomainUrl(url);
|
|
||||||
ajax.request(url, {
|
ajax.request(url, {
|
||||||
'timeout': 100000,
|
timeout: 100000,
|
||||||
'dataType': isJsonp ? 'jsonp' : '',
|
data: utils.extend({
|
||||||
'headers': editor.options.serverHeaders || {},
|
|
||||||
'data': utils.extend({
|
|
||||||
start: this.listIndex,
|
start: this.listIndex,
|
||||||
size: this.listSize
|
size: this.listSize
|
||||||
}, editor.queryCommandValue('serverparam')),
|
}, editor.queryCommandValue('serverparam')),
|
||||||
'method': 'get',
|
headers: editor.options.serverHeaders || {},
|
||||||
|
method: 'get',
|
||||||
'onsuccess': function (r) {
|
'onsuccess': function (r) {
|
||||||
try {
|
try {
|
||||||
var json = isJsonp ? r : eval('(' + r.responseText + ')');
|
var json = eval('(' + r.responseText + ')');
|
||||||
json = editor.options.serverResponsePrepare(json);
|
|
||||||
if (json.state === 'SUCCESS') {
|
if (json.state === 'SUCCESS') {
|
||||||
_this.pushData(json.list);
|
_this.pushData(json.list);
|
||||||
_this.listIndex = parseInt(json.start) + parseInt(json.list.length);
|
_this.listIndex = parseInt(json.start) + parseInt(json.list.length);
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
|
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
|
||||||
<CloseFull v-if="!themeConfig.isLockScreen" />
|
<CloseFull v-if="!themeConfig.isLockScreen" />
|
||||||
</el-config-provider>
|
</el-config-provider>
|
||||||
<BigUploader></BigUploader>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@ -20,11 +19,10 @@ import setIntroduction from '/@/utils/setIconfont';
|
|||||||
import LockScreen from '/@/layout/lockScreen/index.vue';
|
import LockScreen from '/@/layout/lockScreen/index.vue';
|
||||||
import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
|
import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
|
||||||
import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
|
import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
|
||||||
import BigUploader from '/@/components/bigUploader/index.vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'app',
|
name: 'app',
|
||||||
components: { LockScreen, Setings, CloseFull,BigUploader },
|
components: { LockScreen, Setings, CloseFull},
|
||||||
setup() {
|
setup() {
|
||||||
const { proxy } = <any>getCurrentInstance();
|
const { proxy } = <any>getCurrentInstance();
|
||||||
const setingsRef = ref();
|
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>
|
<template>
|
||||||
<div>
|
<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">
|
<el-dialog title="选择用户" v-model="visible" width="80%" top="5vh" append-to-body :close-on-click-modal="false">
|
||||||
<div class="system-user-container">
|
<div class="system-user-container">
|
||||||
<el-row :gutter="10" style="width: 100%;">
|
<el-row :gutter="10" style="width: 100%;">
|
||||||
@ -61,7 +69,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</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-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@ -97,11 +105,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 {ElTree,FormInstance} from 'element-plus';
|
||||||
import { Search } from '@element-plus/icons-vue'
|
import { Search } from '@element-plus/icons-vue'
|
||||||
import UserList from './component/userList.vue';
|
import UserList from './component/userList.vue';
|
||||||
import {getDeptTree} from '/@/api/system/user/index';
|
import {getDeptTree, getUserByIds} from '/@/api/system/user/index';
|
||||||
|
|
||||||
interface QueryParam {
|
interface QueryParam {
|
||||||
ids:number[];
|
ids:number[];
|
||||||
@ -122,14 +130,14 @@ export default defineComponent({
|
|||||||
props:{
|
props:{
|
||||||
multiple:{
|
multiple:{
|
||||||
type:Boolean,
|
type:Boolean,
|
||||||
default:false
|
default:true
|
||||||
},
|
},
|
||||||
selectedUsers:{
|
modelValue:{
|
||||||
type:Array,
|
type:Array<number>,
|
||||||
default:()=>[]
|
default:()=>[]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits:['selectUser','okBack'],
|
emits:['update:modelValue'],
|
||||||
setup(prop,{emit}) {
|
setup(prop,{emit}) {
|
||||||
const selectedUsersPage = ref(1)
|
const selectedUsersPage = ref(1)
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
@ -140,6 +148,39 @@ export default defineComponent({
|
|||||||
const filterText = ref('');
|
const filterText = ref('');
|
||||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||||
const search = Search
|
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>({
|
const state = reactive<QueryParam>({
|
||||||
ids:[],
|
ids:[],
|
||||||
deptProps:{
|
deptProps:{
|
||||||
@ -156,16 +197,6 @@ export default defineComponent({
|
|||||||
dateRange:[]
|
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 = ()=>{
|
const getUserList = ()=>{
|
||||||
userListRef.value.setUserList();
|
userListRef.value.setUserList();
|
||||||
};
|
};
|
||||||
@ -202,21 +233,35 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
const handleSelectUserOk = (row:any)=>{
|
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 = ()=>{
|
const goBack = ()=>{
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
emit("okBack");
|
|
||||||
}
|
}
|
||||||
const removeAll = ()=>{
|
const removeAll = ()=>{
|
||||||
selectedUserInfo.value = []
|
selectedUsers.value = []
|
||||||
}
|
}
|
||||||
const remove = (index:number)=>{
|
const remove = (index:number)=>{
|
||||||
index = (selectedUsersPage.value-1)*10+index
|
index = (selectedUsersPage.value-1)*10+index
|
||||||
let newSel:any = [...selectedUserInfo.value]
|
let newSel:any = [...selectedUsers.value!]
|
||||||
selectedUserInfo.value = []
|
selectedUsers.value = []
|
||||||
newSel.splice(index,1)
|
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 {
|
return {
|
||||||
selectedUsersPage,
|
selectedUsersPage,
|
||||||
@ -228,6 +273,8 @@ export default defineComponent({
|
|||||||
treeRef,
|
treeRef,
|
||||||
search,
|
search,
|
||||||
sys_user_sex,
|
sys_user_sex,
|
||||||
|
selectedUsers,
|
||||||
|
deptUser,
|
||||||
selectedUserInfo,
|
selectedUserInfo,
|
||||||
openDialog,
|
openDialog,
|
||||||
getUserList,
|
getUserList,
|
||||||
@ -237,8 +284,15 @@ export default defineComponent({
|
|||||||
goBack,
|
goBack,
|
||||||
removeAll,
|
removeAll,
|
||||||
remove,
|
remove,
|
||||||
|
handleClose,
|
||||||
|
handleSelectUser,
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.u-m-r-10{
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -30,7 +30,7 @@ export default defineComponent({
|
|||||||
initialFrameHeight: 400,
|
initialFrameHeight: 400,
|
||||||
maximumWords: 5000,
|
maximumWords: 5000,
|
||||||
topOffset: 80,
|
topOffset: 80,
|
||||||
zIndex:2020
|
zIndex:2050
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -41,6 +41,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
emits:['update:modelValue'],
|
||||||
setup(props,{emit}){
|
setup(props,{emit}){
|
||||||
const config = Object.assign({
|
const config = Object.assign({
|
||||||
elementPathEnabled: false,
|
elementPathEnabled: false,
|
||||||
@ -59,7 +60,7 @@ export default defineComponent({
|
|||||||
return props.modelValue
|
return props.modelValue
|
||||||
},
|
},
|
||||||
set:(newVal)=>{
|
set:(newVal)=>{
|
||||||
emit('setEditContent',newVal)
|
emit('update:modelValue',newVal)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return {
|
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"
|
:on-success="handleSuccess"
|
||||||
:data="dataParam"
|
:data="dataParam"
|
||||||
:on-preview="handlePreview"
|
:on-preview="handlePreview"
|
||||||
|
ref="upFileRef"
|
||||||
>
|
>
|
||||||
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
|
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
|
||||||
<div class="el-upload__text">
|
<div class="el-upload__text">
|
||||||
@ -54,9 +55,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits:['update:modelValue'],
|
||||||
setup(props,{ emit }) {
|
setup(props,{ emit }) {
|
||||||
let uploadedFile:Array<any> = [] ;
|
let uploadedFile:Array<any> = [] ;
|
||||||
const {proxy} = <any>getCurrentInstance();
|
const {proxy} = <any>getCurrentInstance();
|
||||||
|
const upFileRef = ref()
|
||||||
const dataParam = reactive({
|
const dataParam = reactive({
|
||||||
token:getToken(),
|
token:getToken(),
|
||||||
})
|
})
|
||||||
@ -73,7 +76,7 @@ export default defineComponent({
|
|||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
set: val => {
|
set: val => {
|
||||||
emit('upFileData', val)
|
emit('update:modelValue', val)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = () => {
|
const beforeUpload: UploadProps['beforeUpload'] = () => {
|
||||||
@ -130,6 +133,9 @@ export default defineComponent({
|
|||||||
const handlePreview = (file:UploadUserFile)=>{
|
const handlePreview = (file:UploadUserFile)=>{
|
||||||
window.open(file.url)
|
window.open(file.url)
|
||||||
}
|
}
|
||||||
|
const stopUpFile = ()=>{
|
||||||
|
upFileRef.value.abort()
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
dataFileList,
|
dataFileList,
|
||||||
handleSuccess,
|
handleSuccess,
|
||||||
@ -139,6 +145,8 @@ export default defineComponent({
|
|||||||
handleChange,
|
handleChange,
|
||||||
handleExceed,
|
handleExceed,
|
||||||
handlePreview,
|
handlePreview,
|
||||||
|
upFileRef,
|
||||||
|
stopUpFile,
|
||||||
dataParam
|
dataParam
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
:on-exceed = "handleExceed"
|
:on-exceed = "handleExceed"
|
||||||
:before-upload="beforeAvatarUpload"
|
:before-upload="beforeAvatarUpload"
|
||||||
:data="dataParam"
|
:data="dataParam"
|
||||||
|
ref="upImageRef"
|
||||||
>
|
>
|
||||||
<el-icon><ele-Plus /></el-icon>
|
<el-icon><ele-Plus /></el-icon>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
@ -66,7 +67,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits:['update:modelValue'],
|
||||||
setup(props,{ emit }) {
|
setup(props,{ emit }) {
|
||||||
|
const upImageRef = ref()
|
||||||
const baseURL:string|undefined|boolean = import.meta.env.VITE_API_URL
|
const baseURL:string|undefined|boolean = import.meta.env.VITE_API_URL
|
||||||
const {proxy} = <any>getCurrentInstance();
|
const {proxy} = <any>getCurrentInstance();
|
||||||
const dialogImageUrl = ref('')
|
const dialogImageUrl = ref('')
|
||||||
@ -93,7 +96,7 @@ export default defineComponent({
|
|||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
set: val => {
|
set: val => {
|
||||||
emit('uploadData', val)
|
emit('update:modelValue', val)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -144,7 +147,11 @@ export default defineComponent({
|
|||||||
const setDataFileList = () => {
|
const setDataFileList = () => {
|
||||||
dataFileList.value = uploadedFile
|
dataFileList.value = uploadedFile
|
||||||
};
|
};
|
||||||
|
const stopUpImage = ()=>{
|
||||||
|
upImageRef.value.abort()
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
|
upImageRef,
|
||||||
dataFileList,
|
dataFileList,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
baseURL,
|
baseURL,
|
||||||
@ -155,6 +162,7 @@ export default defineComponent({
|
|||||||
handleRemove,
|
handleRemove,
|
||||||
handlePictureCardPreview,
|
handlePictureCardPreview,
|
||||||
handleAvatarSuccess,
|
handleAvatarSuccess,
|
||||||
|
stopUpImage,
|
||||||
dataParam
|
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 = {
|
let noticeParam = {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 5,
|
pageSize: 10,
|
||||||
type: barName,
|
type: barName,
|
||||||
|
isTrim:true
|
||||||
}
|
}
|
||||||
listShowNotice(noticeParam).then((res: any) => {
|
listShowNotice(noticeParam).then((res: any) => {
|
||||||
state.noticeList = res.data.list || []
|
state.noticeList = res.data.list || []
|
||||||
@ -225,12 +226,14 @@ const handleRead = (item: SysNoticeInfoData) => {
|
|||||||
getData(item.type)
|
getData(item.type)
|
||||||
ElMessage.success("已读");
|
ElMessage.success("已读");
|
||||||
})
|
})
|
||||||
|
getUnReadCount()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.layout-navbars-breadcrumb-user-news {
|
.layout-navbars-breadcrumb-user-news {
|
||||||
|
height: calc(100vh - 150px);
|
||||||
|
overflow-y: auto;
|
||||||
.content-box {
|
.content-box {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
|
@ -60,38 +60,6 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
|
|||||||
roles: ['admin'],
|
roles: ['admin'],
|
||||||
icon: 'iconfont icon-diannao',
|
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>
|
<template>
|
||||||
<div>
|
<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>
|
<h3>同步获取编辑器内容如下:</h3>
|
||||||
<div v-html="content"></div>
|
<div v-html="content"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in sysYesNoOptions"
|
v-for="dict in sysYesNoOptions"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.value"
|
:value="dict.value"
|
||||||
>{{dict.label}}</el-radio>
|
>{{dict.label}}</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -27,15 +27,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
|
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
|
||||||
<el-form-item label="负责人">
|
<el-form-item label="负责人">
|
||||||
<div v-if="deptUser.length > 0">
|
<select-user v-model="ruleForm.leader"></select-user>
|
||||||
<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>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
|
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
|
||||||
@ -68,13 +60,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
<select-user ref="selectUserRef" @selectUser="confirmUser" :selectedUsers="deptUser"></select-user>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 {addDept,editDept, getDeptList} from "/@/api/system/dept";
|
||||||
import {getUserByIds} from '/@/api/system/user';
|
|
||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
import selectUser from "/@/components/selectUser/index.vue"
|
import selectUser from "/@/components/selectUser/index.vue"
|
||||||
|
|
||||||
@ -105,8 +95,6 @@ defineOptions({ name: "systemEditDept"})
|
|||||||
const emit = defineEmits(['deptList'])
|
const emit = defineEmits(['deptList'])
|
||||||
const {proxy} = getCurrentInstance() as any;
|
const {proxy} = getCurrentInstance() as any;
|
||||||
const formRef = ref<HTMLElement | null>(null);
|
const formRef = ref<HTMLElement | null>(null);
|
||||||
const selectUserRef = ref();
|
|
||||||
const deptUser = ref([]);
|
|
||||||
const state = reactive<DeptSate>({
|
const state = reactive<DeptSate>({
|
||||||
isShowDialog: false,
|
isShowDialog: false,
|
||||||
ruleForm: {
|
ruleForm: {
|
||||||
@ -135,24 +123,8 @@ const openDialog = (row?: RuleFormState|number) => {
|
|||||||
});
|
});
|
||||||
if(row && typeof row === "object"){
|
if(row && typeof row === "object"){
|
||||||
state.ruleForm = row;
|
state.ruleForm = row;
|
||||||
let leaders = row.leader??[]
|
}else if(row){
|
||||||
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'){
|
|
||||||
state.ruleForm.parentId = row
|
state.ruleForm.parentId = row
|
||||||
deptUser.value = [];
|
|
||||||
}else{
|
|
||||||
deptUser.value = [];
|
|
||||||
}
|
}
|
||||||
state.isShowDialog = true;
|
state.isShowDialog = true;
|
||||||
};
|
};
|
||||||
@ -201,31 +173,6 @@ const resetForm = ()=>{
|
|||||||
status: 1,
|
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>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.u-m-r-10{
|
.u-m-r-10{
|
||||||
|
@ -73,12 +73,12 @@
|
|||||||
<el-dialog :title="selectRow.name+'-用户列表'" v-model="isShowDialog" width="70vw">
|
<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"/>
|
<UserList v-if="isShowDialog" ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="userListParam" @getUserList="userList"/>
|
||||||
</el-dialog>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { ElMessageBox, ElMessage,ElLoading } from 'element-plus';
|
||||||
import EditRole from '/@/views/system/role/component/editRole.vue';
|
import EditRole from '/@/views/system/role/component/editRole.vue';
|
||||||
import DataScope from '/@/views/system/role/component/dataScope.vue';
|
import DataScope from '/@/views/system/role/component/dataScope.vue';
|
||||||
@ -257,26 +257,10 @@ const handleCommand = (command: string )=>{
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const confirmUser = (data:any[]) => {
|
watch(roleUsers,(newVal) => {
|
||||||
if(data.length>0){
|
setRoleUser(newVal)
|
||||||
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
|
|
||||||
})
|
})
|
||||||
|
const setRoleUser = (ids:any)=>{
|
||||||
//用户授权提交
|
//用户授权提交
|
||||||
setRoleUsers({roleId:setRole.value,userIds:ids}).then((res:any)=>{
|
setRoleUsers({roleId:setRole.value,userIds:ids}).then((res:any)=>{
|
||||||
roleList()
|
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-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="内容">
|
<el-form-item label="内容">
|
||||||
<gf-ueditor editorId="ueSysNoticeContent" v-model="formData.content"
|
<gf-ueditor editorId="ueSysNoticeContent" v-model="formData.content"></gf-ueditor>
|
||||||
@setEditContent="setContentEditContent"></gf-ueditor>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<!-- <el-radio-group v-model="formData.status">
|
<!-- <el-radio-group v-model="formData.status">
|
||||||
|
@ -130,6 +130,12 @@
|
|||||||
<el-option label="多图上传" value="images" />
|
<el-option label="多图上传" value="images" />
|
||||||
<el-option label="单文件上传" value="file" />
|
<el-option label="单文件上传" value="file" />
|
||||||
<el-option label="多文件上传" value="files" />
|
<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-option label="键值对" value="keyValue" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user