vue中,el-table树形数据与懒加载【实例】(二)——封装拖拽上传el-upload & 本地下载模板-public下的file文件夹 & JSON.parse转对象 & 表单禁止输入特殊字符
4、封装拖拽下载模板和上传
src\components\customPlanImport.vue
<template>
<div class="item-content">
<el-dialog
v-model="isShow"
:close-on-click-modal="true"
:destroy-on-close="true"
:show-close="true"
title="导入规划计划"
width="35%"
@close="handleCancel"
>
<el-upload
ref="uploadRef"
action="#"
class="upload-demo"
drag
:file-list="fileList"
:http-request="handlePeopleUpload"
:limit="1"
:multiple="false"
:show-file-list="true"
>
<!-- :on-change="handleChange"
:on-remove="handleRemove" -->
<i class="el-icon-upload"></i>
<div class="el-upload__text">
<em>选择文件</em>
或将文件拖拽至此区域
</div>
<div class="el-upload__tip">支持格式:xls、xlsx</div>
</el-upload>
<el-button type="text" @click="handleDownLoadTemplate(source)">
下载导入模板
</el-button>
<div class="upload-footer">
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button plain type="primary" @click="handleCancel">取 消</el-button>
</div>
</el-dialog>
<!-- 导入失败 -->
<el-dialog
v-model="isShowFail"
class="fail-dialog"
:close-on-click-modal="true"
:destroy-on-close="true"
:show-close="true"
title="导入失败"
width="35%"
@close="handleClose"
>
<template v-if="errorList[0].row">
<h4 class="fail-title">
导入失败,请求改后重新上传,错误数据{{ errNumber }}条,失败原因如下:
</h4>
<el-table border :data="errorList">
<el-table-column label="行数" prop="index" />
<el-table-column label="错误原因" prop="msg" />
</el-table>
</template>
<template v-else>
<h4 class="fail-title">
导入失败,请求改后重新上传,失败原因:
<span style="font-weight: 200; color: red">
{{ errorList[0].msg }}
</span>
</h4>
</template>
</el-dialog>
</div>
</template>
<script>
// import { downloadFileCom } from '@src/hooks/useFolesUtils.js' // 走接口下载
// import { getFeasDownload } from '@/api/research/index' // 走接口下载
import { downFile } from '@src/utils/util' // 本地下载
import { ElMessage } from 'element-plus'
import { uploadPlanFileApi } from '@/api/organization/project'
export default defineComponent({
name: 'CustomTaskImport',
components: {},
props: {
source: {
require: true,
type: String,
default: '',
},
showDialog: {
require: true,
type: Boolean,
default: false,
},
},
emits: ['closeDialog', 'submitDialog', 'downLoadTemplate'],
setup(props, { emit }) {
const { showDialog, source } = toRefs(props)
const state = reactive({
isShow: showDialog,
uploadRef: null,
isShowFail: false,
source: source,
fileList: [],
formData: {
name: '',
},
fileData: [],
errNumber: 0,
errorList: [],
})
//下载导入模板
const handleDownLoadTemplate = (type) => {
if (type === 'approval') {
// 接口下载模板
// getFeasDownload().then((res) => {
// downloadFileCom(res, '规划计划导入模板')
// })
downFile('Plan-Import.xlsx')
}
}
//提交
const handlePeopleUpload = (options) => {
const formData = new FormData()
formData.append('file', options.file)
switch (state.source) {
case 'research':
case 'task':
case 'approval':
uploadPlanFileApi(formData).then((res) => {
if (res.code === '00000') {
// state.fileData = res.data
ElMessage({
type: 'success',
message: '上传成功',
})
} else {
// state.errNumber = 0
ElMessage.error(res.message)
}
})
}
}
const handleSubmit = () => {
state.uploadRef.clearFiles()
// if (state.errNumber > 0) {
// state.isShowFail = true
// } else {
// state.isShowFail = false
// if (state.fileData.length && state.errorList.length) {
// ElMessage.success('导入成功')
// } else {
// ElMessage.error({
// type: 'error',
// message: '请先上传文件',
// })
// }
// }
handleClose()
// emit('submitDialog', state.fileData)
emit('submitDialog', state.handleClose)
}
// 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
const handleChange = (file) => {
const fileName = file.raw.name
//限制文件类型
const fileExt = fileName.replace(/.+\./, '')
if (['xlsx', 'xls'].indexOf(fileExt.toLowerCase()) === -1) {
ElMessage({
showClose: true,
message: '请上传后缀名为xlsx、xls',
type: 'error',
})
return
}
}
//移除
const handleRemove = () => {}
// 取消
const handleCancel = () => {
emit('closeDialog')
}
// 关闭
const handleClose = () => {
state.handleClose = false
}
return {
...toRefs(state),
handleClose,
handleSubmit,
handleRemove,
handleChange,
handleCancel,
handlePeopleUpload,
handleDownLoadTemplate,
}
},
})
</script>
<style lang="scss" scoped>
.fail-dialog {
.fail-title {
margin: 0px 0 10px;
}
:deep() {
.ym-dialog__body {
padding: 10px 20px 20px;
}
}
}
:deep() {
.ym-dialog {
.ym-dialog__title {
float: left;
}
}
}
.upload-footer {
display: flex;
justify-content: center;
padding-bottom: 20px;
}
</style>
src\app\plan\api\organization\project.js
//上传文件-规划计划模板
export const uploadPlanFileApi = (data) => {
return request({
url: `${plan}/prog/importProg`,
method: 'post',
data,
headers: {
'Content-Type': 'multipart/from-data',
},
})
}
5、本地下载-public下的file文件夹
src\utils\util.js
//文件流导出数据处理
export const downLoadxls = (res, fileName) => {
let name = fileName
if (res.headers['content-disposition']) {
const contentDisposition = res.headers['content-disposition'].split('=')
name = (contentDisposition && decodeURI(contentDisposition[1])) || ''
}
const file = new File([res.data], name, res.data)
const href = URL.createObjectURL(file)
const aTag = document.createElement('a')
aTag.download = file.name
aTag.target = '_blank'
aTag.href = href
aTag.click()
URL.revokeObjectURL(href)
}
//本地文件下载 下载的模板文件放在public下的file文件夹中
const configInfo = sessionStorage.getItem('configInfo') || '{}'
const url = JSON.parse(configInfo)?.baseApiUrl
const apiUrl = url ? `${url.split('kjapi')[0] + 'cmn/science/'}` : '/'
export const downFile = (fileName) => {
const anchor = document.createElement('a')
anchor.href = `${apiUrl}static/file/${fileName}`
anchor.setAttribute('download', fileName)
anchor.innerHTML = 'downloading...'
anchor.style.display = 'none'
document.body.appendChild(anchor)
setTimeout(() => {
anchor.click()
document.body.removeChild(anchor)
setTimeout(() => {
self.URL.revokeObjectURL(anchor.href)
}, 250)
}, 66)
}
6、获取会话数据-sessionStorage.getItem 和 JSON.parse
src\utils\getOrgLevel.js
export const getOrgLevel = () => {
const loginUserInfo = JSON.parse(sessionStorage.getItem('loginUserInfo'))
const orgNo = loginUserInfo?.orgNo
if (!orgNo) {
return ''
}else if (orgNo === '0000') {
return '01' // 总部
}else if (orgNo.length === 4) {
return '02' // 省级
}else if (orgNo.length === 8) {
return '03' // 市级
}else if (orgNo.length === 12) {
return '04' // 县级
}
}
7、表单禁止输入特殊字符
src\utils\validate.ts
import { ElMessage } from 'element-plus'
import { validatorSpecialCharacter,filterSpecialCharacterAction } from './filter'
// 禁止输入框特殊字符校验
export function replaceCommonText(e: any) {
if (e.length) {
const val = e.trim()
if (!val) {
ElMessage({
message: '请输入有效内容',
type: 'warning',
})
return
}
const err: any = validatorSpecialCharacter(e)
if (err) {
ElMessage({
message: err.message,
type: 'warning',
})
const y = filterSpecialCharacterAction(val)
return y
} else {
return e
}
}
}
src\utils\filter.js
/* eslint-disable */
// 特殊字符 敏感词检验过滤
import { init } from './deailFilter'
let regObj = {
urlReg:
/\+|\?|#|\*|%2B|%20|%2F|%3F|%25|%23|%26|%3D|%27|%26|%22|%28|%29|%2a|%2b|%2d|%30/,
htmlReg:
/object|comment| |"|&|'|/|<|>|Æ|Á|Â|À|Å|Ã|Ä|Ç|Ð|É|Ê|È|Ë|Í|Î|Ì|Ï|Ñ|Ó|Ô|Ò|Ø|Õ|Ö|Þ|Ú|Û|Ù|Ü|Ý|á|â|æ|à|å|ã|ä|ç|é|ê|è|ð|ë|í|î|ì|ï|ñ|ó|ô|ò|ø|õ|ö|ß|þ|ú|û|ù|ü|ý|ÿ|¢|\\""|&|'/,
jsReg:
/,|\||{|}|%|<|>|&|""|'|\/|\\|\\r|\\n|\\\\|\\t|\\f|\\b|!|@|#|\$|\^|\*|\(|\)|~|:|;|\\`|-|=|\[|\]|javascript|script|function|jscript|vbscript|onfocus|onblur|location|document|window|onclick|href|<!--|--|->|\/\\\\\\\*|\\\\\\\*\/|onfocus|\/\/|onerror|\/\*|data:|\\u003e|\\u003c|eval|url|expr|URLUnencoded|referrer|write\(.\)|writeln|body\.innerHtml|execScript|navigate|srcdoc|%0a|<\/|\.|_|·|!|¥|…|【|】|、|;|‘|’|:|“|”|,|。|、|《|》|?/,
sqlReg: /\\\*|--|%28|\)|\/\/|\/\*|\/\\\*\\\\*/,
replaceStrs: [
'//',
'/*',
'</',
'/\\*',
'net +user',
'net +localgroup +administrators',
'information_schema.columns',
'netlocalgroup administrators',
'body.innerHtml',
'()',
'(',
')',
'null',
'NULL',
'Null',
],
}
const sqlKeyword = [
'and',
'or',
'exec',
'execute',
'insert',
'select',
'delete',
'update',
'alter',
'create',
'drop',
'count',
'chr',
'char',
'asc',
'desc',
'mid',
'substring',
'master',
'truncate',
'declare',
'xp_cmdshell',
'restore',
'backup',
'net user',
'like',
'table',
'from',
'grant',
'use',
'column_name',
'group_concat',
'table_schema',
'union',
'where',
'order',
'by',
'join',
'modify',
'into',
'substr',
'ascii',
'having',
]
const htmlTag = [
'a',
'iframe',
'body',
'form',
'base',
'img',
'style',
'div',
'meta',
'link',
'input',
'br',
]
const jsFunction = [
'open',
'confirm',
'prompt',
'setInterval',
'setTimeout',
'alert',
]
let jsFunctionStr = (() => {
let regArr = []
jsFunction.forEach((item) => {
regArr.push(`${item}\\(.*\\)`)
})
return regArr.join('|')
})()
let htmlTagRegStr = (() => {
let regArr = []
htmlTag.forEach((item) => {
regArr.push(`<${item}`)
regArr.push(`${item}>`)
})
return regArr.join('|')
})()
regObj.jsFunctionReg = new RegExp(jsFunctionStr)
regObj.htmlTagReg = new RegExp(htmlTagRegStr, 'i')
regObj.sqlKeywordReg = new RegExp(sqlKeyword.join('\\s|') + '\\s', 'i')
const reg = copyReg(regObj)
init(reg)
// 全局敏感词校验方法
export function validatorSpecialCharacter(value) {
if (!isNaN(value)) return false
if (typeof value !== 'string') return false // 只校验字符串
value = String(value).trim()
if (value === '') return false // 空不检验
if (reg.urlReg.test(value)) {
return {
message: '不能包含特殊字符',
reg: reg.urlReg,
}
}
if (reg.htmlReg.test(value)) {
return {
message: '不能包含关键字',
reg: reg.htmlReg,
}
}
if (reg.jsReg.test(value)) {
return {
message: '不能包含特殊字符',
reg: reg.jsReg,
}
}
if (reg.sqlReg.test(value)) {
return {
message: '不能包含特殊字符',
reg: reg.sqlReg,
}
}
if (reg.sqlKeywordReg.test(value)) {
return {
message: '不能包含关键字',
msg: '不能包含SQL语句',
reg: reg.sqlKeywordReg,
}
}
if (reg.htmlTagReg.test(value)) {
return {
msg: '不能包含HTML标签',
message: '不能包含关键字',
reg: reg.htmlTagReg,
}
}
if (reg.jsFunctionReg.test(value)) {
return {
msg: '不能包含JS方法',
message: '不能包含关键字',
reg: reg.jsFunctionReg,
}
}
let str = reg.replaceStrs.find((val) => {
return value.indexOf(val) !== -1
})
if (str) {
return {
message: '不能包含关键字',
str: str,
reg: str,
}
}
return false
}
function isArray(data) {
return Object.prototype.toString.call(data) === '[object Array]'
}
function isObject(data) {
return Object.prototype.toString.call(data) === '[object Object]'
}
// 过滤特殊字符的方法
export function filterSpecialCharacterAction(data) {
if (isArray(data) || isObject(data)) {
for (const key in data) {
data[key] = filterSpecialCharacterAction(data[key])
}
} else {
let err = validatorSpecialCharacter(data)
if (err) {
if (err.reg) {
return filterSpecialCharacterAction(data.replace(err.reg, ''))
}
if (err.str) {
return filterSpecialCharacterAction(data.replace(err.str, ''))
}
}
}
return data
}
function copyReg(regObj) {
let result = {}
for (const key in regObj) {
result[key] = regObj[key]
}
return result
}
// 响应数据过滤数据
export const filterSpecialCharacter = (data) => {
let result = data
return result
}