vue中,el-table树形数据与懒加载【实例】(二)——封装拖拽上传el-upload & 本地下载模板-public下的file文件夹 & JSON.parse转对象 & 表单禁止输入特殊字符

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|&nbsp;|&quot;|&amp;|&#x27;|&#x2F;|&lt;|&gt;|&AElig;|&Aacute;|&Acirc;|&Agrave;|&Aring;|&Atilde;|&Auml;|&Ccedil;|&ETH;|&Eacute;|&Ecirc;|&Egrave;|&Euml;|&Iacute;|&Icirc;|&Igrave;|&Iuml;|&Ntilde;|&Oacute;|&Ocirc;|&Ograve;|&Oslash;|&Otilde;|&Ouml;|&THORN;|&Uacute;|&Ucirc;|&Ugrave;|&Uuml;|&Yacute;|&aacute;|&acirc;|&aelig;|&agrave;|&aring;|&atilde;|&auml;|&ccedil;|&eacute;|&ecirc;|&egrave;|&eth;|&euml;|&iacute;|&icirc;|&igrave;|&iuml;|&ntilde;|&oacute;|&ocirc;|&ograve;|&oslash;|&otilde;|&ouml;|&szlig;|&thorn;|&uacute;|&ucirc;|&ugrave;|&uuml;|&yacute;|&yuml;|&cent;|\\""|&|&#39/,
  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
}
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值